오브젝트

코드로 이해하는 객체지향 설계

역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!

객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음은 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 존재로 바라보는 것이다. 세번째 걸음을 내디딜 수 있는지 여부는 협력에 참여하는 객체 들에게 얼마나 적절한 역할과 책임을 부여할 수 있느냐에 달려 있다. 객체지향의 마지막 걸음은 앞에서 설명한 개념들을 여러분이 사용하는 프로그래밍 언어라는 틀에 흐트러짐 없이 담아낼 수 있는 기술을 익히는 것이다.

《객체지향의 사실과 오해》가 첫번째 걸음과 두번째 걸음인 객체와 협력에 초점을 맞췄다면 《오브젝트: 코드로 이해하는 객체지향 설계》는 세번째와 네번째 걸음인 책임의 할당과 그 구현에 초점을 맞춘다. 이 책을 읽고 나면 객체에 적절한 역할과 책임을 부여하는 방법과 유연하면서도 요구사항에 적절한 협력을 설계하는 방법을 익히게 될 것이다. 나아가 프로그래밍 언어라는 도구를 이용해 객체지향의 개념과 원칙들을 오롯이 표현할 수 있는 방법 역시 익힐 수 있을 것이다.

 

★ 이 책에서 다루는 내용 ★

  • 역할, 책임, 협력에 기반해 객체지향 프로그램을 설계하고 구현하는 방법
  • 응집도와 결합도를 이용해 설계를 트레이드오프하는 방법
  • 설계를 유연하게 만드는 다양한 의존성 관리 기법
  • 타입 계층을 위한 상속과 코드 재사용을 위한 합성의 개념
  • 다양한 설계 원칙과 디자인 패턴

 

추천사

“《오브젝트》는 객체지향 프로그래밍의 발전에 기여한 많은 선배 개발자들의 수고와 고민의 결과를 조영호 님의 실무 경험과 통찰에 녹여내어 실전에서 접할 수 있을 법한 구체적인 예제 코드에 담아 설명한다. 객체지향 원리와 이론들이 일상에서 내가 작성하는 코드와 어떤 관련이 있는지 의문을 가졌다면 이 책을 통해 많은 해답을 찾을 수 있을 것이다.”

— 이일민(토비의 스프링 저자)

 

도서 상세 이미지

조영호

객체지향 설계와 도메인-주도 설계에 관심이 많으며 행복한 팀과 깔끔한 코드, 존중과 협력이 훌륭한 소프트웨어를 낳는다는 믿음을 가지고 있는 평범한 개발자다.

개발자, 교육자, 관리자를 오가며 익힌 다양한 경험을 바탕으로 좋은 코드와 함께 좋은 프로덕트를 만들기 위해 노력하고 있다.

