오브젝트의 2장은 [객체지향 프로그래밍]을 소제목으로 하고있다.
2장에서는 '영화 예매 시스템'을 예시로 두고 객체지향 프로그래밍에 대해 설명하고 있다. 2장부터는 깃헙에 사전 정리된 내용이 없기 때문에 책일 읽으면서 내용을 해당 게시글에 정리하고자 한다.
01_객체, 설계 파트에서는 티켓 판매 애플리케이션을 구현하는 것을 예시로 두고 설명하였고, 02_객체지향 프로그래밍에서는 영화 예매 시스템을 예시로 두고 객체지향에 대해 설명하고 있다.
01. 요구사항 살펴보기
- '요구사항 살펴보기'의 경우 애플리케이션을 설계하기 전에 사전에 정의되어야 하는 용어의 구분 및 할인 정책 등을 기술하고 있다. 상세내역은 블로그에 정리하지 않고 내용에 대한 확인이 필요할 때 책을 다시 펼쳐보기 위해 참고 페이지만 기록해둔다.
- p. 37 ~ p.40
02. 객체지향 프로그래밍을 향해진정한 객체지향 패러다임으로의 전환 : 클래스가 아닌 객체에 초점을 맞출 때에만 얻을 수 있다.
- 객체지향 프로그래밍의 집중점
- 첫째, 어떤 클래스가 필요한지 고민하기 전에 어떤 객체들이 필요한지 고민하라.
- 클래스의 윤곽을 잡기 위해서는 어떤 객체들이 어떤 상태와 행동을 가지는지를 먼저 결정해야 한다.
- 둘째, 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 보라.
- 객체를 고립된 존재로 바라보지 말고 협력에 참여하는 협력자로 보아야 한다.
- 객체들의 윤곽이 잡히면 공통된 특성과 상태를 가진 객체들을 타입으로 분류하고 이 타입을 기반으로 클래스를 구현한다.
- 첫째, 어떤 클래스가 필요한지 고민하기 전에 어떤 객체들이 필요한지 고민하라.
- 도메인 : 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야
- 객체지향 프로그래밍은 요구사항과 프로그램을 객체라는 동일한 관점에서 바라볼 수 있다. 따라서 도메인을 구성하는 개념들이 프로그램의 객체와 클래스로 매끄럽게 연결될 수 있다.
- 일반적으로 클래스의 이름은 대응되는 도메인 개념의 이름과 동일하거나 적어도 유사하게 네이밍한다.
- 클래스 사이의 관계도 최대한 도메인 개념 사이에 맺어진 관계와 유사하게 만든다.
- 목적 : 프로그램의 구조를 이해하고 예상하기 쉽게 하기 위함
- 클래스 구현하기
- 예제에 사용되는 클래스 명칭(참고)
- 영화 - Movie
- 상영 - Screening
- 할인정책 - DiscountPolicy
- 금액 할인 정책 - AmountDiscountPolicy
- 비율 할인 정책 - PercentDiscountPolicy
- 할인조건 - DiscountCondition
- 순번 조건 - SequenceCondition
- 기간 조건 - PeriodCondition
- 예매 - Reservation
- 클래스의 경계
- 클래스는 내부와 외부로 구분되며 훌륭한 클래스를 설계하기 위한 핵심은 어떤 부분을 외부에 공개하고 어떤 부분을 감출지를 결정하는 것이다.
- 클래스의 내•외부 구분 이유
- 첫째, 경계의 명확성이 객체의 자율성을 보장
- 객체지향의 핵심은 스스로 상태를 관리하고, 판단하고, 행동하는 자율적인 객체드의 공동체를 구성하는 것
- 캡슐화와 접근 제어는 객체를 외부에서 접근 가능한 '퍼블릭 인터페이스(public-interface)'와 내부에서만 접근 가능한 '구현(implementation)'으로 나눈다.
- 둘째, 프로그래머에게 구현의 자유를 제공
- 프로그래머의 역할
- 첫째, 클래스 작성자 : 새로운 데이터 타입을 프로그램에 추가
- 구현 은닉(implementation hiding) : 클라이언트 프로그래머가 숨겨놓은 부분을 마음대로 접근할 수 없도록 방지하고, 내부 구현을 클래스 작성자가 마음대로 변경할수 있도록 구현하는 행위
- 둘째, 클라이언트 프로그래머 : 클래스 작성자가 추가한 데이터 타입을 사용
- 목표 : 필요 클래스들을 엮어서 애플리케이션을 빠르고 안정적으로 구축
- 첫째, 클래스 작성자 : 새로운 데이터 타입을 프로그램에 추가
- 객체의 외부와 내부를 구분하면 클라이언트 프로그래머가 알아야 할 지식의 양이 줄어들고 클래스 작성자가 자유롭게 구현을 변경할 수 있는 폭이 넓어진다.
- 프로그래머의 역할
- 첫째, 경계의 명확성이 객체의 자율성을 보장
- 예제에 사용되는 클래스 명칭(참고)
- github_01. 영화 예매 애플리케이션 구현 첫 번째 - 협력
- https://github.com/JIHYEON-PF/book_review_object/pull/5
02_객체지향 프로그래밍 : 영화 예매 애플리케이션 첫 번째 커밋 by JIHYEON-PF · Pull Request #5 · JIHYEON
오브젝트 : 코드로 이해하는 객체지향 설계의 두 번째 챕터 객체지향 프로그래밍의 영화 할인 애플리케이션 첫 번째 커밋 작성된 클래스는 상영(Screening), 돈(Money), 예약(Reservation)만 작성되었다.
github.com
- 협력
- 협력에 관한 내용은 위 깃헙의 02_객체지향 프로그래밍 : 영화 예매 애플리케이션의 첫 번째 커밋을 통해 간결하게 설명해두었다.
- 상속(Inheritance), 다형성(polymorphism), 추상화(abstraction)
- TEMPLATE METHOD Pattern : 부모 클래스에 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴
- 오버라이딩(Overriding) VS 오버로딩(Overloading)
- 오버라이딩(Overriding)
- 부모 클래스에 정의된 같은 이름, 같은 파라미터 목록을 가진 메서드를 자식 클래스에서 재정의하는 경우
- 자식 클래스의 메서드는 오버라이딩한 부모 클래스의 메서드를 가리기 때문에 외부에서는 부모 클래스의 메서드가 보이지 않음.
- 오버로딩(Overloading)
- 메서드의 이름은 같지만 제공되는 파라미터 목록이 다름
- 오버로딩한 메서드는 원래의 메서드를 가리지 않기 때무누에 이 메서드들은 공존.
- 컴파일 시간 의존성과 실행 시간 의존성
- 코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있음.
- 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있음.
- github 예제) Moive 클래스의 경우 코드상에서 DiscountPolicy와 의존관계에 있지만, 실행시점에서는 AmountDiscountPolicy 또는 PercentDiscountPolicy와 의존관계에 있을 수 있음.
- 확장 가능한 객체지향 설계는 코드의 의존성과 실행 시점의 의존성이 다르다.
- 코드의 의존성과 실행시점의 의존성이 다르면 다를수록 코드는 더 유연해지고 확장 가능해진다.
- But, 코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드를 이해하기 어려워 진다.
- a.k.a 회사 프로젝트 중 게시판 메뉴의 조회에 상속 구조 설계(내가 짜고 내가 헷갈리는 경우도 발생할 수 있음..)
- 트레이드오프
- 설계가 유연해질 수록 코드를 이해하고 디버깅하기는 점점 더 어려워진다.
- 유연성을 억제하면 코드를 이해하고 디버깅하기는 쉬워지지만 재사용성과 확장 가능성이 낮아진다.
- 무조건 유연한 설계도, 무조건 읽기 쉬운 코드도 정답이 아니다.
- 유연성과 가독성 사이에서 많이 고민하고 잘 선택해야 한다.(컨벤션을 따를 수도..)
- 코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있음.
- 상속
- 객체지향에서 코드를 재사용하기 위해 가장 널리 사용되는 방법
- 클래스 사이의 관계를 설정하는 것만으로 기존 클래스가 가지고 있는 모든 속성과 행동을 새로운 클래스에 포함시킬 수 있다.
- 기존 클래스를 기반으로 새로운 클래스를 쉽고 빠르게 추가할 수 있는 간편한 방법을 제공
- 부모 클래스의 구현은 공유하면서도 행동이 다른 자식 클래스를 쉽게 추가할 수 있다.
- 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍(programming by difference)이라고 한다.
- 자식 클래스는 상속을 통해 부모 클래스의 인터페이스를 물려받기 때문에 부모 클래스 대신 사용될 수 있다.
- 컴파일러는 코드 상에서 부모 클래스가 나오는 모든 장소에서 자식 클래스를 사용하는것을 허용
- 업캐스팅(upcasting) : 자식 클래스가 부모 클래스를 대신하는 것
- 구현 상속(implementation inheritance) VS 인터페이스 상속(interface inheritance)
- 구현 상속
- 서브클래싱(subclassing)
- 순수하게 코드를 재사용하기 위한 목적으로 상속을 사용하는 것
- 인터페이스 상속
- 서브타이핑(subtyping)
- 다형적인 협력을 위해 부모 클래스와 자식 클래스가 인터페이스를 공유할 수 있도록 상속을 이용하는 것
- 상속은 구현 상속이 아닌 인터페이스 상속을 위해 사용해야 한다.
- 인터페이스를 재사용할 목적이 아니라 구현을 재사용할 목적으로 상속을 사용하면 변경에 취약한 코드를 낳게 될 확률이 높다.
- 구현 상속
- 다형성
- 메시지를 전달하는 클래스는 동일한 메시지를 전송하지만, 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다.
- 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력
- 다형적인 협력에 참여하는 객체들은 모두 같은 메시지를 이해할 수 있어야 한다. (= 인터페이스가 동일하다.)
- 객체지향 프로그램의 컴파일 시간 의존성과 실행 시간 의존성이 다를 수 있다는 사실을 기반으로 한다.
- 컴파일 시간 의존성과 실행 시간 의존성을 다르게 만들 수 있는 객체지향의 특성을 이용해 서로 다른 메서드를 실행할 수 있게 한다.
- 지연 바인딩(lazy binding) or 동적 바인딩(dynamic binding)
- 메시지와 메서드를 실행 시점에 바인딩
- 초기 바인딩(early binding) or 정적 바인딩(static binding)
- 전통적인 함수 호출처럼 컴파일 시점에 실행될 함수나 프로시저를 결졍하는 것
- 메시지를 전달하는 클래스는 동일한 메시지를 전송하지만, 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다.
- 인터페이스와 다형성
- 인터페이스(interface) : 구현은 공유할 필요 없고, 순수하게 인터페이스만 공유하는 상속의 형태
- 구현에 대한 고려 없이 다형적인 협력에 참여하는 클래스들이 공유 가능한 외부 인터페이스를 정의하는 것
- 추상화와 유연성
- 추상화의 장점
- 첫째, 추상화의 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있다.
- 세부사항에 억눌리지 않고 상위 개념만으로도 도메인의 중요한 개념을 설명할 수 있게 한다.
- 둘째, 추상화를 이용하면 설계가 좀 더 유연해진다.
- 추상화를 이용해 상위 정책을 표현하면 기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고 확장할 수 있다.
- 첫째, 추상화의 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있다.
- 추상화의 장점
- 오버라이딩(Overriding)
- github_02. 영화 예매 애플리케이션 구현 두 번째 - 상속과 다형성
- https://github.com/JIHYEON-PF/book_review_object/pull/6
02_객체지향 프로그래밍 : 영화 예매 애플리케이션 - 상속과 다형성 by JIHYEON-PF · Pull Request #6 · JIH
개요 오브젝트 : 코드로 이해하는 객체지향 설계의 두 번째 챕터 객체지향 프로그래밍의 두번째 PR 해당 PR을 통해 작성된 클래스는 다음과 같다 할인 조건(DiscountCondition) 기간 할인 조건(PeriodCondi
github.com
- 트레이드오프
- 구현과 관련된 모든 것들이 트레이드오프의 대상이 될 수 있다.
- 트레이드오프를 통해 얻어진 결론과 그렇지 않은 결론 사이의 차이는 크다.
- 합성(Composition)
- 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용 하는 방법
- 상속 VS 합성
- 상속
- 단점
- 캡슐화 위반
- 부모 클래스의 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화된다.
- 캡슐화의 약화는 자식 클래스가 부모 클래스에 강하게 결합되도록 만들기 대문에 부모 클래스를 변경할 대 자식 클래스도 함께 변경될 확률을 높인다.
- 설계를 유연하지 못하게 한다.
- 상속은 부모 클래스와 자식 클래스 사이의 관계를 컴파일 시점에 결정한다. 따라서 실행 시점에 객체의 종류를 변경하는 것이 불가능하다.
- 캡슐화 위반
- 단점
- 합성
- 인터페이스에 정의된 메시지를 통해서만 코드를 재사용 하는 방법
- 상속의 문제점 해결
- 캡슐화 위반 : 인터페이스에 정의된 메시지를 통해서만 재사용이 가능하기 때문에 구현을 효과적으로 캡슐화 할 수 있다.
- 유연하지 못한 설계 : 의존하는 인스턴스를 교체하는 것(setter)이 비쇼적 쉽기 때문에 설계를 유연하게 만든다.
- 상속은 클래스를 통해 강하게 결합되는 데 비해 합성은 메시지를 통해 느슨하게 결합한다.
- 코드 재사용을 위해서는 상속보다는 합성을 선호하는 것이 더 좋은 방법
- 상속
- 대부분의 설계에서는 상속과 합성을 함께 사용
- 정리 : 코드의 재사용을 위해서는 합성 / 다형성을 위해 인터페이스를 재사용하는 것은 상속과 합성을 조합
- github_03. 영화 예매 애플리케이션 구현 세 번째 - 트레이드오프
- https://github.com/JIHYEON-PF/book_review_object/pull/7
02_객체지향 프로그래밍 : 영화 예매 애플리케이션 - 트레이드 오프 by JIHYEON-PF · Pull Request #7 · JIH
개요 오브젝트 : 코드로 이해하는 객체지향 설계의 두 번째 챕터 객체지향 프로그래밍의 세 번째 PR 트레이드 오프를 통해 클래스 구조가 수정되었다. 부모 클래스 DiscountPolicy와 자식 클래스 NonDi
github.com
- 마무리
- 객체지향에서 가장 중요한 것은 애플리케이션의 기능을 구현하기 위해 협력에 참여하는 객체들 사이의 상호작용이다.
- 객체지향 설계의 핵심 : 적절한 협력을 식별하고 협력에 필요한 역할을 정의한 후에 역할을 수행할 수 있는 적잘한 객체에게 적절한 책임을 할당하는 것
- 02_객체지향 프로그래밍에 대한 생각점
- 상속을 통한 구현을 하면서 단순히 코드의 재사용 목적을 고려한 과거의 나를 반성하게 되는 시간이었다. 그리고 상속만이 코드의 중복을 방지하고 설계의 흐름을 상위단계에서 보여주기 쉽게한다고 생각하였으나, 상속 외에도 합성이라는 개념을 알게되고 나서 두 가지 개념을 좀 더 효과적으로 조합해서 사용해야 할 것같다.
- 반면에 합성을 사용할 경우 프로젝트에서 데이터에 대한 에러나 로직에 대한 에러를 통해 역추적 할 경우에는 값이 어디서 변경되는지 추적하기 어려운 경우가 발생할 수 있으므로 이 또한 무분별한 사용은 지양해야 할 것이다.
- 상속과 다형성 외에 더 중요하게 생각되는 포인트는 트레이드오프이다. 이 책 말고도 다른 책에서도 언급되기도 하는 트레이드 오프는 애플리케이션의 설계와 클래스의 구현 등 개발을 하는 모든 과정에서 필수불가결한 개념이며, 단순 설계에서도 모든 가능성을 열어두고 항시 트레이드오프를 진행하고 개발을 해야겠다.
(참고문헌: 조영호, 오브젝트 - 코드로 이해하는 객체지향 설계, 위키북스, 2022.11.30(6쇄 발행), p.37 ~ p.72)
'BookReview > 오브젝트' 카테고리의 다른 글
05_책임 할당하기 (0) | 2023.01.18 |
---|---|
04_설계 품질과 트레이드 오프 (2) | 2023.01.09 |
03_역할, 책임, 협력 (0) | 2023.01.08 |
01_객체, 설계 (0) | 2023.01.02 |
00_Intro (0) | 2023.01.02 |