From 832fab34321d3c3bbd6a250e4ede8a2c29b889e4 Mon Sep 17 00:00:00 2001 From: kiuuon Date: Sat, 17 Feb 2024 15:28:22 +0900 Subject: [PATCH] =?UTF-8?q?update:=2007.=20=ED=95=A8=EA=BB=98=20=EB=AA=A8?= =?UTF-8?q?=EC=9C=BC=EA=B8=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0 \353\252\250\354\234\274\352\270\260.md" | 387 +++++++++++++++++- 1 file changed, 386 insertions(+), 1 deletion(-) diff --git "a/\352\260\235\354\262\264\354\247\200\355\226\245\354\235\230 \354\202\254\354\213\244\352\263\274 \354\230\244\355\225\264/07. \355\225\250\352\273\230 \353\252\250\354\234\274\352\270\260.md" "b/\352\260\235\354\262\264\354\247\200\355\226\245\354\235\230 \354\202\254\354\213\244\352\263\274 \354\230\244\355\225\264/07. \355\225\250\352\273\230 \353\252\250\354\234\274\352\270\260.md" index 118122b..9dc1fcb 100644 --- "a/\352\260\235\354\262\264\354\247\200\355\226\245\354\235\230 \354\202\254\354\213\244\352\263\274 \354\230\244\355\225\264/07. \355\225\250\352\273\230 \353\252\250\354\234\274\352\270\260.md" +++ "b/\352\260\235\354\262\264\354\247\200\355\226\245\354\235\230 \354\202\254\354\213\244\352\263\274 \354\230\244\355\225\264/07. \355\225\250\352\273\230 \353\252\250\354\234\274\352\270\260.md" @@ -1,2 +1,387 @@ -# 07. 함께 모으기 +# 📌 Contents +- 마틴 파울러는 객체지향 설계 안에 있는 세 가지 상호 연관된 관점을 각각 개념 관점, 명세 관점, 구현 관점이라고 부름 + +
+ +- `개념 관점(Conceptual Perspective)`에서 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현함 +- 도메인이란 사용자들이 관심을 가지고 있는 특정 분야나 주제임 +- 소프트웨어는 도메인에 존재하는 문제를 해결하기 위해 개발됨 +- 개념 관점은 사용자가 도메인을 바라보는 관점을 반영함 +- 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심! + +
+ +- `명세 관점(Specification Perspective)`에 이르면 사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨짐 +- 명세 관점은 도메인 개념이 아닌 소프트웨어 안의 객체들의 책임에 초점을 맞춤 +- 명세 관점에서 프로그래머는 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점을 맞춤 +- 인터페이스와 구현을 분리하는 것은 훌륭한 객체지향 설계를 낳는 가장 기본적인 원칙임 +- 안타깝게도 대부분 객체지향 언어는 인터페이스와 구현을 클래스 안으로 섞어 많은 설계자들이 인터페이스와 구현의 분리의 중요성을 잊음 +- 명세 관점과 구현 관점을 명확하게 분리 하자! + +
+ +- `구현 관점(Implementation Perspective)`은 프로그래머인 우리에게 가장 익숙한 관점으로, 실제 작업을 수행하는 코드와 연관돼 있음 +- 구현 관점의 초점은 객체들이 책임을 수행하는 데 필요한 동작하는 코드를 작성하는 것임 +- 프로그래머는 객체의 책임을 '어떻게'수행할 것인가에 초점을 맞춤 +- 프로그래머는 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가함 + +
+ +- 위의 설명이 개념 관점, 명세 관점, 구현 관점의 순서대로 소프트웨어를 개발한다는 의미처럼 들릴 수 있지만, +- 개념 관점, 명세 관점, 구현 관점은 동일한 클래스를 세 가지 다른 방향에서 바라보는 것을 의미함 +- 클래스가 은유하는 개념은 도메인 관점을 반영 +- 클래스의 공용 인터페이스는 명세 관점을 반영 +- 클래스의 속성과 메서드는 구현 관점을 반영 +- 클래스는 세 가지 관점을 모두 수용할 수 있도록 개념, 인터페이스, 구현을 함께 드러내면서, +- 동시에 코드 안에서 세가지 관점을 쉽게 식별할 수 있도록 깔끔하게 분리해야 함 + +
+ +- 지금까지는 명세 관점에서 객체를 바라봤음(역할, 책임, 협력을 이용해 객체의 인터페이스를 식별하고, 메시지를 결정하고 하는 등...) +- 이번 장에서는 커피 전문점의 예제를 들어 명세 관점에 더해 개념 관점과 구현 관점을 함께 다룰 예정임 + +### 📌 커피 전문점 도메인 + +- 이 예제의 목적은 커피 전문점에서 커피를 주문하는 과정을 객체들의 협력 관계로 구현하는 것임 + +``` +이 예제에서 다룰 도메인은 동네 어디에서나 볼 수 있는 아담한 커피 전문점이다. +커피 전문점에서는 아메리카노, 카푸치노, 카라멜 마키아또, 에스프레소의 네 가지 커피를 판매하고 있다. +커피 전문점에는 커피의 이름과 가격이 적힌 메뉴판이 있다. +손님은 테이블에 앉아 메뉴판을 보고 커피를 주문한다. +주문 받은 커피를 제조하는 것은 바리스타의 몫이다. + +커피 전문점은 작고, 단순하고, 고려해야 할 사항도 거의 없는 아주 간단한 도메인이다. +우리의 최종 목표는 손님이 커피를 주문하는 사건을 컴퓨터 안에 재구성하는 것이다. 물론 객체를 이용해서 말이다. +``` + +- 커피 전문점을 객체들로 구성된 작은 세상으로 바라보면, +- 메뉴판 객체, 메뉴 항목 객체, 손님 객체, 바리스타 객체, 커피 객체로 구성된 작은 세상임 +- 각각의 객체들 사이에는 관계가 있음 +- 상태와 무관하게 동일하게 행동할 수 있는 객체들은 동일한 타입의 인스턴스로 분류할 수 있음 + - 손님 객체 - 손님 타입 + - 바리스타 객체 - 바리스타 타입 + - 아메리카노, 에스프레소, 카라멜 마키아또, 카푸치노 커피 객체 - 커피 타입 + - 메뉴판 객체 - 메뉴판 타입 + - 아메리카노, 에스프레소, 카라멜 마키아또, 카푸치노 메뉴 항목 객체 - 메뉴 항목 타입 +- 메뉴판 타입과 메뉴 항목 타입은 포함 관계 또는 합성 관계임 + - 그림으로 나타낼 때 메뉴판과 메뉴 항목을 선으로 잇고 메뉴판 쪽에 속이 찬 마름모를 그림 (메뉴 항목이 메뉴판에 포함 또는 합성) + - 메뉴 항목 좌측 아래에 4라는 숫자를 적음(메뉴 항목 4개) +- 손님 타입과 메뉴판 타입, 손님 타입과 바리스타 타입, 바리스타 타입과 커피 타입은 연관 관계 + - 그림으로 나타낼 때 두 타입을 선으로 이음 + +
+ +- 커피 전문점 도메인을 구성하는 타입들의 종류와 관계를 표현한 그림은 커피 제조와 관련된 객체들을 타입과 관계를 이용해 추상화한 일종의 모델임 +- 이처럼 소프트웨어가 대상으로 하는 영역인 도메인을 단순화해서 표현한 모델을 도메인 모델이라고 함 +- 커피 전문점이라는 도메인을 단순화해서 이해했으므로 이제 다음 단계는 지금까지 배운 지식을 총동원해서 협력을 설계(적절한 객체에게 적절한 책임을 할당)할 것임 + +### 📌 설계하고 구현하기 + +#### 커피를 주문하기 위한 협력 찾기 + +- 훌륭한 객체를 얻기 위해선 훌륭한 협력을 설계해야 함 +- 협력을 설계할 떄는 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 함 +- 즉, 메시지를 찾고 메시지를 처리하기에 적합한 객체를 선택해야함(책일을 수행할 적절한 타입을 찾아야함) + +
+ +- 현재 설계하고 있는 협력은 커피를 주문하는 것이라 첫 번째 메시지는 '커피를 주문하라'일 것임 +- '커피를 주문하라'라는 메시지를 수신할(커피를 주문할 책임을 질) 객체는 손님 타입의 인스턴스임 +- 이 경우 메시지에 '메뉴 이름'이라는 인자를 포함함 + +
+ +- 손님은 메뉴 항목에 대해 알지 못함. 따라서 자신이 선택한 메뉴 항목을 누군가가 제공해 줄 것을 요청함 +- '메뉴 항목을 찾아라'라는 새로운 메시지의 등장 +- '메뉴 항목을 찾아라'라는 메시지를 수신할(메뉴 항목을 찾을 책임을 질) 객체는 메뉴판임 +- 이 경우 메시지에 인자로 '메뉴 이름'을 포함할 것이고, 메뉴판은 메뉴 이름에 대응되는 '메뉴 항목'을 반환할 것임 + +
+ +- 손님은 자신이 주문한 커피에 대한 메뉴 항목을 얻었으니 메뉴 항목에 맞는 커피를 제조해달라고 요청할 수 있음 +- 새로운 요청이 생긴 것은 새로운 메시지가 필요하다는 신호임 +- 이 메시지를 수신할(커피를 제조할 책임을 질) 객체는 바리스타임 +- 이 경우 메시지에 인자로 '메뉴 항목'을 포함할 것이고, 메뉴판은 메뉴 이름에 대응되는 '제조된 커피'를 반환할 것임 + +
+ +- 바리스타는 커피를 제조하는 데 필요한 모든 정보를 알고 있음 +- 커피를 만들기 위한 지식은 바리스타의 상태, 기술은 바리스타의 행동으로 간주할 수 있음 +- 커피 주문을 위한 협력은 이제 바리스타가 새로운 커피를 만드는 것으로 끝남 + +
+ +- 이제 협력에 필요한 객체의 종류와 책임, 메시지에 대한 대략적인 윤곽이 잡혔으므로, +- 남은 일은 메시지를 정제함으로써 각 객체의 인터페이스를 구현 가능할 정도로 상세하게 정제하는 것임 + +#### 인터페이스 정리하기 + +- 각 객체를 협력이라는 문맥에서 떼어내어 수신 가능한 메시지만 추려내면 객체의 인터페이스가 됨 +- 객체가 어떤 메시지를 수신할 수 있다는 것은 그 객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션이 존재한다는 것을 의미함 +- 손님 객체의 인터페이스 안에는 '커피를 주문하라'라는 오퍼레이션을 제공 +- 메뉴판 객체의 인터페이스 안에는 '메뉴 항목을 찾아라'라는 오퍼레이션을 제공 +- 바리스타 객체의 인터페이스 안에는 '커피를 제조하라'라는 오퍼레이션을 제공 +- 커피 객체의 인터페이스 안에는 '생성하라'라는 오퍼레이션을 제공 + +
+ +- 객체들의 협력은 실행 시간에 컴퓨터에서 일어나는 상황을 동적으로 묘사한 모델 +- 실제 구현은 동적인 객체가 아닌 정적인 타입을 이용해 이뤄짐 +- 따라서 객체들을 포괄하는 타입을 정의한 후 식별된 오퍼레이션을 타입의 인터페이스에 추가해야 함 +- 객체의 타입을 구현하는 일반적인 방법은 클래스를 이용하는 것 +- 협력을 통해 식별된 오퍼레이션은 외부에서 접근 가능한 공용 인터페이스의 일부임 +- 따라서 인터페이스에 포함된 오퍼레이션 역시 외부에서 접근 가능하도록 공용(public)으로 선언돼 있어야 함 + +```java +// 자바의 문법을 이용한 클래스의 인터페이스 + +class Customer { + public void order(String menuName) {} +} + +class MenuItem { +} + +class Menu { + public MenuItem choose(String name) {} +} + +class Barista { + piblic Coffee makeCoffee(MenuItem menuItem) {} +} + +class Coffee { + public Coffee(MenuItem menuItem) {} +} + +``` + +#### 구현하기 + +- Customer는 Menu에게 menuName에 해당하는 MenuItem을 찾아달라고 요청해야함 +- 그리고 MenuItem을 받아 이를 Barista에게 전달해서 원하는 커피를 제조하도록 요청해야함 +- 여기서 문제는 Customer가 어떻게 Menu 객체와 Barista 객체에 접근할 것이냐임 +- 객체가 다른 객체에게 메시지를 전달하기 위해서는 객체에 대한 참조를 얻어야 함 +- 객체 참조를 얻는 다양한 방법이 있지만 여기서는 Customer의 order() 메서드 인자로 Menu와 Barista 객체를 전달 받는 방법으로 해결할 것임 +- 이 방법은 Customer의 인터페이스를 변경함 + +```java +class Customer { + public void order(String menuName, Menu menu, Barista barista) {} +} +``` + +- 이제 order() 메서드를 구현하면, + +```java +class Customer { + public void order(String menuName, Menu menu, Barista barista) { + MenuItem menuItem = menu.choose(menuName); + Coffee coffee = barista.makeCoffee(menuItem); + } +} +``` + +- 구현 도중에 객체의 인터페이스가 변경될 수 있다는 점을 눈여겨 봐야함 +- 구현하지 않고 머릿속으로만 구상한 설계는 코드로 구현하는 단계에서 대부분 변경 됨 +- 중요한 것은 설계가 아니라 코드임 +- 따라서 협력을 구상하는 단계에 너무 오랜 시간을 쏟지 말고 최대한 빨리 코드를 구현해서 설계에 이상이 없는지, 설계가 구현 가능한지를 판단해야함 +- 코드를 통한 피드백 없이는 깔끔한 설계를 얻을 수 없음 + +
+ +- Menu는 menuName에 해당하는 MenuItem을 찾아야 하는 책임이 있음 +- 이 책임을 수행하기 위해 Menu는 내부적으로 MenuItem을 관리하고 있어야 함 +- 간단하게 Menu가 MenuItem의 목록을 포함하게 하고, Menu의 choose() 메서드는 MenuItem의 목록을 하나씩 검사해가면서 이름이 동일한 MenuItem을 찾아 반환함 + +```java +class Menu { + private List items; + + public Menu (List items) { + this.items = items; + } + + public MenuItem choose(String name) { + for(MenuItem each: items) { + if (each.getName().equals(name)) { + return each; + } + } + + return null; + } +} +``` + +- 속성을 포함시키는 결정 역시 클래스를 구현하는 도중에 내려짐. 객체의 속성은 캡슐화돼야 함 +- 인터페이스를 정하는 단계에서는 객체가 어떤 속성을 가지는지, 또 그 속성이 어떤 자료 구조로 구현됐는지를 고려하지 않아야 함 +- 객체에게 책임을 할당하고 인터페이스를 결정할 때는 가급적 객체 내부의 구현에 대한 어떤 가정도 하지 말아야 함 +- 객체가 어떤 책임을 수행해야 하는지를 결정한 후에야 책임을 수행하는 데 필요한 객체의 속성을 결정하는 것이 인터페이스와 구현을 깔끔하게 분리할 수 있는 기본적인 방법임 + +
+ +- Barista는 MenuItem을 이용해서 커피를 제조함 + +```java +class Barista { + public Coffee makeCoffee(MenuItem menuItem) { + Coffee coffee = new Coffee(menuItem); + return coffee; + } +} +``` + +
+ +- Coffee는 자기 자신을 생성하기 위한 생성자를 제공함 +- Coffee는 커피 이름과 가격을 속성으로 가지고 생성자 안에서 MenuItem에 요청을 보내 커피 이름과 가격을 얻은 후 Coffee의 속성에 저장함 + +```java +class Coffee { + private String name; + private int price; + + public Coffee(MenuItem menuItem) { + this.name = menuItem.getName(); + this.price = menuItem.cost(); + } +} +``` + +
+ +- MenuItem은 getName()과 cost() 메시지에 응답할 수 있도록 메서드를 구현해야 함 + +```java +public class MenuItem { + private String name; + private int price; + + public MenuItem(String name, int price) { + this.name = name; + this.price = price; + } + + public int cost() { + return price; + } + + public String getName() { + return name; + } +} +``` + +
+ +- 인터페이스를 통해 실제로 상호작용을 해보지 않은 채 인터페이스의 모습을 정확하게 예측하는 것은 불가능에 가까움 +- 설계를 간단히 끝내고 빨리 구현해 돌입해야함 +- 설계가 제대로 그려지지 않는다면 고민하지 말고 실제로 코드를 작성해가면서 협력의 전체적인 밑그림을 그려보는 것이 좋음 +- 이것이 테스트-주도 설계로 코드를 구현하는 사람들이 하는 작업임. 그들은 테스트 코드를 작성해 가면서 협력을 설계함 + +> ####info::Note +> +> 축하한다. 모든 구현이 끝났다. 이제 여러분은 객체지향적인 설계와 구현이 무엇인지를 이해하기 시작했을 것이다. +> 메시지가 객체를 결정한다는 말의 의미, 책임을 따라 설계가 이뤄지는 과정, 인터페이스와 구현의 분리와 같은 다양한 이슈에 대해 어렴풋하게나마 개념을 잡을 수 있었기를 바란다. + +### 📌 코드와 세 가지 관점 + +#### 코드는 세 가지 관점을 모두 제공해야 한다 + +- 개념 관점에서 코드를 보면 Customer, Menu, Barista 등의 클래스가 커피 전문점 도메인을 구성하는 중요한 개념과 관계를 반영한다는 사실을 쉽게 알 수 있음 +- 소프트웨어 클래스가 도메인 개념의 특성을 최대한 수용하면 변경을 관리하기 쉽고 유지보수성을 향상시킬 수 있음 + - ex) 커피 제조 과정을 변경하고 싶으면 Barista 클래스를 보면 됨: 현실에서도 커피를 만드는 사람은 바리스타이기 때문 +- 소프트웨어 클래스와 도메인 클래스 사이의 간격이 좁으면 좁을수록 기능을 변경하기 위해 뒤적거려야 하는 코드의 양도 점점 줄어듬 + +
+ +- 명세 관점은 클래스의 인터페이스를 바라봄 +- 공용 인터페이스는 외부의 객체가 해당 객체에 접근할 수 있는 유일한 부분임 +- 인터페이스를 수정하면 해당 객체와 협력하는 모든 객체에게 영향을 미침. 객체의 인터페이스는 수정하기 어려움 +- 최대한 변화에 안정적인 인터페이스를 만들기 위해서는 인터페이스를 통해 구현과 관련된 세부 사항이 드러나지 않게 해야 함 + +
+ +- 구현 관점은 클래스의 내부 구현을 바라봄 +- 클래스의 메서드와 속성은 구현에 속하며 공용 인터페이스의 일부가 아님 +- 메서드와 속성은 철저하게 캡슐화돼야 함 + +
+ +- 개념 관점, 명세 관점, 구현 관점은 동일한 코드를 바라보는 서로 다른 관점임 +- 하나의 클래스 안에 세 가지 관점을 모두 포함하면서 각 관점을 잘 드러낼 수 있어야 함 +- 다른 사람의 코드를 보고 세 가지 관점을 쉽게 포착 못하면 명확히 드러나도록 개선해 보자! + +#### 도메인 개념을 참조하는 이유 + +- 도메인 개념 안에서 적절한 객체를 선택하는 것은 도메인에 대한 지식을 기반으로 코드의 구조와 의미를 쉽게 유추할 수 있게 함 +- 이것은 시스템의 유지보수성에 커다란 영향을 미침 +- 소프트웨어 클래스가 도메인 개념을 따르면 변화에 쉽게 대응할 수 있음 + +#### 인터페이스와 구현을 분리하라 + +- 명세 관점과 구현 관점이 뒤섞여 머릿속을 함부로 어지럽히지 못하게 하라 +- 명세 관점은 클래스의 안정적인 측면을 드러내고, 구현 관점은 클래스의 불안정한 측면을 드러내야 함 +- 마틴 파울러는 개념적인 관점과 명세 관점 사이는 그렇게 중요하지 않은 경우가 많지만, +- 명세 관점과 구현 관점을 분리하는 것은 매우 중요하다고 주장함 +- 프로그래머 입장에서 가장 많이 접하는 것은 코드이므로 구현 관점을 빈번하게 사용하겠지만, +- 실제로 훌륭한 설계를 결정하는 측면은 명세 관점인 객체의 인터페이스임 +- 명세 관점이 설계를 주도하게 하면 설계의 품질이 향상될 수 있다! + +> ####info::Note +> +> 중요한 것은 여러분이 클래스를 봤을 때 클래스를 명세 관점과 구현 관점으로 나눠볼 수 있어야 한다는 것이다. +> 캡슐화를 위반해서 구현을 인터페이스 밖으로 노출해서도 안 되고, 인터페이스와 구현을 명확하게 분리하지 않고 흐릿하게 섞어놓아도 안 된다. +> 결국 세 가지 관점 모두에서 클래스를 바라볼 수 있으려면 훌륭한 설계가 뒷받침돼야 하는 것이다. + +# ❓ Questions + +### ❓ public class vs class + +- 공용 인터페이스의 포함 된 오퍼레이션은 외부에서 접근 가능하도록 공용(public)으로 선언돼 있어야 한다고 나온다. +- 그래서 메서드 앞에 public을 붙이는 것은 다른 클래스에서도 메서드를 사용할 수 있게 하기 위해서인 것을 알게 되었다. +- 그런데 책에서 예제를 구현할 떄 MenuItem 클래스 앞에 public을 붙인다. + +```java +public class MenuItem { + private String name; + private int price; + + pulic MenuItem(String name, int price) { + this.name = name; + this.price = price; + } + + public int cost() { + return price; + } + + public String getName() { + return name; + } +} +``` + +- class 앞에 public을 붙인 이유가 무엇일까? +- public class와 그냥 class의 차이가 무엇일까? + +
+ +- 자바에서 public class는 다른 패키지에 접근이 가능하다. +- 그리고 public class의 이름은 파일명과 동일해야만 한다. +- 동일해야 하는 이유는 하나의 자바파일에 여러 개의 클래스를 작성하는 경우에는 하나의 대표 클래스를 지정해 파일명과 동일하게 하는 것이 가독성에 좋아서 그렇다고 한다. +- public class와 그냥 class에는 이 정도 차이가 있는 것 같은데 왜 MenuItem 클래스 앞에 public을 붙인 건지는 여러가지 생각을 해봐도 잘 모르겠다. +- 생각해본 이유들은 다음과 같다. +- MenuItem을 대표 클래스라 생각해서 붙인건가? -> 왜 MenuItem이 대표 클래스인거지? +- 다른 패키지에서 MenuItem이 필요해서 붙인건가? -> 다른 클래스 말고 MenuItem만 필요한 이유가 뭐지? +- 다른 클래스들과 MenuItem 클래스가 다른 패키지에 있고 MenuItem은 다른 클래스들에서 필요하니까 public을 붙인건가? -> MenuItem 클래스만 다른 패키지인 이유가 뭐지? +- 이 정도 생각을 해봤는데 MenuItem 앞에만 public을 붙인 정확한 이유는 잘 모르겠다... + +# 🔚 책을 읽고 나서 + +나는 원래 객체지향이 너무 어렵다 생각이 들고, 클래스 등을 이용한 코드 작성을 왜 굳이 하나 싶어서 객체지향을 굳이 공부해야 하나 싶었었다. 그리고 프론트엔드 개발자를 목표하고 있기 때문에 객체지향 공부를 안해도 된다고 생각했었다. 하지만 코딩 교육 프로그램을 경험하면서 프론트엔드 개발자도 객체지향, 디자인 패턴 등을 공부하는 것이 중요하다고 배웠고, 또 리액트에도 클래스형 컴포넌트가 있기 때문에 객체지향을 공부하면 좋을 것 같았다. 그래서 객체지향 공부을 공부해야 겠다고 마음 먹었고 이 책을 읽게 되었다. +이 책을 읽기 전에는 객체지향에 대해 제대로 공부한 적은 없고 클래스를 이용한 프로그램은 몇 번 구현해 봤었다. 그래서 그냥 클래스를 사용한다면 객체지향이라고 생각을 했고, 진짜 별 생각 없이 그냥 클래스와 메서드를 구현하는 방법으로만 코딩을 했었다. 그렇게 코딩을 하다 보니 객체지향의 장점도 잘 모르겠고 귀찮기만 했다. +이 책은 객체지향의 구현보다는 설계에 초점을 맞춘 책이다. 마지막 부분에 구현하는 내용도 있긴 하지만 책 전체적으로 객체지향의 본질, 설계에 대해 얘기하고 있다. 그리고 이 책은 내가 멋대로 생각하던 객체지향에 대한 오해를 바로 잡아 주었다. 그래서 이 책은 객체지향을 구현하는데에 흥미를 느끼지 못했던 나한테 객체지향의 세계에 대한 흥미를 높혀주었다. 객체지향에서 중요한 것이 무엇인지 알게 되었고, 객체지향이 왜 좋은지를 알게 되었다. 이 책을 한 권 딱 읽었다고 해서 객체지향 설계를 완벽하게 한다던가, 객체지향 프로그램을 완벽하게 구현할 수 있게 된 것은 아니다. 그래도 이 책 덕분에 객체지향의 세계에 관심과 흥미가 생기게 된 것 같다.