저서로 《객체지향의 사실과 오해》가 있다.

  • ▣ 들어가며: 프로그래밍 패러다임
    • 01 패러다임의 시대
    • 02 프로그래밍 패러다임
    •  
  • ▣ 1장: 객체, 설계
    • 01 티켓 판매 애플리케이션 구현하기
    • 02 무엇이 문제인가
      • 예상을 빗나가는 코드
      • 변경에 취약한 코드
    • 03 설계 개선하기
      • 자율성을 높이자
      • 무엇이 개선됐는가
      • 어떻게 한 것인가
      • 캡슐화와 응집도
      • 절차지향과 객체지향
      • 책임의 이동
      • 더 개선할 수 있다
      • 그래, 거짓말이다!
    • 04 객체지향 설계
      • 설계가 왜 필요한가
      • 객체지향 설계
    •  
  • ▣ 2장: 객체지향 프로그래밍
    • 01 영화 예매 시스템
      • 요구사항 살펴보기
    • 02 객체지향 프로그래밍을 향해
      • 협력, 객체, 클래스
      • 도메인의 구조를 따르는 프로그램 구조
      • 클래스 구현하기
      • 협력하는 객체들의 공동체
      • 협력에 관한 짧은 이야기
    • 03 할인 요금 구하기
      • 할인 요금 계산을 위한 협력 시작하기
      • 할인 정책과 할인 조건
      • 할인 정책 구성하기
    • 04 상속과 다형성
      • 컴파일 시간 의존성과 실행 시간 의존성
      • 차이에 의한 프로그래밍
      • 상속과 인터페이스
      • 다형성
      • 인터페이스와 다형성
    • 05 추상화와 유연성
      • 추상화의 힘
      • 유연한 설계
      • 추상 클래스와 인터페이스 트레이드오프
      • 코드 재사용
      • 상속
      • 합성
    •  
  • ▣ 3장: 역할, 책임, 협력
    • 01 협력
      • 영화 예매 시스템 돌아보기
      • 협력
      • 협력이 설계를 위한 문맥을 결정한다
    • 02 책임
      • 책임이란 무엇인가
      • 책임 할당
      • 책임 주도 설계
      • 메시지가 객체를 결정한다
      • 행동이 상태를 결정한다
    • 03 역할
      • 역할과 협력
      • 유연하고 재사용 가능한 협력
      • 객체 대 역할
      • 역할과 추상화
      • 배우와 배역
    •  
  • ▣ 4장: 설계 품질과 트레이드오프
    • 01 데이터 중심의 영화 예매 시스템
      • 데이터를 준비하자
      • 영화를 예매하자
    • 02 설계 트레이드오프
      • 캡슐화
      • 응집도와 결합도
    • 03 데이터 중심의 영화 예매 시스템의 문제점
      • 캡슐화 위반
      • 높은 결합도
      • 낮은 응집도
      • 캡슐화를 지켜라
    • 04 자율적인 객체를 향해
      • 스스로 자신의 데이터를 책임지는 객체
      • 캡슐화 위반
    • 05 하지만 여전히 부족하다
      • 높은 결합도
      • 낮은 응집도
      • 데이터 중심 설계는 객체의 행동보다는 상태에 초점을 맞춘다
    • 06 데이터 중심 설계의 문제점
      • 데이터 중심 설계는 객체를 고립시킨 채 오퍼레이션을 정의하도록 만든다
    •  
  • ▣ 5장: 책임 할당하기
    • 01 책임 주도 설계를 향해
      • 데이터보다 행동을 먼저 결정하라
      • 협력이라는 문맥 안에서 책임을 결정하라
      • 책임 주도 설계
    • 02 책임 할당을 위한 GRASP 패턴
      • 도메인 개념에서 출발하기
      • 정보 전문가에게 책임을 할당하라
      • 높은 응집도와 낮은 결합도
      • 창조자에게 객체 생성 책임을 할당하라
    • 03 구현을 통한 검증
      • DiscountCondition 개선하기
      • 타입 분리하기
      • 다형성을 통해 분리하기
      • 변경으로부터 보호하기
      • Movie 클래스 개선하기
      • 변경과 유연성
    • 04 책임 주도 설계의 대안
      • 메서드 응집도
      • 객체를 자율적으로 만들자
    •  
  • ▣ 6장: 메시지와 인터페이스
    • 01 협력과 메시지
      • 클라이언트-서버 모델
      • 메시지와 메시지 전송
      • 메시지와 메서드
      • 퍼블릭 인터페이스와 오퍼레이션
      • 시그니처
    • 02 인터페이스와 설계 품질
      • 묻지 말고 시켜라
      • 의도를 드러내는 인터페이스
      • 함께 모으기
    • 03 원칙의 함정
      • 디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다
      • 결합도와 응집도의 충돌
    • 04 명령-쿼리 분리 원칙
      • 반복 일정의 명령과 쿼리 분리하기
      • 명령-쿼리 분리와 참조 투명성
      • 책임에 초점을 맞춰라
    •  
  • ▣ 7장: 객체 분해
    • 01 프로시저 추상화와 데이터 추상화
    • 02 프로시저 추상화와 기능 분해
      • 메인 함수로서의 시스템
      • 급여 관리 시스템
      • 급여 관리 시스템 구현
      • 하향식 기능 분해의 문제점
      • 언제 하향식 분해가 유용한가?
    • 03 모듈
      • 정보 은닉과 모듈
      • 모듈의 장점과 한계
    • 04 데이터 추상화와 추상 데이터 타입
      • 추상 데이터 타입
    • 05 클래스
      • 클래스는 추상 데이터 타입인가?
      • 추상 데이터 타입에서 클래스로 변경하기
      • 변경을 기준으로 선택하라
      • 협력이 중요하다
    •  
  • ▣ 8장: 의존성 관리하기
    • 01 의존성 이해하기
      • 변경과 의존성
      • 의존성 전이
      • 런타임 의존성과 컴파일타임 의존성
      • 컨텍스트 독립성
      • 의존성 해결하기
    • 02 유연한 설계
      • 의존성과 결합도
      • 지식이 결합을 낳는다
      • 추상화에 의존하라
      • 명시적인 의존성
      • new는 해롭다
      • 가끔은 생성해도 무방하다
      • 표준 클래스에 대한 의존은 해롭지 않다
      • 컨텍스트 확장하기
      • 조합 가능한 행동
    •  
  • ▣ 9장: 유연한 설계
    • 01 개방-폐쇄 원칙
      • 컴파일타임 의존성을 고정시키고 런타임 의존성을 변경하라
      • 추상화가 핵심이다
    • 02 생성 사용 분리
      • FACTORY 추가하기
      • 순수한 가공물에게 책임 할당하기
    • 03 의존성 주입
      • 숨겨진 의존성은 나쁘다
    • 04 의존성 역전 원칙
      • 추상화와 의존성 역전
      • 의존성 역전 원칙과 패키지
    • 05 유연성에 대한 조언
      • 유연한 설계는 유연성이 필요할 때만 옳다
      • 협력과 책임이 중요하다
    •  
  • ▣ 10장: 상속과 코드 재사용
    • 01 상속과 중복 코드
      • DRY 원칙
      • 중복과 변경
      • 상속을 이용해서 중복 코드 제거하기
      • 강하게 결합된 Phone과 NightlyDiscountPhone
    • 02 취약한 기반 클래스 문제
      • 불필요한 인터페이스 상속 문제
      • 메서드 오버라이딩의 오작용 문제
      • 부모 클래스와 자식 클래스의 동시 수정 문제
    • 03 Phone 다시 살펴보기
      • 추상화에 의존하자
      • 차이를 메서드로 추출하라
      • 중복 코드를 부모 클래스로 올려라
      • 추상화가 핵심이다
      • 의도를 드러내는 이름 선택하기
      • 세금 추가하기
    • 04 차이에 의한 프로그래밍
    •  
  • ▣ 11장: 합성과 유연한 설계
    • 01 상속을 합성으로 변경하기
      • 불필요한 인터페이스 상속 문제: java.util.Properties와 java.util.Stack
      • 메서드 오버라이딩의 오작용 문제: InstrumentedHashSet
      • 부모 클래스와 자식 클래스의 동시 수정 문제: PersonalPlaylist
    • 02 상속으로 인한 조합의 폭발적인 증가
      • 기본 정책과 부가 정책 조합하기
      • 상속을 이용해서 기본 정책 구현하기
      • 기본 정책에 세금 정책 조합하기
      • 기본 정책에 기본 요금 할인 정책 조합하기
      • 중복 코드의 덫에 걸리다
    • 03 합성 관계로 변경하기
      • 기본 정책 합성하기
      • 부가 정책 적용하기
      • 기본 정책과 부가 정책 합성하기
      • 새로운 정책 추가하기
      • 객체 합성이 클래스 상속보다 더 좋은 방법이다
    • 04 믹스인
      • 기본 정책 구현하기
      • 트레이트로 부가 정책 구현하기
      • 부가 정책 트레이트 믹스인하기
      • 쌓을 수 있는 변경
    •  
  • ▣ 12장: 다형성
    • 01 다형성
    • 02 상속의 양면성
      • 상속을 사용한 강의 평가
      • 데이터 관점의 상속
      • 행동 관점의 상속
    • 03 업캐스팅과 동적 바인딩
      • 같은 메시지, 다른 메서드
      • 업캐스팅
      • 동적 바인딩
    • 04 동적 메서드 탐색과 다형성
      • 자동적인 메시지 위임
      • 동적인 문맥
      • 이해할 수 없는 메시지
      • self 대 super
    • 05 상속 대 위임
      • 위임과 self 참조
      • 프로토타입 기반의 객체지향 언어
    •  
  • ▣ 13장: 서브클래싱과 서브타이핑
    • 01 타입
      • 개념 관점의 타입
      • 프로그래밍 언어 관점의 타입
      • 객체지향 패러다임 관점의 타입
    • 02 타입 계층
      • 타입 사이의 포함관계
      • 객체지향 프로그래밍과 타입 계층
    • 03 서브클래싱과 서브타이핑
      • 언제 상속을 사용해야 하는가?
      • is-a 관계
      • 행동 호환성
      • 클라이언트의 기대에 따라 계층 분리하기
      • 서브클래싱과 서브타이핑
    • 04 리스코프 치환 원칙
      • 클라이언트와 대체 가능성
      • is-a 관계 다시 살펴보기
      • 리스코프 치환 원칙은 유연한 설계의 기반이다
      • 타입 계층과 리스코프 치환 원칙
    • 05 계약에 의한 설계와 서브타이핑
      • 서브타입과 계약
    •  
  • ▣ 14장: 일관성 있는 협력
    • 01 핸드폰 과금 시스템 변경하기
      • 기본 정책 확장
      • 고정요금 방식 구현하기
      • 시간대별 방식 구현하기
      • 요일별 방식 구현하기
      • 구간별 방식 구현하기
    • 02 설계에 일관성 부여하기
      • 조건 로직 대 객체 탐색
      • 캡슐화 다시 살펴보기
    • 03 일관성 있는 기본 정책 구현하기
      • 변경 분리하기
      • 변경 캡슐화하기
      • 협력 패턴 설계하기
      • 추상화 수준에서 협력 패턴 구현하기
      • 구체적인 협력 구현하기
      • 협력 패턴에 맞추기
      • 패턴을 찾아라
    •  
  • ▣ 15장: 디자인 패턴과 프레임워크
    • 01 디자인 패턴과 설계 재사용
      • 소프트웨어 패턴
      • 패턴 분류
      • 패턴과 책임-주도 설계
      • 캡슐화와 디자인 패턴
      • 패턴은 출발점이다
    • 02 프레임워크와 코드 재사용
      • 코드 재사용 대 설계 재사용
      • 상위 정책과 하위 정책으로 패키지 분리하기
      • 제어 역전 원리
    •  
  • ▣ 마치며: 나아가기
    •  
  • ▣ 부록 A: 계약에 의한 설계
    • 01 협력과 계약
      • 부수효과를 명시적으로
      • 계약
    • 02 계약에 의한 설계
      • 사전조건
      • 사후조건
      • 불변식
    • 03 계약에 의한 설계와 서브타이핑
      • 계약 규칙
      • 가변성 규칙
      • 함수 타입과 서브타이핑
    •  
  • ▣ 부록 B: 타입 계층의 구현
    • 클래스를 이용한 타입 계층 구현
    • 인터페이스를 이용한 타입 계층 구현
    • 추상 클래스를 이용한 타입 계층 구현
    • 추상 클래스와 인터페이스 결합하기
    • 덕 타이핑 사용하기
    • 믹스인과 타입 계층
    •  
  • ▣ 부록 C: 동적인 협력, 정적인 코드
    • 01 동적 모델과 정적 모델
      • 행동이 코드를 결정한다
      • 변경을 고려하라
    • 02 도메인 모델과 구현
      • 도메인 모델에 관하여
      • 몬스터 설계하기
      • 행동과 변경을 고려한 도메인 모델
      • 분석 모델, 설계 모델, 그리고 구현 모델
    •  
  • ▣ 부록 D: 참고문헌 - 참고문헌
  • IV쪽, 1번째 줄

    무엇인가라는 원론적면서도 다소

    ==>

    무엇인가라는 원론적이면서도 다소

  • 9쪽, 11번째 줄

    이벤트에 담청된 관람객과

    ==>

    이벤트에 당첨된 관람객과

  • 10쪽, 4번째 줄

    초대장(ticket), 티켓(invitation), 현금(amount)을

    ==>

    초대장(invitation), 티켓(ticket), 현금(amount)을

  • 13쪽, 그림 1.1을 다음 그림으로 교체(좌측 하단의 Audience 클래스명을 TicketSeller로 변경)

    object-1-1.png

  • 18쪽, 10번째 줄

    관람객과 판매원를 자율적인

    ==>

    관람객과 판매원을 자율적인

  • 18쪽, 본문 밑에서 4번째 줄

    TickeSeller가 근무하는

    ==>

    TicketSeller가 근무하는

  • 18쪽, 페이지 하단 그림 1.3에서 박스 영역에 아래 코드도 포함

    if (audience.getBag().hasInvitation()) {
    
  • 24쪽, 그림 1.6을 다음 다음으로 교체(TicketSeller에서 Audience 클래스로 이어지는 선의 화살표를 추가)

    object-1-6.png

  • 32쪽, 밑에서 두 번째 줄

    TicketSeller의 자율성은 높였지만

    ==>

    TicketOffice의 자율성은 높였지만

  • 48쪽, 본문 첫 번째 줄

    상영 정보(Screening),

    ==>

    상영 정보(screening),

  • 48쪽, 예제 코드

    public class Reservation {
      private Customer customer;
      private Screening Screening;
      private Money fee;
      private int audienceCount;
    
      public Reservation(Customer customer, Screening Screening, Money fee, int audienceCount) {
        this.customer = customer;
        this.Screening = Screening;
        this.fee = fee;
        this.audienceCount = audienceCount;
      }
    }
    

    ==>

    public class Reservation {
      private Customer customer;
      private Screening screening;
      private Money fee;
      private int audienceCount;
    
      public Reservation(Customer customer, Screening screening, Money fee, int audienceCount) {
        this.customer = customer;
        this.screening = screening;
        this.fee = fee;
        this.audienceCount = audienceCount;
      }
    }
    
  • 51쪽, 본문 밑에서 5번째 줄

    DiscountCondition의 리스트인 discountCoinditions

    ==>

    DiscountCondition의 리스트인 conditions

  • 51쪽, 본문 밑에서 4번째 줄

    calculateDiscountAmount 메서드는 전체 할인 정책에 대해

    ==>

    calculateDiscountAmount 메서드는 전체 할인 조건에 대해

  • 52쪽, 2번째 줄

    만족하는 할인 조건이 하나도 존재하지 않는다면 Screening의 getMovieFee 메서드를 호출해 원래의 영화 가격을 반환한다.

    ==>

    만족하는 할인 조건이 하나도 존재하지 않는다면 할인 요금으로 0원을 반환한다.

  • 58쪽, 4번째 줄

    계산하기 위해 할인 정책을 적용하고

    ==>

    계산하기 위해 금액 할인 정책을 적용하고

  • 60쪽, 그림 2.10의 캡션

    그림 2.10 파생 클래스와 파생 클래스, 조상과 자손

    ==>

    그림 2.10 기반 클래스와 파생 클래스, 조상과 자손

  • 64쪽, 인터페이스와 다형성의 9번째 줄

    실체화하고 있는 Sequence Condition

    ==>

    실체화하고 있는 SequenceCondition

  • 64쪽, 본문 밑에서 6번째 줄

    C++의 경우 추상 파생 클래스(Abstract Base Class, ABC)를 통해

    ==>

    C++의 경우 추상 기반 클래스(Abstract Base Class, ABC)를 통해

  • 65쪽, 3번째 줄

    공유하며 DiscountCondition를 대신해서

    ==>

    공유하며 DiscountCondition을 대신해서

  • 69쪽, 그림 2.15를 다음 다음으로 교체(NoneDiscountPolicy 클래스의 메서드명 변경)

    object-2-15.png

  • 69쪽, 본문 밑에서 4번째 줄

    구현과 관려된 모든

    ==>

    구현과 관련된 모든

  • 70쪽, 본문 밑에서 5번째 줄

    Movie의 calculateFee 메서드 안에서

    ==>

    Movie의 calculateMovieFee 메서드 안에서

  • 79쪽, 그림 3.3에서 우측 하단의 Movie 카드

    Movie

    ==>

    DiscountCondition

  • 107쪽, 예제 코드의 밑에서 8번째 줄

    fee = movie.getFee().minus(discountAmount).times(audienceCount);
    

    ==>

    fee = movie.getFee().minus(discountAmount);
    
  • 111쪽, 그림 4.2를 다음 그림으로 교체(문구 교체)

    object-4-2.png

  • 111쪽, 그림 4.3을 다음 그림으로 교체(문구 교체)

    object-4-3.png

  • 137쪽, 본문 밑에서 1번째 줄

    두 가지 할인 조건을 적용할 수 없다는

    ==>

    두 가지 할인 정책을 적용할 수 없다는

  • 141쪽, 두번째 그림

    3: 할인 요금을 계산하라

    ==>

    3: 할인 여부를 판단하라

  • 152쪽, 본문 2번째 줄

    물론 순번 조건을

    ==>

    물론 기간 조건을

  • 157쪽, 세 번째 예제 코드에 아래 isDiscountable 메서드를 추가

    private boolean isDiscountable(Screening screening) {
      return discountConditions.stream().anyMatch(condition -> condition.isSatisfiedBy(screening))
    }
    
  • 158쪽, 본문 밑에서 2번째 줄

    조건의 구현이 방법이

    ==>

    조건의 구현 방법이

  • 164쪽, 그림 5.8을 다음 그림으로 교체

    object-5-8.png

  • 164쪽, 하단 예제 코드의 5번째 줄

    movie.changeDiscountPolicy(new PercentDiscountMovie(…));
    

    ==>

    movie.changeDiscountPolicy(new PercentDiscountPolicy(…));
    
  • 167쪽, 예제 코드의 밑에서 8번째 줄

    discountAmount = movie.getFee();
    

    ==>

    discountAmount = Money.ZERO;
    
  • 167쪽, 예제 코드의 밑에서 2번째 줄

    fee = movie.getFee();
    

    ==>

    fee = movie.getFee().times(audienceCount);
    
  • 170쪽, 예제 코드의 10번째 줄

    return screening.getMovie().getFee();
    

    ==>

    return screening.getMovie().getFee().times(audienceCount);
    
  • 170쪽, 예제 코드의 밑에서 8번째 줄

    return movie.getFee();
    

    ==>

    return Money.ZERO;
    
  • 176쪽, 그림 6.2를 다음 그림으로 교체(왼쪽 박스의 :Screening을 :Movie로 교체)

    object-6-2.png

  • 177쪽, 그림 6.3을 다음 그림으로 교체(우측 상단의 문구를 수정)

    object-6-3.png

  • 179쪽, 본문 밑에서 8번째 줄

    가리키는 경우가 대분이다.

    ==>

    가리키는 경우가 대부분이다.

  • 182쪽, 페이지 중간의 불릿 목록에서 마지막 '디미터 법칙' 항목은 다음 그림과 같이 소제목 형식으로 수정

    object-182.png

  • 183쪽, 본문 첫 번째 줄

    인자로 전달된 ReservationAgency와 Screening 사이의

    ==>

    인자로 전달된 Screening과 ReservationAgency 사이의

  • 212쪽, 첫 번째 수학식에서 다음 항목 수정

    f(1) – 1    = 3
    

    ==>

    f(1) – 1    = 2
    
  • 212쪽, 두 번째 수학식에서 다음 항목 수정

    3 - 1 = 3
    

    ==>

    3 - 1 = 2
    
  • 213쪽, 페이지 상단 수학식에서 다음 항목 수정

    f(1) – 1    = 3
    

    ==>

    f(1) – 1    = 2
    
  • 236쪽, 본문 10번째 줄

    커다란 방어박을 쳐서

    ==>

    커다란 방어막을 쳐서

  • 247쪽, 본문 밑에서 8번째 줄

    Employees라는 하나의

    ==>

    Employee라는 하나의

  • 247쪽, 본문 밑에서 6번째 줄

    구현에서는 Employees 추상 데이터 타입에

    ==>

    구현에서는 Employee 추상 데이터 타입에

  • 251쪽, 그림 7.6을 다음 그림으로 교체

    object-7-6.png

  • 255쪽, 그림 8.2를 다음 그림으로 교체

    object-8-2.png

  • 257쪽, 8번째 줄

    의존하는 경우을 가리킨다.

    ==>

    의존하는 경우를 가리킨다.

  • 265쪽, 본문 7번째 줄

    만약 Movie가 PercentDiscountCondition뿐만 아니라 AmountDiscountCondition과도 협력해야 한다면

    ==>

    만약 Movie가 PercentDiscountPolicy뿐만 아니라 AmountDiscountPolicy와도 협력해야 한다면

  • 265쪽, 본문 9번째 줄

    반드시 PercentDiscountCondition의 인스턴스일

    ==>

    반드시 PercentDiscountPolicy의 인스턴스일

  • 265쪽, 본문 14번째 줄

    AmountDiscountCondition 클래스와 PercentDiscountCondition 클래스가

    ==>

    AmountDiscountPolicy 클래스와 PercentDiscountPolicy 클래스가

  • 265쪽, 본문 16번째 줄

    AmountDiscountCondition 인스턴스와 PercentDiscountCondition 인스턴스에

    ==>

    AmountDiscountPolicy 인스턴스와 PercentDiscountPolicy 인스턴스에

  • 266쪽, 본문 4번째 줄

    Movie가 PercentDiscountCondition에 의존하고 있는 경우에 Movie를 AmountDiscountCondition과 협력하도록 만들고 싶다면 어떻게 해야 할까? 방법은 하나밖에 없다. percentDiscountCondition의 타입을 PercentDiscountCondition에서 AmountDiscountCondition으로 변경하는 것이다.

    ==>

    Movie가 PercentDiscountPolicy에 의존하고 있는 경우에 Movie를 AmountDiscountPolicy와 협력하도록 만들고 싶다면 어떻게 해야 할까? 방법은 하나밖에 없다. percentDiscountPolicy의 타입을 PercentDiscountPolicy에서 AmountDiscountPolicy로 변경하는 것이다.

  • 266쪽, 본문 9번째 줄

    결국 이것은 바림직하지 못한

    ==>

    결국 이것은 바람직하지 못한

  • 266쪽, 본문 밑에서 2번째 줄

    Movie 클래스가 추상 클래스인 DiscountCondition에 의존하면 AmountDiscountCondition과 PercentDiscountCondition 모두와 협력할 수 있다. 따라서 Movie와 DiscountCondition은 느슨하게 결합된다.

    ==>

    Movie 클래스가 추상 클래스인 DiscountPolicy에 의존하면 AmountDiscountPolicy와 PercentDiscountPolicy 모두와 협력할 수 있다. 따라서 Movie와 DiscountPolicy는 느슨하게 결합된다.

  • 267쪽, 본문 첫 번째 줄

    Movie가 PercentDiscountCondition에 직접 의존하는 경우 Movie는 PercentDiscountCondition 외의 다른 인스턴스와 협력하는 것이 불가능하다. 만약 AmountDiscountCondition과 협력해야 하는 상황에서 Movie를 재사용하고자 한다면 코드를 수정할 수밖에 없다. 따라서 Movie는 PercentDiscountCondition에 강하게 결합된다.

    ==>

    Movie가 PercentDiscountPolicy에 직접 의존하는 경우 Movie는 PercentDiscountPolicy 외의 다른 인스턴스와 협력하는 것이 불가능하다. 만약 AmountDiscountPolicy와 협력해야 하는 상황에서 Movie를 재사용하고자 한다면 코드를 수정할 수밖에 없다. 따라서 Movie는 PercentDiscountPolicy에 강하게 결합된다.

  • 274쪽, 페이지 하단 코드를 다음 코드로 교체

    public class Movie {
      private DiscountPolicy discountPolicy;
    
      public Movie(String title, Duration runningTime, Money fee) {
        this(title, runningTime, fee, new AmountDiscountPolicy(...));
      }
    
      public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        ...
        this.discountPolicy = discountPolicy;
      }
    }
    
  • 283쪽, 본문 8번째 줄

    런타임 의존성와

    ==>

    런타임 의존성과

  • 284쪽, 그림 9.2의 캡션

    그림 9.2 확장에는 열려 있고 수정에는 닫혀 있는 금액 할인 정책 설계

    ==>

    그림 9.2 확장에는 열려 있고 수정에는 닫혀 있는 할인 정책 설계

  • 285쪽, 예제 코드의 11번째 줄

    return getDiscountedFee(screening);
    

    ==>

    return getDiscountAmount(screening);
    
  • 285쪽, 본문 6번째 줄

    여기서 변하는 부분을 고정하고 변하지 않는 부분을 생략하는

    ==>

    여기서 변하지 않는 부분을 고정하고 변하는 부분을 생략하는

  • 288쪽, 본문 3번째 줄

    생성과 사용을 분리(separation use from creation)[Bain08]해야 한다.

    ==>

    생성과 사용을 분리(separating use from creation)[Bain08]해야 한다.

  • 290쪽, 두 번째 예제 코드의 밑에서 4번째 줄

    Movie avatar = factory.createMadMaxMovie();
    

    ==>

    Movie avatar = factory.createAvatarMovie();
    
  • 290쪽, 그림 9.6을 다음 그림으로 교체

    object-9-6.png

  • 295쪽, 본문 첫 번째 줄

    이제 외부에서는 DiscountCondition이라는

    ==>

    이제 외부에서는 DiscountPolicy라는

  • 298쪽, 본문 2번째 줄

    생성자가 ServiceLicator를 이용해

    ==>

    생성자가 ServiceLocator를 이용해

  • 340쪽, 본문 11번째 줄

    추상 메서드인 calculateFee에만 의존한다. calculateFee 메서드의

    ==>

    추상 메서드인 calculateCallFee에만 의존한다. calculateCallFee 메서드의

  • 362쪽, 하단 예제를 다음 코드로 교체(discountAmount 생성자 인자 할당)

    public class RateDiscountableRegularPhone extends RegularPhone {
      private Money discountAmount;
    
      public RateDiscountableRegularPhone(Money amount, Duration seconds, Money discountAmount) {
        super(amount, seconds);
        this.discountAmount = discountAmount;
      }
    
      @Override
      protected Money afterCalculated(Money fee) {
        return fee.minus(discountAmount);
      }
    }
    
  • 385쪽, 페이지 중간 예제 코드를 다음 코드로 교체

    class RateDiscountableNightlyDiscountPolicy(
        nightlyAmount: Money, 
        regularAmount: Money, 
        seconds: Duration, 
        val discountAmount: Money)
      extends NightlyDiscountPolicy(nightlyAmount, regularAmount, seconds) 
      with RateDiscountablePolicy
    
  • 391쪽, 본문 첫 번째 줄

    pus_bigdecimal ==> plus_bigdecimal

  • 397쪽, 상단 예제를 다음 코드로 교체(중복된 evaluate 메서드 삭제)

      }
    
      private String gradesStatistics() {
        return grades.stream()
                     .map(grade -> format(grade))
                     .collect(joining(" "));
      }
    
      private String format(Grade grade) {
        return String.format("%s:%d", grade.getName(), gradeCount(grade));
      }
    
      private long gradeCount(Grade grade) {
        return getScores().stream()
                          .filter(grade::include)
                          .count();
      }
    }
    
  • 398쪽, 본문 2번째 줄

    동일한 시그니처의 메서드를 재정해서

    ==>

    동일한 시그니처의 메서드를 재정의해서

  • 400쪽, 그림 12.4를 다음 그림으로 교체

    object-12-4.png

  • 403쪽, 그림 12.6을 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-6.png

  • 406쪽, 그림 12.7을 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-7.png

  • 409쪽, 그림 12.9를 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-9.png

  • 412쪽, 그림 12.13을 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-13.png

  • 413쪽, 그림 12.14를 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-14.png

  • 414쪽, 그림 12.15를 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-15.png

  • 416쪽, 첫 번째 예제 코드를 다음 코드로 교체(함수 시그니처 변경)

    class GradeLecture: public Lecture
    {
    public:
      virtual int average();
      virtual int average(std::string grade);
      virtual int average(std::string, int base);
      virtual int average(char grade);
    };
    
  • 417쪽, 본문 첫 번째 줄

    동적 문맥을 결정하다는 사실은

    ==>

    동적 문맥을 결정한다는 사실은

  • 420쪽, 본문 3번째 줄

    이 경우 컴파일러는 lecture의 타입인 Lecture 클래스의

    ==>

    이 경우 컴파일러는 lecture가 가리키는 객체의 타입인 GradeLecture 클래스의

  • 423쪽, 그림 12.19를 다음 그림으로 교체(GradeLecture 클래스의 average 메서드 매개변수명을 수정)

    object-12-19.png

  • 434쪽, 본문 2번째 줄

    중요한 것은 메시지와의 협력이다.

    ==>

    중요한 것은 메시지와 협력이다.

  • 439쪽, 밑에서 4번째 줄

    자바는 ‘프로토타입 기반 언어’로

    ==>

    자바스크립트는 ‘프로토타입 기반 언어’로

  • 453쪽, 본문 밑에서 3번째 줄

    “차이점을 인식하지 못한 채 파생 클래스의 인터페이스를 통해

    ==>

    “차이점을 인식하지 못한 채 기반 클래스의 인터페이스를 통해

  • 454쪽, 7번째 줄

    대부분의 사람들은 “직사각형사각형이다(Square is-a Rectangle)”라는 이야기를 당연하게 생각한다. 하지만 직사각형사각형이 아닐 수 있다. 사실 직사각형사각형의 상속 관계는 리스코프 치환 원칙을 위반하는 고전적인 사례 중 하나다[Martin02, Feathers04, Meyers05].

    먼저 사각형을 구현한 Rectangle부터 살펴보자.

    ==>

    대부분의 사람들은 “정사각형직사각형이다(Square is-a Rectangle)”라는 이야기를 당연하게 생각한다. 하지만 정사각형직사각형이 아닐 수 있다. 사실 정사각형직사각형의 상속 관계는 리스코프 치환 원칙을 위반하는 고전적인 사례 중 하나다[Martin02, Feathers04, Meyers05].

    먼저 직사각형을 구현한 Rectangle부터 살펴보자.

  • 455쪽, 본문 2번째 줄

    setWidth 메서드와 setHeight 메서드는 사각형의 너비와 높이를 변경하고 area 메서드는 사각형의 너비를 반환한다.

    이제 이 애플리케이션에 Square를 추가하자. 개념적으로 정사각형은 사각형의 특수한 경우이고 사각형은 정사각형의 일반적인 경우이기 때문에 정사각형과 사각형 사이에 어휘적으로 is-a 관계가 성립한다.

    ==>

    setWidth 메서드와 setHeight 메서드는 직사각형의 너비와 높이를 변경하고 getArea 메서드는 직사각형의 너비를 반환한다.

    이제 이 애플리케이션에 Square를 추가하자. 개념적으로 정사각형은 직사각형의 특수한 경우이고 직사각형은 정사각형의 일반적인 경우이기 때문에 정사각형과 직사각형 사이에 어휘적으로 is-a 관계가 성립한다.

  • 456쪽, 본문 2번째 줄

    Rectangle과 협력하는 클라이언트는 사각형의 너비와 높이가 다르다고 가정한다. 따라서 아래의 예제 코드처럼 사각형의 너비와 높이를 서로 다르게 설정하도록 프로그래밍할 것이다.

    ==>

    Rectangle과 협력하는 클라이언트는 직사각형의 너비와 높이가 다르다고 가정한다. 따라서 아래의 예제 코드처럼 직사각형의 너비와 높이를 서로 다르게 설정하도록 프로그래밍할 것이다.

  • 456쪽, 밑에서 3번째 줄

    resize 메서드의 구현은 Rectangle이 세운 가정에 기반하기 때문에 사각형의 너비와 높이를 독립적으로 변경할 수 있다고 가정한다.

    ==>

    resize 메서드의 구현은 Rectangle이 세운 가정에 기반하기 때문에 직사각형의 너비와 높이를 독립적으로 변경할 수 있다고 가정한다.

  • 457쪽, 9번째 줄

    클라이언트 입장에서 정사각형을 추상화한 Square는 사각형을 추상화한 Rectangle과 동일하지 않다는 점이다.

    ==>

    클라이언트 입장에서 정사각형을 추상화한 Square는 직사각형을 추상화한 Rectangle과 동일하지 않다는 점이다.

  • 460쪽, 그림 13.8을 다음 그림으로 교체

    object-13-8.png

  • 461쪽, 본문 7번째 줄

    제약을 고려해서 리스코프 원칙을

    ==>

    제약을 고려해서 리스코프 치환 원칙을

  • 480쪽, 그림 14.9를 다음 그림으로 교체

    object-14-9.png

  • 493쪽, 본문 7번째 줄

    그리고 이 타입 계층을 클라이언트로 분리하기 위해

    ==>

    그리고 이 타입 계층을 클라이언트로부터 분리하기 위해

  • 496쪽, 페이지 상단 '서브타입 캡슐화' 항목의 2번째 줄

    이것은 파생 클래스인 DiscountPolicy와의

    ==>

    이것은 기반 클래스인 DiscountPolicy와의

  • 499쪽, 본문 마지막 줄

    이 관계는 BasicRatePolicy, FeeRule, FeeCondition, FeeRule의

    ==>

    이 관계는 BasicRatePolicy, FeeRule, FeeCondition의

  • 509쪽, 밑에서 2번째 줄

    그림 14.20에서

    ==>

    그림 14.16에서

  • 516쪽, 밑에서 11번째 줄

    자바에서는 유용하지 없다.

    ==>

    자바에서는 유용하지 않다.

  • 569쪽, 본문 3번째 줄

    받는 것은 더 약한 사후조건을 정의하는 것과 같다.

    ==>

    받는 것은 더 약한 사전조건을 정의하는 것과 같다.

  • 597쪽, 8번째 줄

    인터페이스를 풍성하게 만들고 만들고 싶다면

    ==>

    인터페이스를 풍성하게 만들고 싶다면

  • 598쪽, 본문 3번째 줄

    PercentDiscountPolicy 클래스를 상속받을 수 있을 것이다.

    ==>

    PercentDiscountPolicy 클래스를 추가할 수 있을 것이다.