CHAPTER 2 객체지향 프로그래밍 패러다임
객체지향 프로그래밍은 현 시점에 가장 대중적인 프로그래밍 패턴이다
2.1 객체지향이란 무엇인가?
2.1.1 객체지향 프로그래밍과 객체지향 프로그래밍 언어
객체지향 프로그래밍이란?
- 프로그래밍 패러다임 또는 프로그래밍 스타일을 의미한다
- 코드를 구성하는 기본 단위로 클래스 와 객체를 사용한다
- 코드 설계 와 구현의 초석으로 캡슐화,추상화,상속,다형성을 사용한다
2.1.2 엄격하게 정의되지 않은 객체지향 프로그래밍 언어
프로그래밍 언어가 클래스 또는 객체의 문법적 개념을 지원하고 이를 코드 구성의 기본 단위로 사용하는 한 단순히 객체지향 언어로 간주될 수 있다고 생각한다
2.1.3 객체지향 분석과 객체지향 설계
분석과 설계 앞에 객체지향이라는 단어가 붙는 이유는 객체나 클래스에 대한 요구 사항을 분석하고 설계하기 때문이다
2.2 캡슐화, 추상화, 상속, 다형성이 등장한 이유
2.2.1 캡슐화
정보 은닉 또는 데이터 액세스 보호라고도 하는데 접근 가능한 인터페이스를 제한하여 클래스가 제공하는 메서드를 통해서만 내부 정보나 데이터에 대한 외부 접근을 허가 하는것을 뜻한다
2.2.2 추상화
매서드 내부 구현을 숨기는 것을 의미한다. 클래스를 사용할 때 기능의 구현 방식에 대해 고민하지 않고 메서드가 제공하는 기능에만 집중할수 있다
추상화는 특이성이 그리 높지 않으며 이로 인해 객체지향 프로그래밍의 특성으로 간주 되지 않는 경우가 있다
2.2.3 상속
상속은 고양이는 포유류의 일종이다 처럼 클래스 사이의 is-a 관계를 나타내는데 사용된다
단점
- 상속계층 구조가 너무 깊고 복잡하면 코드의 가독성과 유지 관리 성이 떨어진다
- 특정 클래스의 기능을 이해하기 위해서는 해당 클래스 코드만 보는게 아니라 상위 클래스도 봐야 된다
- 상위 클래스의 코드 수정이 하위 클래스에 직접적인 영향을 미친다
2.2.4 다형성
하위 클래스를 상위 클래스 대신 사용하고 하위 클래스의 메서드를 호출할 수 있는 특성을 의미한다
다형성을 충족시키기 위한 문법
- 상위 클래스 객체가 하위 클래스 객체를 참조 할수 있어야 된다
- 상속을 지원해야 한다
- 상위 클래스의 메서드를 재정의하는 하위 클래스를 지원해야 된다
다형성 특성을 구현을 위해 인터페이스 문법을 사용하거나 덕타이필 문법을 사용하는 두가지 구현 방법이 일반적이다
2.3 객체지향 분석, 객체지향 설계, 객체지향 프로그래밍을 수행하는 방법
2.3.3 객체지향 설계 방법
- 책임과 기능을 나누고 어떤 클래스가 있는지 확인한다
- 클래스를 정의하고 클래스의 속성과 메서드를 정의한다
- 클래스 간의 상호 작용을 정의한다
- 클래스를 연결하고 실행 엔트리 포인트를 제공한다
2.4 객체지향 프로그래밍, 절차적 프로그래밍, 함수형 프로그래밍의 차이
2.4.1 절차적 프로그래밍
절차적 프로그래밍은 프로그래밍 패러다임이기도하다 코드를 구성하기 위한 기본 단위로 메서드, 기능, 연산 처럼 절차가 필요 하며 멤버 변수,
속성과 같은 데이터가 메서드와 분리되어 있는 것이 특징이다
2.4.2 객체지향 프로그래밍과 절차적 프로그래밍의 비교
- 객체지향 프로그래밍은 대규모의 복잡한 프로그램 개발에 더 적합하다
- 객체지향 프로그래밍 스타일은 코드는 재사용, 확장, 유지 관리가 쉽다
- 객체지향 프로그래밍 언어는 더 사용자 친화적이고 고급 언어이며 지능적이다
2.4.3 함수형 프로그래밍
함수형 프로그래밍의 함수는 프로그래밍 언어에서 말하는 함수가 아니라 수학의 함수 또는 표현식을 말한다
스테이트리스 함수(stateless function)라는 점에서 절차적 프로그래밍과 다르다
- Stream 클래스
- 람다 표현식
- 함수형 인터페이스
2.4.4 객체지향 프로그래밍과 함수형 프로그래밍의 비교
객체 지향 프로그래밍의 프로그래밍 단위는 클래스 또는 객체
절차적 프로그래밍의 프로그래밍 단위는 함수
함수형 프로그래밍의 프로그래밍 단위는 스테이트리스 함수
2.5 객체지향 프로그래밍처럼 보이지만 실제로는 절차적 프로그래밍
2.5.1 getter, setter 메서드 남용
캡슐화 위배
2.5.2 전역 변수와 전역 메서드의 남용
2.5.3 데이터와 메서드 분리로 클래스 정의하기
2.6 빈약한 도메인 모델에 기반한 전통적인 개발 방식은 OOP를 위반하는가?
2.6.1 빈약한 도메인 모델에 기반한 전통적인 개발 방식
빈약한 도메인 모델
- 데이터만 포함하고 비지니스 논리는 포함하지 않은 모델
2.6.2 풍성한 도메인 모델에 기반한 DDD 개발 방식
풍성한 도메인 모델 - 비지니스 논리와 데이터를 포함한 모델
2.6.3 두 가지 개발 방식의 비교
- 빈약한 도메인 모델
- 기본 개발 방식에 의해 구현된 코드를 잘 이해하고 있어야 된다
- 풍성한 도메인 모델
Service Layer 책임
- 저장소 계층과의 통신을 담당
- 여러 도메인 모델의 비즈니스 논리를 결합
- 타 시스템과의 상호작용 담당
2.6.4 빈약한 도메인 모델에 기반한 전통적인 개발 방식이 널리 사용되는 이유
- 대부분 시나리로가 비교적 단순하고 복잡하지 않은 경우가 많다
- 풍성한 도메인 모델은 설계가 더 어렵다
- 사고방식의 전환은 어렵고 그만한 대가가 필요하다
2.7 추상 클래스와 인터페이스
2.7.1 추상 클래스와 인터페이스의 정의와 차이점
- 추상 클래스
- 인스턴스화할 수 없으며 상속만 가능하다
- 추상 클래스는 속성과 메서드를 포함할 수 있다
- 하위 클래스는 추상 클래스를 상속할 때 추상 클래스의 모든 추상 메서드를 실제로 구현해야 된다
- 인터페이스
- 코드 재사용에 초점을 맞춤
인터페이스
- 디커플링에 초점
2.8 인터페이스 기반 프로그래밍: 모든 클래스에 대해 인터페이스를 정의해야 할까?
2.8.1 인터페이스를 이해하는 다양한 방법
인터페이스는 프로토콜 또는 규약의 집합으로 사용자에게 제공되는 기능 목록이다
2.8.2 설계 철학을 실제로 적용해보자
2.8.3 인터페이스의 남용을 방지하려면 어떻게 해야 할까?
시나리오에서 특정 기능에 대한 구현 방법이 하나 뿐이고 이후에도 다른 구현 방법으로 대체할 일이 없다면 인터페이스를 정의할 필요가 없다
2.9 상속보다 합성
2.9.2 합성이 상속에 비해 나은 장점
합성, 인터페이스, 위임을 통해 상속문제를 해결할수 있다
2.9.3 합성을 사용할지 상속을 사용할지 결정하기
클래스 간의 상속 구조가 안정적이어서 쉽게 변경되지 않고 상속 단계가 2단계 이하로 비교적 얕아 상속관계가 복잡하지 않다면 상속을 사용할수도 있다