diff --git "a/book/3\354\243\274\354\260\250/\354\235\264\354\212\271\352\261\264 3\354\243\274\354\260\250.md" "b/book/3\354\243\274\354\260\250/\354\235\264\354\212\271\352\261\264 3\354\243\274\354\260\250.md" new file mode 100644 index 0000000..e544916 --- /dev/null +++ "b/book/3\354\243\274\354\260\250/\354\235\264\354\212\271\352\261\264 3\354\243\274\354\260\250.md" @@ -0,0 +1,224 @@ +# 자바꾼 3주차 + +# 🗺6장 객체 지도 + +- 길을 직접 알려주는 방법이 기능적이고 해결 방법 지향적인 접근법이라면 지도를 이용하는 방법은 구조적이고 문제 지향적인 접근법이다. +- 길을 묻는 방법은 현재의 마을에서 다른 마을로 이동하는 현재의 요구만을 만족한다. +- 지도는 현재의 목적뿐만 아니라 다양한 목적을 위해 재사용된다. + - ex) 병원, 학교, 놀이공원을 갈 수 있다. +- 지도 은유의 핵심은 기능이 아니라 구조를 기반으로 모델을 구축하는 편이 좀 더 범용적이고, 이해하기 쉬우며 변경에 안정적이라는 것이다. + - 이는 기능을 중심으로 구조를 종속시키는 접근법은 범용적이지 않고 재사용이 불가능하며, 변경에 취약한 모델을 낳게 된다. +- 객체지향은 자주 변경되는 기능이 아니라 안정적인 구조를 기반으로 시스템을 구조화한다. + +### 기능 설계 대 구조 설계 + +- 깔끔하고 단순하며 유지보수하기 쉬운 설계는 사용자의 변하는 요구사항을 반영할 수 있도록 쉽게 확장 가능한 소프트웨어를 창조할 수 있는 기반이 된다. +- 불행하게도 요구사항은 무조건 변경된다. 소프트웨어 분야에서 예외가 없는 유일한 규칙은 요구사항이 항상 변경된다는 것이다. +- 설계가 어려운 이유는 어제 약속했던 기능을 제공하는 동시에 내일 변경될지도 모르는 요구사항도 수용할 수 있는 코드를 창조해야 하기 때문이다. +- 우리는 미래를 예측할 수 없다. 단지 대비할 수 있을 뿐이다. +- 미래에 대비하는 가장 좋은 방법은 변경을 예측하는 것이 아니라 변경을 수용할 수 있는 선택의 여지를 설계에 마련해 놓은 것이다. +- 객체지향 접근방법은 자주 변경되지 않는 안정적인 객체 구조를 바탕으로 시스템 기능을 객체 간의 책임으로 분배한다. 객체지향은 객체의 구조에 집중하고, 기능이 객체의 구조를 따르게 만든다. + +### 두 가지 재료: 기능과 구조 + +- 기능은 사용자가 자신의 목표를 달성하기 위해 사용할 수 있는 시스템의 서비스다. 구조는 시스템의 기능을 구현하기 위한 기반이다. + - 구조는 사용자나 이해관계자들이 도메인에 관해 생각하는 개념과 개념들간의 관계로 표현한다. + - 기능은 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템의 행위로 표현한다. +- **유스케이스 모델링** : 기능을 수집하고 표현하기 위한 기법 +- **도메인 모델링** : 구조를 수집하고 표현하기 위한 기법 + +### 안정적인 재료: 구조 + +- **도메인 모델** + - **도메인**은 사용자가 프로그램을 사용하는 대상 분야(이유) + - **도메인 모델**은 소프트웨어 개발과 관련된 이해관계자들이 도메인에 대해 생각하는 관점이다. + - ex) 은행 업무에 종사하는 사람들은 은행 도메인을 고객과 계좌 사이의 돈의 흐름을 이해할 것이다. + - ex) 중고 자동차 판매상은 구매되는 자동차와 판매되는 자동차의 교환으로 자동차 도메인을 바라볼 것이다. + - ex) 게임 플레이어들은 게임 도메인을 캐릭터와 몬스터, 그리고 몬스터가 몬스터가 떨구는 아이템 간의 관계로 파악하다. + - 제품을 설계할 때는 제품에 관한 모든 것이 사용자들이 제품에 대해 가지고 있던 **멘탈 모델(=사람들이 자신이 상호작용하는 사물들에 대해 갖는 모형)**과 정확하게 일치해야 한다고 주장한다. + - ex) 쇼핑몰을 설계 하면 생각하는 고객의 멘탈 모델(=물건 구매, 물건 등록, 물건 조회등등)과 일치해야한다. + - 설계자는 **디자인 모델(=설계자가 마음 속에 갖고 있는 시스템에 대한 개념화)**을 기반으로 만든 시스템 이미지가 사용자 모델을 정확하게 정확하게 반영하도록 노력해야 한다. + - 도메인 모델은 소프트웨어에 대한 멘탈 모델이다. + +- **도메인의 모습을 담을 수 있는 객체지향** + - 객체지향을 이용하면 도메인에 대한 사용자 모델, 디자인 모델, 시스템 이미지 모두가 유사한 모습을 유지하도록 만드는 것이 가능합니다. + +- ********표현적 차이******** + - 은유를 통해 현실 객체와 소프트웨어 객체 사이의 차이를 최대한 줄이는 것이다. + - But 안타깝게도 대부분의 소프트웨어 도메인은 현실에 존재하지 않는 가상의 세계를 대상으로 한다. + - ex) 게임 도메인은 현실에는 존재하지 않는 강력한 마법과 괴물들의 천국이다. + + ![KakaoTalk_20221206_011738430.jpg](%E1%84%8C%E1%85%A1%E1%84%87%E1%85%A1%E1%84%81%E1%85%AE%E1%86%AB%203%E1%84%8C%E1%85%AE%E1%84%8E%E1%85%A1%205e024b2d17eb4208866d6113a7b80229/KakaoTalk_20221206_011738430.jpg) + + - 사용자가 도메인에 대해 생각하는 개념들이다. 즉, 소프트웨어 객체를 창조하기 위해 우리가 은유해야 하는 대상은 바로 도메인 모델이다. + +- **불안정한 기능을 담는 안정적인 도메인 모델** + - 도메인 모델이 제공하는 구조는 상대적으로 안정적이다. + - 그 이유는 도메인에 대한 사용자의 관점을 반영해야 하는 이유는 사용자들이 누구보다도 도메인 ‘본질적인’ 측면을 가장 잘 이해하고 있기 때문이다. + - 본질적이라는 것은 변경이 적고, 비교적 그 특성이 오랜 시간 유지된다는 것을 의미한다. + - 도메인 모델은 여러분이 기능을 구현할 때 참조할 수 있는 궁극적인 지도다. + +### 불안정한 재료: 기능 + +- **유스케이스** + - 시스템이 사용자에게 기능을 제공하는 이유는 무엇인가? + - 사용자들이 시스템을 통해 달성하고자 하는 ‘목표’가 존재하기 때문이다. + - ex) 목표 : 쇼핑몰에서 유저가 달성하고자 하는 목표는 물건을 쇼핑하는 것이다. + - 유스케이스의 가치는 사용자들의 목표를 중심으로 시스템의 기능적인 요구사항들을 이야기 형식으로 묶을 수 있다는 점이다. + +- **유스케이스의 특성** + 1. 유스케이스는 사용자와 시스템 간의 상호작용을 보여주는 ‘텍스트’다. + 1. 유스케이스는 다이어그램이 아니다. 중요한 것은 유스케이스 안에 포함돼 있는 상호작용의 흐름이다. + 2. 유스케이스는 하나의 시나리오가 아니라 여러 시나리오들의 집합이다. + 1. 시나리오 = 시스템을 사용하는 하나의 특정한 이야기 또는 경로이다. + 2. ex) 시나리오 = 예금주가 계좌를 선택하고 당일까지의 이자액을 계산하는 것이다. + 3. 유스케이스는 단순한 피처 목록이다. + 1. 피처는 시스템이 수행해야 하는 기능의 목록을 단순하게 나열한 것이다. + 2. 유스케이스의 강점은 유스케이스가 단순히 기능을 나열하는 것이 아니라 이야기를 통해 연관된 기능들을 사용함께 묶을 수 있다는 점이다. + 4. 유스케이스는 사용자 인터페이스와 관련된 세부 정보를 포함하지 말아야 한다. + 5. 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다. + +- **유스케이스는 설계 기법도, 객체지향 기법도 아니다.** + - 유스케이스에는 단지 사용자가 시스템을 통해 무엇을 얻을 수 있고, 어떻게 상호작용할 수있느냐에 관한 정보만 기술된다. + - 유스케이스는 단지 기능적 요구사항을 사용자의 목표라는 문맥을 중심으로 묶기 위한 정리 기법일 뿐이다. + +### 재료 합치기: 기능과 구조의 통합 + +- **************도메인 모델, 유스케이스 , 그리고 책임-주도 설계************** + - 변경에 유연한 소프트웨어를 만들기 위해서는 유스케이스에 정리된 시스템의 기능을 도메인 모델을 기반으로 한 객체들의 책임으로 분배해야한다. + - 시스템은 사용자와 만나는 경계에서 사용자의 목표를 만족시키기 위해 사용자와의 협력에 참여하는 커다란 객체다. + - **객체 설계 순서** + 1. 협력의 출발을 장식하는 첫 번째 메시지는 시스템의 기능을 시스템의 책임으로 바꾸는 것이다. + 2. 시스템의 할당된 커다란 책임을 이제 시스템 안의 작은 규모의 객체들이 수행해야 하는 더 작은 규모의 책임으로 세분화한다. + 3. 우리는 도메인 모델에 포함된 개념을 은유하는 소프트웨어 객체를 선택해야 한다. + 4. 협력을 완성하는 데 필요한 메시지를 식별하면서 객체들에게 책임을 할당해 나간다. + 5. 협력에 참여하는 객체를 구현하기 위해 클래스를 추가하고, 속성과 함께 메서드를 구현하면 시스템의 기능이 완성된 것이다. + - 정리하자면 요구사항들을 식별하고, 도메인 모델을 생성한 후, 소프트웨어를 클래스에 메서드를 추가하고, 요구사항을 충족시키기 위해 객체들 간의 메시지 전송을 정의하라. + - 객체의 이름은 도메인 모델에 포함된 개념으로부터 차용하고, 책임은 도메인 모델에 정의한 개념의 정의에 부합하도록 할당한다. + - 책임 할당의 기본 원칙은 책임을 수행하는 데 필요한 정보를 가진 객체에게 그 책임을 할당하는 것이기 때문이다. + +- **************기능 변경을 흡수하는 안정적인 구조************** + - 도메인 모델이 안정적인 이유 + 1. 도메인 모델을 구성하는 개념은 비즈니스가 없어지거나 완전히 개편되지 않는 한 안정적으로 유지된다. + 2. 도메인 모델을 구성하는 개념 간의 관계는 비즈니스 규칙을 기반으로 하기 때문에 비즈니스 정책이 크게 변경되지 않는 한 안정적으로 유지된다. + - 기능적인 요구사항이 변경될 경우 책임과 객체 간의 대응 관계만 수정될 뿐이다. + - 객체지향의 가장 큰 장점은 도메인을 모델링하기 위한 기법과 도메인을 프로그래밍하기 위해 사용하는 기법이 동일하다는 점이다. + + +# 💑7장 함께 모으기 + +- **개념 관점 :** 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다. + - 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심이다. +- **명세 관점 :** 도메인의 개념이 아니라 실제로 소프트웨어 안에서 살아 숨쉬는 객체들의 책임에 초점을 맞추게 된다. + - 즉 객체의 인터페이스를 바라보게 된다. +- **구현 관점 :** 구현 관점의 초점은 객체들이 수행하는데 필요한 동작하는 코드를 작성하는 것이다. + +- 위 설명은 개념, 명세, 구현의 순서대로 sw를 개발한다는 의미처럼 들릴 수도 있지만 아니다. 개념, 명세, 구현은 동일한 클래스를 세 가지 다른 방향에서 바라보는 것을 의미한다. + +### 커피 전문점 도메인 + +- **객체로 세상을 바라보는 절차** + 1. 어떤 객체들이 있고, 구별될 수 있는지 + 2. 객체들의 관계는 어떻게 구성되는지 + 3. 객체의 타입을 어떻게 나눌지 + 4. 타입 간의 관계는 어떻게 되는지 + +### **설계하고, 구현찾기** + +- **설계하기** + 1. 메시지를 먼저 선택한다. + 2. 메시지를 처리할 객체를 찾기 전에 도메인 모델 안에 책임을 수행하기에 적절한 타입이 존재하는지 살펴본다. + 3. 적잘한 타입을 발견했다면 책임을 수행할 객체를 그 타입의 인스턴스로 만든다. + 4. 메시지를 수신한 객체는 메시지를 처리할 책임을 맡게되고, 객체가 수신하는 메시지는 객체가 외부에 제공하는 공용 인터페이스에 포함된다. + + 1. 메시지를 먼저 선택한다. + +![https://user-images.githubusercontent.com/102592414/205712298-92f2e2b5-289e-41fd-8994-849e3bfad732.jpg](https://user-images.githubusercontent.com/102592414/205712298-92f2e2b5-289e-41fd-8994-849e3bfad732.jpg) + +1. 메시지를 수신할 객체는 누구인지, 즉 메시지를 책임질 수 있는 객체를 찾는다. + +![https://user-images.githubusercontent.com/102592414/205713285-47033ff5-7311-4f25-b61f-c85c44753e93.jpg](https://user-images.githubusercontent.com/102592414/205713285-47033ff5-7311-4f25-b61f-c85c44753e93.jpg) + +1. 메시지를 수신받을 때 스스로 할 수 없는 일이 있다면 다른 객체에게 요청을 보낸다. + +![https://user-images.githubusercontent.com/102592414/205713723-b631cfae-6b54-4034-a456-5d039efd0387.jpg](https://user-images.githubusercontent.com/102592414/205713723-b631cfae-6b54-4034-a456-5d039efd0387.jpg) + +1. 또 이러한 메시지를 수신할 객체는 누구인지 찾는일(1~3단계)을 반복한다. + +![https://user-images.githubusercontent.com/102592414/205713905-10879d08-78b3-4d1e-81bb-78b1d79dbf06.jpg](https://user-images.githubusercontent.com/102592414/205713905-10879d08-78b3-4d1e-81bb-78b1d79dbf06.jpg) + + + +### 최종 객체 협력 구조도 + +![https://user-images.githubusercontent.com/102592414/205714033-8a7c711b-b45a-4b8d-9be1-7faa6c94ef28.jpg](https://user-images.githubusercontent.com/102592414/205714033-8a7c711b-b45a-4b8d-9be1-7faa6c94ef28.jpg) + +- **인터페이스(=메서드 선언부) 정리하기** + - 메시지가 객체를 선택했고, 선택된 객체는 메시지를 자신의 인터페이스로 받아들인다. + - 객체가 어떤 메시지를 수신할 수 있다는 것은 그 객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션이 존재한다는 것을 의미한다. + - 각 객체를 협력이라는 문맥에서 때어내어 수신 가능한 메시지만 추려내면 객체의 인터페이스가 된다. + - 객체들을 포괄하는 타입을 정의한 후 식별된 오퍼레이션을 타입의 인터페이스에 추가해야 한다. + - 객체의 타입(=구분하는 기준)을 구현하는 일반적인 방법은 **클래스**를 이용하는 것이다. + - 인터페이스에 포함된 오퍼레이션 역시 외부에서 접근 가능하도록 **Public**으로 선언돼 있어야 한다. + +```java +class Customer { + public void order(String menuName) {} +} + +class MenuItem { + +} + +class Menu { + public MenuItem choose(String name) {} +} + +class Barista { + public Coffee makeCoffee(MenuItem menuItem) {} +} + +class Coffee { + public Coffee(MenuItem menuItem) {} +} +``` + +- **구현하기** + - 위 인터페이스의 코드를 구현해보자 + - 단 구현하는 과정에서 구조도를 반드시 참고해야한다. + + ![https://user-images.githubusercontent.com/102592414/205714033-8a7c711b-b45a-4b8d-9be1-7faa6c94ef28.jpg](https://user-images.githubusercontent.com/102592414/205714033-8a7c711b-b45a-4b8d-9be1-7faa6c94ef28.jpg) + + - Cutomer의 협력을 살펴보면서 구현 프로세스를 익히자. + 1. **Customer**는 **Menu**에게 menuName에 해당하는 **MenuItem**을 찾아달라고 요청해야한다. + 2. **Menu**에게 **MenuItem**을 받아서 이를 **Barista**에게 전달해서 원하는 커피를 제조하도록 요청해야 한다. + - 그러나 이 과정에서 **Customer**가 과연 어떻게 **Menu**객체와 **Barista**객체에 접근할 것인지가 중요하다. + - 객체가 다른 객체에게 메시지를 전송하기 위해서는 먼저 객체에 대한 참조를 얻어야 하는데, 참조를 얻는 여러 다양한 방법들 중 여기서는 **Customer** 의 `order()` 메서드의 인자로 **Menu** 와 **Barista** 객체를 전달받는 방법으로 문제를 해결한다. + + ```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); + } + } + ``` + + - 구현하지 않고 머릿속으로만 구상한 설계는 코드로 구현하는 단계에서 대부분 변경된다. 그러니 협력을 구상하는 단계에 너무 오랜 시간을 쏟지말고 최대한 빨리 코드를 구현해서 피드백을 통한 깔끔한 설계를 받아야한다. + + ![https://user-images.githubusercontent.com/102592414/205807783-954132bd-4827-483d-a605-e98c275aa3c8.jpg](https://user-images.githubusercontent.com/102592414/205807783-954132bd-4827-483d-a605-e98c275aa3c8.jpg) + + +### 즉 구현의 순서를 정리하면 + +1. 먼저 구조도를 보고 해당 객체에게 오는 인자를 확인하고, 메서드의 인자로 받는다. +2. 참조 해야하는 객체들 또한 메서드의 인자로 받는다. +3. 구현부에서 해당 객체가 응답해주어야 할 것들을 응답 해준다. \ No newline at end of file diff --git "a/book/\352\260\235\354\202\254\354\230\244/1\354\243\274\354\260\250/1\354\243\274\354\260\250.md" "b/book/\352\260\235\354\202\254\354\230\244/1\354\243\274\354\260\250/1\354\243\274\354\260\250.md" new file mode 100644 index 0000000..22f3b82 --- /dev/null +++ "b/book/\352\260\235\354\202\254\354\230\244/1\354\243\274\354\260\250/1\354\243\274\354\260\250.md" @@ -0,0 +1 @@ +# 1주차 diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/0.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/0.jpg" new file mode 100644 index 0000000..7a93fff Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/0.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/1.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/1.jpg" new file mode 100644 index 0000000..ecbdd0c Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 d76f2aa821434cccb673497b1771025c/1.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 .md" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 .md" new file mode 100644 index 0000000..0e45904 --- /dev/null +++ "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/1\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_1\354\236\245 \352\260\235\354\262\264, \354\204\244\352\263\204 .md" @@ -0,0 +1,421 @@ +# 1장 : 객체, 설계 + +# ✅이 책의 목표 + +- 코드를 개발하는 사람들이 객체지향 패러다임이라는 용어를 사용할 때 완벽하게는 동일하지 않더라도 어느 정도 유사한 그림을 머릿속에 그릴 수 있는 기반을 제공하기 위해서 +- 객체지향에 대한 다양한 오해를 제거함으로써 객체지향 프로그래밍을 하는 개발자들이 동일한 규칙과 표준에 따라 프로그램을 작성할 수 있게 할 것이다. + +개발자는 코드를 통해 패러다임을 이해하고 적용할 수 있는 기술을 습득해야 한다. + +현실의 패러다임은 천동설과 지동설처럼 두 패러다임이 공존하는게 말이 안되고, 과거의 패러다임을 폐기시키는 혁명적인 과정을 거치지만, 프로그래밍 패러다임은 과거의 패러다임의 단점을 보완하고 발전적인 과정을 거친다. + +- 이런 사실은 객체지향 패러다임을 배우는 것이 다른 패러다임을 배우는 것이 도움이 된다는 것이다. +- 은총알은 없다.(=완벽한 것은 없다.) → 객체지향을 배운다고 해서 모든 프로그래밍을 깨우친게 아니다. + +### 이론이 먼저인가 실무가 먼저인가? + +*부제 : 이론이 먼저인가 코딩이 먼저인가?* + +- 대부분의 사람들은 이론이 먼저 정립된 후에 실무가 그 뒤를 따라 발전한다고 생각하지만, 글래스는 어떤 분야를 막론하고, 이론을 정립할 수 없는 초기에 실무가 먼저 급속한 발전을 이룬다고 한다. +- 소프트웨어 설계와 유지보수에 중점을 두려면 이론이 아닌 실무에 초점을 맞추는 것이 효과적이다. + - 이를 쉽게 말하면 설계나 유지보수에 관해 설명할 때 가장 유용한 도구는 이론으로 덕지덕지 치장된 개념과 용어가 아니라 ‘코드’ 그 자체이다. + - 이론을 아는 개발자가 아닌 그 이론을 코드로 나타낼 수 있는 개발자가 되자! + - 개발자는 구체적인 코드를 만지며 손을 더럽힐 때 가장 많은 것을 얻어가는 존재다. + - 이 책에서는 위의 이유들을 통해서 이 책은 코드 중심으로 객체지향을 설명 할 것이라고 설명하고 있다. + +# ✅티켓 판매 애플리케이션 구현하기 + +소극장을 운영하고 있는 사장이 관람객들의 발길이 이어지도록 작은 이벤트를 기획했다. 이벤트의 내용은 추첨을 통해서 선정된 관람객에게 공연을 무료로 관람할 수 있는 초대장을 발송하는 것이다. + +해당 이벤트를 프로그램으로 구현해보자. + +![0.JPG](1%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6,%20%E1%84%89%E1%85%A5%E1%86%AF%E1%84%80%E1%85%A8%20d76f2aa821434cccb673497b1771025c/0.jpg) + +이 때 염두해야할 점 + +- 이벤트에 당첨된 관람객과 그렇지 못한 관람객은 다른 방식으로 입장시켜야 한다. + - 이벤트에 당첨된 관람객은 초대장으로 티켓을 교환한 후 입장할 수 있다. + - 이벤트에 당첨되지 못한 관람객은 티켓을 구매해야만 입장할 수 있다. +- 즉 관람객을 입장시키기 전 이벤트 당첨 여부를 확인해야하고, 당첨자가 아니면 티켓을 판매 시킨 후 입장시켜야 한다. + +```java +public class Invitaion { + private LocalDateTime when; +} +``` + +- 초대장을 구현한 클래스이다. +- 공연을 관람할 수 있는 초대일자(when)를 인스턴스 변수로 포함하고 있다. + +```java +public class Ticket { + private Long fee; + + public Long getFee() { + return fee; + } +} +``` + +- 티켓을 구현한 클래스이다. + +```java +public class Bag { + private Long amount; + private Invitaion invitaion; + private Ticket ticket; + + // 초대장 보유 여부 + public boolean hasInvitaion() { + return invitaion != null; + } + + // 티켓 보유 여부 + public boolean hasTicket() { + return ticket != null; + } + + // 초대장 -> 티켓 + public void setTicket(Ticket ticket) { + this.ticket = ticket; + } + + public void minusAmount(Long amount) { + this.amount -= amount; + } + + public void plusAmount(Long amount) { + this.amount += amount; + } + + public Bag(long amount) { + this(null, amount); // this를 통해 해당 객체 생성자에 바로 접근 + } + + public Bag(Invitaion invitaion, long amount) { + this.invitaion = invitaion; + this.amount = amount; + } +} +``` + +- 관람객이 가지고 올수있는 소지품들(초대장, 현금, 티켓)을 보관할 수 있는 가방 클래스이다. +- 초대장 보유 여부, 티켓 소유 여부, 현금 증가, 감소, 초대장을 티켓으로 변환시켜주는 메서드들이 있다. +- Bag 인스턴스 상태는 현금과 초대장을 함께 가지고 있거나, 현금만 가지고 있는 상태이기에 Bag 객체 생성 시점에 제약을 강제한다. + +```java +public class Audience { + private Bag bag; + + public Audience(Bag bag) { + this.bag = bag; + } + + public Bag getBag() { + return bag; + } +} +``` + +- 관람객을 구현한 클래스이다. +- 관람객은 소지품을 보관하기 위해 가방을 소지할 수 있다. + +```java +public class ticketOffice { + private Long amount; + private List tickets = new ArrayList<>(); + + // 인스턴스 생성시 판매하거나, 교환해줄 Ticket들과 갯수를 받는다. + public ticketOffice(Long amount, Ticket ... tickets) { + this.amount = amount; + this.tickets.addAll(Arrays.asList(tickets)); + } + + public Ticket getTicket() { + return tickets.remove(0); + } + + public void plusAmount(Long amount) { + this.amount += amount; + } +} + +``` + +- 매표소를 구현한 클래스이다. +- 매표소에는 관람객에게 판매할 티켓과 티켓의 판매 금액이 보관 되어있어야 한다. + +```java +public class TicketSeller { + private TicketOffice ticketOffice; + + public TicketSeller(TicketOffice ticketOffice) { + this.ticketOffice = ticketOffice; + } + + public TicketOffice getTicketOffice() { + return ticketOffice; + } +} +``` + +- 판매원을 구현한 클래스이다. +- 판매원은 자신이 일하는 매표소를 알고있어야 한다. + +```java +public class Theather { + private TicketSeller ticketSeller; + + public Theather(TicketSeller ticketSeller) { + this.ticketSeller = ticketSeller; + } + + public void enter(Audience audience) { + // 초대장이 있다면 flow + if (audience.getBag().hasInvitaion()) { + Ticket ticket = ticketSeller.getTicketOffice().getTicket(); + audience.getBag().setTicket(ticket); + } + // 초대장이 없다면 flow + else { + Ticket ticket = ticketSeller.getTicketOffice().getTicket(); + audience.getBag().minusAmount(ticket.getFee()); + audience.getBag().setTicket(ticket); + } + } +} +``` + +- 소극장을 구현한 클래스이다. +- 초대장 유무에 따라서 flow가 나뉘어진다. +- 위 코드들은 정상적으로 동작한다. + - 그러나 당연하게도 직접적인 값(티켓 수, 일자, 요금등)들을 넣어주지 않았기에 동작만 한다. + +# ✅위 코드들은 과연 무엇이 문제인가? + +- 로버트 마틴은 SW 모듈이 가져야 하는 세 가지 기능에 관해 설명했다. + 1. 실행 중에 제대로 동작하는 것 + 2. 변경을 위해 존재하는 것 → 간단한 작업만으로도 변경이 가능해야한다. + 3. 코드를 읽는 사람과 의사소통하는 것 → 쉽게 읽고 이해할 수 있어야한다. + +- 위 코드는 1번은 충족했지만, 2, 3번은 충족하지 못했다. + +### 예상을 빗나가는 코드 + +- 일반적으로는 관람객이 스스로 초대장을 꺼내는 것이 맞는 것이다. 그러나 현재는 관람객은 소극장의 통제를 받는 수동적인 존재이다. + - Audience 객체에서 초대장을 꺼내는 것이 아니라, Theater 객체에서 Audience 객체가 초대장이 있는지 확인한다. +- 일반적으로 판매원이 티켓을 판매하고, 돈을 관리해야 하나 현재는 소극장이 모든 티켓을 판매하고, 돈을 관리한다. + - TicketSeller 객체에서 티켓을 판매하고, 돈을 관리하는 것이 아니라, Theather 객체에서 해당 책임들을 소화한다. + +즉 각 책임들이 올바른 객체들에 할당되지 못했다. + +- 이해 가능한 코드란 그 동작이 우리의 예상에서 크게 벗어나지 않는 코드이다. + - 우리의 예상은 관람객이 직접 자신의 가방에서 초대장을 꺼내 판매원에게 건내고, 티켓을 구매하는 관람객은 가방 안에서 돈을 직접 꺼내서 판매원에게 지불하는 것이다. 판매원은 매표소에 있는 티켓을 직접 꺼내 관람객에게 건네고 관람객에게서 직접 돈을 받아 매표소에 보관해야한다. + +- 코드를 이해하기 어려운 또 다른 이유로는 한 메서드의 코드를 이해하기 위해서는 여러 세부적인 내용들을 한꺼번에 기억하고 있어야 한다는 점이다. + - Theater 객체의 enter 메서드를 이해하기 위해서는 Audience가 Bag을 가지고 있고, Bag 안에는 현금과 티켓이 들어있는 점들을 알고있어야한다. + +🤓 : 내 PostService의 save() 메서드와 비슷하지 않은가… + +### 변경에 취약한 코드 + +- 너무 많은 객체들이 Bag 객체에 의존하고 있기에, Bag 객체를 변서 +- 객체지향 설계는 서로 의존하면서 협력하는 객체들의 공동체를 구축하는 것이다. + - 우리의 목표는 애플리케이션의 기능을 구현하는 데 필요한 최소한의 의존성만 유지하고 불필요한 의존성을 제거하는 것이다. + +![1.JPG](1%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6,%20%E1%84%89%E1%85%A5%E1%86%AF%E1%84%80%E1%85%A8%20d76f2aa821434cccb673497b1771025c/1.jpg) + +🤓 : 내 PostService도 이런데 ㅋㅋㅋㅋ ㅠㅠㅠㅠ + +# ✅설계 개선하기 + +- 어떻게 개선할 수 있는가?? + - 올바른 책임을 올바른 객체에게 부여하여서, 관람객과 판매원을 **자율적인 존재**로 만들자 + + +### 자율성을 높이자 + +- 설계를 변경하기 어려운 이유는 Theater가 너무 많은 클래스에 의존하고 있기 때문이다. + - 이의 해결 방법은 Theater가 기존에 Bag과 TicketOffice를 접근하는 책임을 Audience와 TicketSeller가 직접 Bag과 TicketOffice를 처리하는 **자율적인 존재**가 되도록 설계를 변경하는 것이다. + - 🤓 : 그러면 PostService의 save() 또한 postMap과 Map을 직접 save 하는 것이 아니라 각 postMap과 Map에게 각자 save를 하는 책임을 부여해야하구나!! + - 🤓 : 그러면 PostMapService와 MapService에서 save() 메서드를 따로 만들었어야 할까? + +- Theater 객체에서 TicketOffice에 접근하던 책임을 TicketSeller에게 책임을 넘겨주자. + +```java +public class Theather { + private TicketSeller ticketSeller; + + public Theather(TicketSeller ticketSeller) { + this.ticketSeller = ticketSeller; + } + + public void enter(Audience audience) { + ticketSeller.sellTo(audience); + } +} +``` + +```java +public class TicketSeller { + private TicketOffice ticketOffice; + + public TicketSeller(TicketOffice ticketOffice) { + this.ticketOffice = ticketOffice; + } + +// public TicketOffice getTicketOffice() { +// return ticketOffice; +// } + + public void sellTo(Audience audience) { + // 초대장이 있다면 flow + if (audience.getBag().hasInvitaion()) { + Ticket ticket = ticketOffice.getTicket(); + audience.getBag().setTicket(ticket); + } + // 초대장이 없다면 flow + else { + Ticket ticket = ticketOffice.getTicket(); + audience.getBag().minusAmount(ticket.getFee()); + ticketOffice.plusAmount(ticket.getFee()); + audience.getBag().setTicket(ticket); + } + } +} +``` + +- Theater에서 더 이상 TicketOffice에서 접근하지 못하도록 `getTicketOffice()` 를 제거해줬다. + - 따라서 TicketSeller는 ticketOffice에 대한 접근은 오직 TicketSeller 안에만 존재하기에, TicketSeller는 ticketOffice에서 티켓을 꺼내거나 판매 요금을 적립하는 일을 스스로 수행할 수 밖에 없다. +- 이렇게 개념적으로나 물리적으로 객체 내부의 세부적인 사항을 감추는 것을 **캡슐화**라고 부른다. + - 🤓 : PostService의 `save()`에서는 너무 많은 사항들이 나와있구나…! 각 엔티티 save 과정, s3upload 과정들을 더 캡슐화 할 필요가 있겠다. → 각 객체들에게 책임을 분담하고, `save()`에서는 인터페이스에만 의존할 수 있도록(=호출하기만 할 수 있도록) + +- Theater는 오직 TicketSeller의 인터페이스에만 의존한다.(=`ticketSeller.sellTo(audience);`) TicketSeller가 내부에 TicketOffice 객체를 포함하고 있다는 사실은 구현의 영역이다. + - 객체를 인터페이스와 구현으로 나누고, 인터페이스만을 공개하는 것은 객체 사이의 결합도를 낮추고 변경하기 쉬운 코드를 작성하기 위해 따라야 하는 가장 기본적인 설계 원칙이다. + - 🤓 : 이 말은 올바른 객체에게 올바른 책임을 주고, 그 객체를 사용하는 객체에서는 호출해서 사용하기만 하면 되도록 하라는 말인 듯 + +- 그러나 아직도 Audience는 Bag에서 스스로 돈을 꺼내서 티켓을 구매하는 것이 아니라 TicketSeller가 대신 돈을 꺼내서 티켓을 구매하고 있다. + +```java +public class TicketSeller { + private TicketOffice ticketOffice; + + public TicketSeller(TicketOffice ticketOffice) { + this.ticketOffice = ticketOffice; + } + +// public TicketOffice getTicketOffice() { +// return ticketOffice; +// } + + public void sellTo(Audience audience) { + ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket())); + } +} +``` + +```java +public class Audience { + private Bag bag; + + public Audience(Bag bag) { + this.bag = bag; + } + +// public Bag getBag() { +// return bag; +// } + + public Long buy(Ticket ticket) { + // 초대장이 있다면 flow + if (bag.hasInvitaion()) { + bag.setTicket(ticket); + return 0L; + } + // 초대장이 없다면 flow + else { + bag.setTicket(ticket); + bag.minusAmount(ticket.getFee()); + return ticket.getFee(); + } + } +} +``` + +### 무엇이 개선됐는가? + +- 우리의 예상과 이제 일치하기에, 코드를 읽는 사람과의 의사소통이라는 관점에서 이 코드는 확실히 개선된 것으로 보인다. +- 또한 Audience나 TicketSeller의 내부 구현을 변경해도 Theater를 함께 변경할 필요가 없어졌다는 것이다. → Theater에서는 Audience나 TicketSeller의 인터페이스만 사용하기에 + +### 어떻게 한 것인가 + +- 객체의 자율성을 높이는 방향으로 설계를 개선했다. + - TicketSeller가 티켓을 판매하기 위해서 TicketOffice를 사용하는 모든 부분을 TicketSeller 내부로 옮기고, 관람객이 스스로 가방에서 돈, 초대장을 꺼내 티켓을 구매하기 위해 Bag을 사용하는 모든 부분을 Audience 내부로 옮겼다. + - 자기 자신의 문제를 스스로 해결하도록 코드를 변경한 것이다. + - 🤓 : PostMap 일은 PostMap이~, Map 일은 Map이~ + - 자기 자신의 일을 다른 누군가가 마음대로 접근해서 대신 해결하지 않도록 해야한다. → **캡슐화** + - 🤓 : PostMap 일을 Post가 해결하지 못하도록 PostMap 내부에 `save()` 메서드를 구현하고 외부에서 PostMap 엔티티 자체에 접근하지 못하게 만들고, Post에서는 `save()` 인터페이스만 이용하도록하자. + +### 캡슐화와 응집도 + +- 위의 말들을 요약하면 다음과 같다. + + + +- 객체의 응집도를 높이기 위해서는 객체 스스로 자신의 데이터를 책임져야 한다. +- 객체 지향 애플리케이션은 스스로 책임을 수행하는 자율적인 객체들의 공동체를 구성함으로써 완성된다. + - 🤓 : 저번에 수민님께서 인터페이스를 사용하여서 `save()` 하라고 말씀하셨던 부분이 이해가 되었다!! 인터페이스를 사용하면 기존과 똑같이 의존하는 것이 아니라 결합도를 낮추고, 변경을 쉽게 할 수 있도록 하는 것이구나!! + +### 절차지향과 객체지향 + +- 변경하기 쉬운 설계는 한 번의 변경에 하나의 클래스만 변경할 수 있는 설계다. + - 🤓 : PostMap 엔티티에 새로운 멤버 변수를 추가해야한다면 나는 PostService의 `save()` 까지 변경해야 했구나 +- 훌륭한 객체지향 설계의 핵심은 캡슐화를 이용해 의존성을 적절히 관리함으로써 객체 사이의 결합도를 낮추는 것이다. + +1. 어떤 기능을 설계하는 방법은 한가지 이상일 수 있다. +2. 동일한 기능을 한 가지 이상의 방법으로 설계할 수 있기 때문에 결국 설계는 트레이드오프의 산물이다. + 1. 🤓 : 이번 프로젝트간 게시글과 사진을 보내는 방법이 여러개였던 것 처럼 + +### 더 개선할 수 있다. + +- Bag은 과거 Audience처럼 스스로 자기 자신을 책임지지 않고, 끌려다니는 수동적인 존재다. + - Bag의 내부 상태에 접근하는 모든 로직을 Bag 안으로 캡슐화해서 결합도를 낮추면 된다. + - 🤓 : 오 리팩토링할 때 해당 엔티티에 접근하는 로직들을 해당 엔티티 안으로 캡슐화 하면 되겠구나!! + +- 그런데 Bag은 현실에서는 자율적인 존재가 아닌데, 스스로 책임을 지게 해도 괜찮은가? + - 현실에서는 수동적인 존재라고 하더라도 객체 지향 세계에서는 모든 것이 능동적이고 자율적인 존재로 바뀐다. → **의인화** + +- 이해하기 쉽고 변경하기 쉬운 코드를 작성하고 싶다면 한 편의 애니메이션을 만든다고 생각하자. 다른 사람의 코드를 읽고 이해하는 동안에는 애니메이션을 보고 있다고 생각하자 그리고 각 객체들을 의인화 시켜보자. 그렇게 하면 안에서 각 캐릭터(=객체)들이 웃고, 떠들고, 화내는 여러 객체를 만나더라도 당황하지 않을 것이다. + - 🤓 : 그렇지 애니메이션에서는 가방이 웃고 떠들수 있는 자율적인 객체가 될 수 있으니! + +# ✅1강 정리 + + + + + + + +- 설계는 코드를 작성하는 것보다 높은 차원의 창조적인 행위가 아니다. +- 설계는 코드를 작성하는 순간 매 순간 코드를 어떻게 배치할 것인지를 결정하는 과정에서 나온다. + + + +- 데이터와 프로세스를 하나의 덩어리로 모으는 것은 훌륭한 객체지향 설계로 가는 첫걸음 → 이는 객체 스스로 자신의 데이터를 책임져야 한다. 는 말과 같다. \ No newline at end of file diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/2.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/2.jpg" new file mode 100644 index 0000000..9a3a3bb Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/2.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/3.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/3.jpg" new file mode 100644 index 0000000..92cdd11 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/3.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/4.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/4.jpg" new file mode 100644 index 0000000..a02a919 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/4.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/5.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/5.jpg" new file mode 100644 index 0000000..7863c67 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/5.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/6.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/6.jpg" new file mode 100644 index 0000000..210a34d Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/6.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/7.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/7.jpg" new file mode 100644 index 0000000..9dda6d4 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 d315814bc8c848eb938acc725d511ff2/7.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" new file mode 100644 index 0000000..632e145 --- /dev/null +++ "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/2\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_2\354\236\245 \352\260\235\354\262\264\354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" @@ -0,0 +1,560 @@ +# 2장 : 객체지향 프로그래밍 + +# ✅영화 예매 시스템 + +### 영화 예매 시스템 요구사항들 + +- 사용자는 영화 예매 시스템을 이용해 쉽고 빠르게 보고 싶은 영화를 예매할 수 있다. + - 영화 → 영화에 대한 기본 정보 + - 상영 → 실제로 관객들이 영화를 관람하는 사건(상영 일자, 시간, 순번) +- 특정한 조건을 만족하는 예매자는 요금을 할인받을 수 있다. 단 영화별로 하나의 할인 정책만 할당할 수 있다. 그러나 할인 조건은 다수의 할인 조건을 함께 지정할 수 있다. + - 할인 조건 → 가격의 할인 여부 결정 + - 순서 조건 : 상영 순번을 이용해 할인 여부 결정 + - 기간 조건 : 영화 시작 시간이 해당 기간 안에 포함될 경우 요금을 할인 + - 할인 정책 → 할인 요금을 결정 + - 금액 할인 정책 : 예매 요금에서 일정 금액을 할인 + - 비율 할인 정책 : 정가에서 일정 비율의 요금을 할인 +- 할인을 적용하기 위해서 할인 조건과 할인 정책을 함께 조합해서 사용한다. + - 먼저 사용자의 예매 정보가 할인 조건 중 하나라도 만족하는지 검사하고, 만족할 경우 할인 정책을 이용해 할인 요금을 계산한다. + - 할인 정책은 적용되었으나 할인 조건을 만족못하면 요금을 할인하지 않는다. + +![2.JPG](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/2.jpg) + +# ✅객체지향 프로그래밍을 향해 + +### 협력, 객체, 클래스 + +- 객체지향은 객체를 지향하는 것이다. + - 혹시 객체지향 프로그램을 작성할 때 가장 먼저 고려하는 것이 어떤 클래스가 필요할지 고민하지는 않는가? 이는 객체지향의 본질과는 거리가 멀다. 객체지향은 말 그대로 객체를 지향하는 것이다. + +1. 어떤 클래스가 필요한지를 고민하기 전에 어떤 객체들이 필요한지 고민하라. + 1. 클래스의 윤곽을 잡기 위해서는 어떤 객체들이 어떤 상태와 행동을 가지는지 먼저 결정해야 한다. + 2. 🤓 : 어떤 객체들이 필요한지 정확히 어떻게 생각하지?? → 3장 참고 +2. 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야 한다. + + + +### 도메인의 구조를 따르는 프로그램 구조 + +- 🤓 : 어떤 객체들이 필요한지 정확히 어떻게 생각하지??에 대한 답이 여기 나온다. + - 요구사항을 분석하는 단계부터 프로그램을 구현하는 마지막 단계까지 도메인의 개념들을 프로그램의 객체라는 개념과 동일하게 볼 수 있다. 즉 어떤 객체들이 필요한지를 어떤 도메인들이 있는지로 연결하면 된다. + - 요구사항과 프로그램을 객체라는 동일한 관점에서 바라볼 수 있기 때문에 도메인을 구성하는 개념들이 프로그램의 객체와 클래스로 매끄럽게 연결될 수 있다. + + + +- 일반적으로 클래스의 이름은 대응되는 도메인 개념의 이름과 동일하거나 적어도 유사하게 지어야한다. 클래스 사이의 관계 또한 최대한 도메인 개념 사이에 맺어진 관계와 유사하게 만들어서 프로그램의 구조를 이해하고 예상하기 쉽게 만들어야 한다. + +![3.JPG](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/3.jpg) + +![4.JPG](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/4.jpg) + +### 클래스 구현하기 + +- 위에서 도메인 개념들의 구조를 반영하여서 적절한 클래스 구조를 만들었다면 이제 적절한 프로그래밍 언어를 이용해 이 구조를 구현하는 것이다. + +```java +public class Screening { + private Movie movie; + private int sequence; + private LocalDateTime whenScreened; + + public Screening(Movie movie, int sequence, LocalDateTime whenScreened) { + this.movie = movie; + this.sequence = sequence; + this.whenScreened = whenScreened; + } + + public LocalDateTime getStartTime() { + return whenScreened; + } + + public boolean isSequence(int sequence) { + return this.sequence == sequence; + } + + public Money getMovieFee() { + return movie.getFee(); + } +} +``` + +- 사용자들이 예매하는 대상인 ‘상영’을 구현 +- 영화, 순번, 상영 시작 시간을 인스턴스 변수로 포함 +- 중요한 점은 인스턴스 변수의 가시성은 private이고, 메서드의 가시성은 public이라는 것이다. + - 클래스를 구현할 때 가장 중요한 것은 클래스의 경계를 구분 짓는 것이다. + +### 자율적인 객체 + +- 객체에게 원하는 것을 요청하고는 객체가 스스로 최선의 방법을 결정할 수 있을 것이라는 점을 믿고 기다려야 한다. + + + +인터페이스와 구현의 분리 + +- 퍼블릭 인터페이스 → 외부에서 접근 가능한 부분 ex) public +- 구현 → 외부에서 접근 불가능하고, 오직 내부에서만 접근 가능한 부분으로 구현 ex) private, protected + +### 프로그래머의 자유 + +- 프로그래머의 역할을 **클래스 작성자**와 **클라이언트 프로그래머**로 구분하자. + - **클래스 작성자 →** 새로운 데이터 타입을 프로그램에 추가한다. + - 클라이언트 프로그래머에게 필요한 부분만 공개하고, 나머지는 꽁꽁 숨겨야 한다. + - **구현 은닉** : 그래야 외부 접근으로 인한 코드 변경을 걱정하지 않고, 내부 구현을 마음대로 변경할 수있다. + + - **클라이언트 프로그래머** → 필요한 클래스들을 엮어서 애플리케이션을 빠르고 안정적으로 구축하는 것 + + +### 협력하는 객체들의 공동체 + +```java +public Reservation reserve(Customer customer, int audienceCount) { + return new Reservation(customer, this, calculateFee(audienceCount), audienceCount); +} +``` + +- Screening 클래스 내의 예매하는 기능을 구현한 메서드이다. + +```java +public Money calculateFee(int audienceCount) { + return movie.calcurateMovieFee(this).times(audienceCount); +} +``` + +- Screening 클래스 내의 요금을 계산해서 Reservation 객체를 만드는데 사용하는 메서드 + +```java +public class Money { + // final은 값이 바뀌지 않고, static은 메모리 주소가 딱 한번 할당된다. + // 어차피 값 안바뀔꺼, 메모리 주소를 할당하는게 낫지 않겠나? -> 같이 쓰이는 이유 + public static final MoneyZERO= Money.wons(0); + + // BigDecimal은 돈과 소수점을 다룬다면 필수로 선택해야할 자료형 + private final BigDecimal amount; + + public static Money wons(long amount) { + return new Money(BigDecimal.valueOf(amount)); + } + + public static Money wons(double amount) { + return new Money(BigDecimal.valueOf(amount)); + } + + Money(BigDecimal amount) { + this.amount = amount; + } + + public Money plus(Money amount) { + // BigDecimal 타입의 사칙 연산은 메서드를 이용해야 한다. + // 입력으로 받은 돈을 현재 돈에 더해준다. + return new Money(this.amount.add(amount.amount)); + } + + public Money minus(Money amount) { + return new Money(this.amount.subtract(amount.amount)); + } + + public Money times(double percent) { + return new Money(this.amount.multiply( + BigDecimal.valueOf(percent))); + } + + public boolean isLessThan(Money other) { + return amount.compareTo(other.amount) < 0; + } + + public boolean isGreaterThanOrEqual(Money other) { + return amount.compareTo(other.amount) >= 0; + } +} +``` + +- 금액과 관련된 다양한 계산을 구현하는 간단한 클래스이다. + +- 1장에서는 금액을 구현하기 위해 Long 타입을 사용했지만, 위 코드에서는 Money 객체 타입을 사용한 이유 + - Long 타입은 Money 타입처럼 저장하는 값이 금액과 관련돼 있다는 의미를 전달할 수 없고,금액과 관련된 로직이 서로 다른 곳에 중복되어 구현되는 것을 막을 수 없다. + - 🤓 : 프로젝트간 Date 클래스를 만들어서 Date 타입을 만들지 않았다면 모든 엔티티에 Date 로직을 넣어줬어야 할 거야 ㅎㄷㄷ… + + + +```java +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; + } +} +``` + +- 영화를 예매하기 위한 클래스이다 +- 고객, 상영정보, 예매 요금, 인원수가 속성으로 포함된다. + +![5.JPG](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/5.jpg) + +- 위의 흐름은 다음과 같다. + 1. Screening `reserve()`에서 인자로 Customer, count를 받는다. + 2. `reserve()`에서 Reservation 객체를 생성해주기 위해, Screening 내의 요금을 계산하는 `calcurateFee()`를 호출한다. `calcurateFee()` 는 Money의 `calculateMovieFee()` 를 호출한다. + 3. Money의 `calculateMovieFee()` 는 1인당 예매 요금을 반환해준다. + 4. `calcurateFee()` 는 `calculateMovieFee()` 로부터 반환받은 1인당 예매 요금에 인원수를 곱해서 최종 요금을 Reservation 생성자에 넘겨준다. + +- 영화를 예매하기 위해서 Screening, Moview, Reservation 인스턴스들은 서로의 메서드를 호출하며 상호작용한다. + - 이처럼 시스템의 어떤 기능을 구현하기 위해 객체들 사이에 이뤄지는 상호작용을 **협력**이라고 부른다. + - 🤓 : 아니 그러면 의존성이 생기는거 아니에요?? + - 아니지. 캡슐화를 통해서 인터페이스로 사용을 하잖아 + +- 객체지향 프로그램을 작성할 때는 먼저 협력의 관점에서 어떤 객체가 필요한지를 결정하고, 객체들의 공통 상태와 행위를 구현하기 위해서 클래스를 작성해야한다. + - 🤓 : 협력의 관점에서 어떻게 생각할 수 있을까?? + - 도메인간에 어떻게 상호작용하는지를 보면 알 수 있지 않을까? + +### 협력에 관한 짧은 이야기 + +- 메시지와 메서드를 구분하는 것은 매우 중요하다. 이 개념으로부터 다형성이 출발하기 때문이다. + - 위에서 Reservation의 `calcurateFee()` 는 Money의 `calculateMovieFee()` 를 호출한다고 하였지만, 사실은 Reservation의 `calcurateFee()` 는 Money가 `calculateMovieFee` 이라는 메시지에 응답할 수 있다고 믿고, 메시지를 전달한 것이다. + +# ✅할인 요금 구하기 + +### 할인 요금 계산을 위한 협력 시작하기 + +![6.JPG](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/6.jpg) + +```java +public class Movie { + private String title; + private Duration runnringTime; + private Money fee; + private DiscountPolicy discountPolicy; + + public Movie(String title, Duration runnringTime, Money fee, DiscountPolicy discountPolicy) { + this.title = title; + this.runnringTime = runnringTime; + this.fee = fee; + this.discountPolicy = discountPolicy; + } + + public Money getFee() { + return fee; + } + + public Money calculateMoviewFee(Screening screening) { + return fee.minus(discountPolicy.calculateDiscountAmount(screening)); + } +} +``` + +- Movie를 구현한 클래스이다. +- 제목, 상영 시간, 영화 비용, 할인 정책등을 멤버 변수로 가지고있다. +- `calculateMoviewFee(**)`** 메서드는 영화의 비용을 할인 비용을 적용해서 반환한다. + +- 만약 위 코드를 보고, 아니 할인 정책의 종류를 어떻게 알아요?라고 한다며 아직 객체지향 패러다임에 익숙하지 않은것이다. + - 위 코드에는 객체지향에서 중요하다고 여겨지는 **상속(inheriteance)**과 **다형성**이 숨어있다. + +```java +public abstract class DiscountPolicy { + private List conditons = new ArrayList<>(); + + public DiscountPolicy(DistcountConditon ... conditons) { + this.conditons = Arrays.asList(conditons); + } + + public Money calculateDiscountAmount(Screening screening) { + for(DistcountConditon each : conditons) { + if (each.isSatisfiedBy(screening)) { + return getDiscountAmount(screening); + } + } + return Money.ZERO; + } + + abstract protected Money getDiscountAmount(Screening Screening); +} +``` + +- 위코드는 **할인정책**을 구현한 클래스이고, 이 추상 클래스를 AmountDiscountPolicy와 PercentDiscountPolicy가 상속받을 것이다. +- 멤버 변수 conditions는 여러개의 할인 조건을 가지고 있다. +- `calculateDiscountAmount()` 는 전체 할인조건에 따라 차례대로 DiscountCondition의 `isSatisfiedBy()` 메서드를 호출한다. + - `isSatisfiedBy()` 는 인자로 받은 `Screening`이 조건을 만족하면 true, 만족 못하면 false를 반환한다. + - 할인 조건이 하나라도 존재하면 추상 메서드인 `getDisCountAmount()` 메서드를 호출한다. + - `getDisCountAmount()` 메서드는 할인 요금을 계산하는 메서드이다. + - 만족하는 할인 조건이 하나도 없으면 0원을 반환한다. + +- DiscountPolicy는 할인 여부와 요금 계산에 필요한 전체적인 흐름은 정의하지만 실제로 요금을 계산하는 부분은 추상 메서드인 `calculateDiscountAmount()` 에게 위임하고, DiscountPolicy를 상속받는 자식 클래스들이 오버라이딩하여서 구현할 것이다. + + + +- 🤓 : 아 부모 PostService 클래스에서 기본 CRUD를 구현하고, 자식 클래스에서 더 자세한 로직을 구현할 때 사용할 수 있겠구나 + +```java +public class AmountDiscountPolicy extends DiscountPolicy { + private Money discountAmount; + + public AmountDiscountPolicy(Money discountAmount, DiscountConditon... conditons) { + super(conditons); + this.discountAmount = discountAmount; + } + + @Override + protected Money getDiscountAmount(Screening screening) { + return discountAmount; + } +} +``` + +- 위 DiscountPolicy를 상속받은 자식 클래스이다. +- `super(conditons)` 는 부모 클래스인 DiscountPolicy의 기본 생성자에 접근하여서 인자들을 넘겨준다. + +```java +public class PercentDiscountPolicy extends DiscountPolicy { + private double percent; + + public PercentDiscountPolicy(double percent, DiscountConditon... conditons) { + super(conditons); + this.percent = percent; + } + + @Override + protected Money getDiscountAmount(Screening screening) { + // times()는 bigDecimal 타입의 데이터를 곱하는 함수 + return screening.getMovieFee().times(percent); + } +} +``` + +- 위 DiscountPolicy를 상속받은 자식 클래스이다. +- AmountDiscountPolicy와 다른 점이라면 고정 금액이 아닌 일정 비율을 차감한다는 것이다. + +```java +public interface DiscountConditon { + boolean isSatisfiedBy(Screening screening); +} +``` + +- **할인 조건**의 인터페이스이다. +- 위 인터페이스들을 이용해서 각 할인 조건들을 구현체로 구현할 것이다. + +```java +public class SequenceCondition implements DiscountConditon{ + private int sequence; + + public SequenceCondition(int sequence) { + this.sequence = sequence; + } + + @Override + public boolean isSatisfiedBy(Screening screening) { + return screening.isSequence(sequence); + } +} +``` + +- 순번 조건의 할인 여부를 판단하기 위한 구현체 +- 만약 상영의 순번과 일치할 경우 할인이 가능하다 판단한다. + +```java +public class PeriodConditon implements DiscountConditon { + private DayOfWeek dayOfWeek; + private LocalTime startTime; + private LocalTime endTime; + + @Override + public boolean isSatisfiedBy(Screening screening) { + return screening.getStartTime().getDayOfWeek().equals(dayOfWeek) && + startTime.compareTo(screening.getStartTime().toLocalTime()) <= 0 && + endTime.compareTo(screening.getStartTime().toLocalTime()) >= 0; + } +} +``` + +- 기간 조건을 판단하는 구현체 +- 인자로 전달된 Screening의 상영 요일이 dayOfWeek과 같고, 상영 시작 시간이 startTime과 endTime 사이에 있을 경우 true를 반환 + +- 하나의 영화에 대해 단 하나의 할인 정책만 설정할 수 있지만, 할인 조건의 경우에는 여러개를 적용할 수 있다는 것을 기억하자. + - Movie 생성자의 파라미터 목록을 이용해 초기화에 필요한 정보를 전달하도록 강제하면, 올바른 상태를 가진 객체의 생성을 보장할 수 있다. + +```java +public Movie(String title, Duration runnringTime, Money fee, DiscountPolicy discountPolicy) { +... } +``` + +# ✅상속과 다형성 + +- Movie 클래스 어디에서도 할인 정책이 금액 할인 정책인지, 비율 할인 정책인지를 판단하지 않는다. 그러면 어떻게 정책을 선택할 수 있을까?? + - 상속과 다형성을 이용해 특정한 조건을 선택적을 실행하는 방법을 알아보자. + +```java +public Movie(String title, Duration runnringTime, Money fee, DiscountPolicy discountPolicy) { +... } +``` + +- Movie의 생성자는 DiscountPolicy 타입의 객체를 인자로 받는다. + - AmountDiscountPolicy나 PercentDiscountPolicy는 DiscountPolicy를 상속받기 때문에 Movie 객체를 생성할 때 AmountDiscountPolicy나 PercentDiscountPolicy의 인스턴스를 대신 전달함으로써 할인 정책을 선택할 수 있다. + - 금액 할인 정책을 사용하고 싶으면 AmountDiscountPolicy 인스턴스를 전달하고, 비율 할인 정책을 적용하고 싶다면 PercentDiscountPolicy 인스턴스를 전달해야한다. + + ![**위 사진을 통해 알 수 있듯이 자식 클래스는 부모 클래스의 타입을 물려받는다**](2%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%80%E1%85%A2%E1%86%A8%E1%84%8E%E1%85%A6%E1%84%8C%E1%85%B5%E1%84%92%E1%85%A3%E1%86%BC%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%86%E1%85%B5%E1%86%BC%20d315814bc8c848eb938acc725d511ff2/7.jpg) + + **위 사진을 통해 알 수 있듯이 자식 클래스는 부모 클래스의 타입을 물려받는다** + +- 코드의 의존성과 실행 시점(객체를 생성하는 시점)의 의존성이 서로 다를 수 있다는 것이다. + - 즉 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있다는 것이다. + - Movie 클래스와 AmountDiscountPolicy, PercentDiscountPolicy 클래스는 의존성이 없지만, Movie 객체와 AmountDiscountPolicy, PercentDiscountPolicy 객체는 의존성이 있다. + +- 🤓 : 그러면 Movie의 인스턴스가 어떤 객체에 의존하고 있는지를 어떻게 알 수 있나?? + - Movie 클래스에서는 의존하는 대상이 DiscountPolicy라는 것만 알 수 있다. Movie 인스턴스를 생성하는 부분을 찾아서 생성자에 전달되는 객체를 통해서 알 수 있다. + +- 🤓 : 아니 알겠어 알겠어. 근데 객체지향으로 갈 수록 코드를 보고 이해하기가 오히려 너무 어려워지는데?? + - 설계가 유연해질수록 코드를 이해하고, 디버깅하기는 어려워진다. 반대로 유연성을 억제하면 코드를 이해하고, 디버깅하기는 쉬워지지만 재사용성과 확장 가능성은 낮아진다. + + + +### 상속 + +- 🤓 : 아니 그런데 어떻게 Movie와 DiscountPolicy의 의존성이 실행 시점에 AmountDiscountPolicy, PercentDiscountPolicy의 의존성으로 바뀔 수 있는걸까?? + - 답은 상속이다. + - 상속이 가치 있는 이유는 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있기 때문이다. + - 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다. + - 컴파일러는 코드 상에서 부모 클래스가 나오는 모든 장소에서 자식 클래스를 사용하는 것을 허용한다. + - 그렇기에 Movie의 생성자 인자 타입이 DiscountPolicy임에도, AmountDiscountPolicy, PercentDiscountPolicy 인스턴스를 전달할 수 있는 것이다. + - 이처럼 자식 클래스가 부모 클래스를 대신하는 것을 **업캐스팅(upcasting)**이라고 부른다. + +- 부모 클래스를 상속해서, 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 **차이에 의한 프로그래밍**이라고 부른다. + +### 다형성 + +- 메시지와 메서드는 다른 개념이다. + - Movie는 DiscountPolicy의 인스턴스에 calculateDiscountAmount 메시지를 전송한다. + - 이 때 실행되는 메서드는 Movie와 상호작용하기 위해 연결된 객체의 클래스에 따라 달라진다. + - Movie와 협력하는 객체가 AmountDiscountPolicy라면 AmountDiscountPolicy에서 오버라이딩한 메서드가, PercentDiscountPolicy 라면 PercentDiscountPolicy에서 오버라이딩한 메서드가 실행될 것이다. + - Movie는 동일한 메시지를 전송하지만 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다. 이를 **다형성**이라 칭한다. + +- 다형성 : 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력 + - 즉 객체들은 모두 수신한 메시지를 이해할 수 있어야 하기에, 인터페이스가 동일(= 같은 부모로 부터 상속)하다는 것이다. + - ex) AmountDiscountPolicy와 PercentDiscountPolicy 가 동일한 부모를 상속받아서 같은 인터페이스를 가지게 되고, 이로 인해서 같은 메시지를 이해할 수 있게 된 것 + - 상속은 인터페이스를 통일 시켜서 다형성을 구현하는 방법중 하나이다. + - 객체지향이 컴파일 시점의 의존성과 실행 시점의 의존성을 분리하고, 하나의 메시지를 선택적을 서로 다른 메서드에 연결할 수 있는 이유가 **지연 바인딩**이라는 메커니즘을 사용한 덕분 + +- 🤓 : 그러면 인터페이스와 추상 클래스를 다르게 사용하는 목적이 뭐야??? + - 추상 클래스 → 자식 클래스들이 인터페이스와 내부 구현을 함께 상속 받을 수 있게 할 때 + - 인터페이스 → 구현은 공유할 필요가 없고, 인터페이스만 공유하고 싶을 때 + - 공용 인터페이스를 자극해서 책임을 수행하게 하는 것은 객체에게 전송되는 메시지다. + - 메시지를 따르면 최소의 인터페이스를 얻을 수 있다. + - 쉽게 말하면 인터페이스란 **메서드 선언부**이다. + +# ✅추상화와 유연성 + +- DiscountPolicy는 AmountDiscountPolicy와 PercentDiscountPolicy보다 더 추상적이다. + - 그 이유는 인터페이스에 초점을 맞추기 때문이다. → DiscountPolicy는 모든 할인 정책들이 수신할 수 있도록 메시지를 정의하기 때문이다. + +- 이렇게 추상화를 사용했을 때(=다형성을 구현했을 때)의 장점은 아래와 같다., + 1. 추상화 계층(부모 클래스 or 인터페이스)만 따로 떼어 넣고 살펴보면 **요구사항의 정책을 높은 수준**에서 서술할 수 있다. + 1. ex) 아이패드로 글 쓰기가 아닌 글 쓰기가 더 상위 정책이라고 할 수 있다. + 2. 상위 정책을 기술한다는 것은 기본적인 애플리케이션의 협력 흐름을 기술한다는 것이다. + 3. 재사용 가능한 설계의 기본을 이루는 디자인 패턴이나 프레임워크 모두 추상화를 이용해서 상위 정책을 정의하는 객체지향의 매커니즘을 활용하고있다. + 2. 설계가 좀 더 **유연해진다.** + 1. 기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고, 확장할 수 있다. + 2. ex) 현재 할인 정책이 없는 경우에는 할인 금액이 0원이라는 사실을 결정하는 책임이 Movie에 책임이 있다. 이를 DiscountPolicy에 책임을 주기 위해서는 0원이라는 할인 요금을 계산할 책임을 NoneDiscountPolicy 클래스를 만들어 부여 하면 된다. 이로써 기존 코드를 수정 하지 않고, 애플리케이션의 기능을 확장할 수 있다. → 유연하고 확장 가능한 설계 + + ```java + public Movie(String title, Duration runnringTime, Money fee, DiscountPolicy discountPolicy + ``` + + + + + + +- 🤓 : 왜 DiscountPolicy에서 할인 조건이 없을 경우에는 getDiscountAmount()를 호출하지 않는다고 한걸까? + + ```java + public Money calculateDiscountAmount(Screening screening) { + for(DiscountConditon each : conditons) { + if (each.isSatisfiedBy(screening)) { + return getDiscountAmount(screening); + } + } + return Money.ZERO; + } + ``` + + - 조건이 없을 경우 return으로 바로 빠져오기에 + +### 상속과 합성 + +- 상속의 가장 큰 문제점은 캡슐화를 위반한다는 것이다. + - 상속을 이용하기 위해서는 자식이 부모 클래스의 내부 구조를 잘 알고 있어야 하기 때문이다. + - 캡슐화의 약화는 자식 클래스가 부모 클래스에 강하게 결합되어서 부모가 변경되면 자식도 변경될 확률을 높인다. +- 두 번째 단점은 설계가 유연하지 않다는 것이다. + - 상속은 실행 시점에 객체의 종류를 변경하는 것이 불가능하다. + - 상속을 사용하면 기존 생성된 객체의 클래스를 변경할 수 없기에, 새로운 객체를 생성하고, 기존 객체의 상태를 새로운 상태에 복사 붙여 넣기 할 수 밖에 없다. + +- 그러나 인스턴스 변수로 연결한 기존 방법을 사용하면 실행 시점에 할인 정책을 간단하게 바꿀 수 있다 + +```java +public void changeDiscountPolicy(DiscountPolicy discountPolicy) { + this.discountPolicy = discountPolicy; + } +``` + + + +- 합성은 위 상속이 가진 두 가지 문제점을 모두 해결한다. + 1. 인터페이스에 정의된 메시지를 통해서만 재사용이 가능하기에 구현을 효과적으로 캡슐화 할 수 있다. + 2. 의존하는 인스턴스를 교체하는 것이 비교적 쉽기에 설계를 유연하게 만든다. + +- 🤓 : 코드를 재사용하고 싶다면, 추상 클래스를 상속하는 것보다 합성, 즉 객체를 자신의 인스턴스 변수로 가져와서 사용해라. 그게 아니라 다형성을 위한 것이면 인터페이스를 사용하라! + +[레퍼런스](https://www.notion.so/2d146f71c3b842a592d3476a1199821b) \ No newline at end of file diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/%EC%BA%A1%EC%B2%98.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/%EC%BA%A1%EC%B2%98.jpg" new file mode 100644 index 0000000..54a8b1c Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/%EC%BA%A1%EC%B2%98.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/10.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/10.jpg" new file mode 100644 index 0000000..dc818de Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/10.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/11.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/11.jpg" new file mode 100644 index 0000000..bed8904 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/11.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/12.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/12.jpg" new file mode 100644 index 0000000..3b5b50b Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/12.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/13.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/13.jpg" new file mode 100644 index 0000000..ebcf33f Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/13.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/8.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/8.jpg" new file mode 100644 index 0000000..f59f4f0 Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/8.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/9.jpg" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/9.jpg" new file mode 100644 index 0000000..79671ad Binary files /dev/null and "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245 ac70c50055b8409cbc305c2bd4b837ce/9.jpg" differ diff --git "a/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245.md" "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245.md" new file mode 100644 index 0000000..afb5ced --- /dev/null +++ "b/book/\354\230\244\353\270\214\354\240\235\355\212\270/3\354\236\245/\354\235\264\354\212\271\352\261\264_\354\230\244\353\270\214\354\240\235\355\212\270_3\354\236\245 \354\227\255\355\225\240, \354\261\205\354\236\204, \355\230\221\353\240\245.md" @@ -0,0 +1,191 @@ +# 3장 : 역할, 책임, 협력 + +> “객체지향에서 가장 중요한 것은 역할, 책임, 협력이다. 역할, 책임, 협력이 제자리를 찾지 못한 상태라면 응집도 높은 클래스와 중복 없는 상속 계층을 구현하더라도 의미없다.” +> + +- **협력 →** 객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용 +- **책임** → 객체가 협력에 참여하기 위해 수행하는 로직 +- **역할** → 객체가 협력안에서 수행하는 책임들이 모여 객체가 수행하는 것 + +# ✅협력 + +```java +public class Screening { + + public Money calculateFee(int audienceCount) { + return movie.calculateMovieFee(this).times(audienceCount); + } +``` + +- 위 코드에서처럼 **협력**은 하나의 객체(Screen)가 다른 객체(Movie)에게 도움을 요청할 때 시작된다. + - 객체는 다른 객체의 상세한 내부 구현에 직접 접근할 수 없기에(private으로 캡슐화를 했기에) 오직 메시지 전송을 통해서만 자신의 요청을 전달할 수 있다. + +```java +public class Movie { + public Money calculateMovieFee(Screening screening) { + return fee.minus(discountPolicy.calculateDiscountAmount(screening)); + } +} +``` + +- 메시지를 수신한 객체(Movie)는 메서드(`calculateMovieFee`)를 실행해 요청에 응답한다. + - 메시지를 수신한 객체는 메시지를 처리할 방법(로직)을 스스로 선택한다는 점이 중요하다. 외부 객체는 오직 메시지만 전송할 수 있다. → 이는 객체가 자율적인 존재라는 것을 뜻한다. + - 메시지를 수신한 객체(Movie)는 메시지를 처리하던 중에 직접 처리할 수 없는 정보나 행동이 필요한 경우 또 다른 객체(discountPolicy)에게 위처럼 도움을 요청할 수 있다. +- Screening이 Movie에게 처리를 위임하는 이유는 요금을 계산하는데 필요한 기본 요금과 할인 정책을 가장 잘 알고 있는 객체가 Movie이기 때문이다. + +- 객체 사이의 협력을 설계할 때는 객체를 서로 분리된 인스턴스가 아닌 협력하는 파트너로 인식해야 한다. → 즉 Movie와 Screeening은 영화를 예매하는 협력에 참여한다. + +- 🤓 : 캡슐화시 객체가 가질 수 있는 상태와 행동은 어떤 기준으로 결정해야하나?? 객체를 설계할 때 어떤 행동과 상태를 할당했다면 이유는 무엇인가? + - 객체의 행동을 결정하는 것은 객체가 참여하고 있는 **협력**이다. + - 협력은 객체가 필요한 이유와 객체가 수행하는 행동의 동기를 제공한다. + +```java +public class Movie { + private Money fee; + private DiscountPolicy discountPolicy; + + public Money calculateMovieFee(Screening screening) { + return fee.minus(discountPolicy.calculateDiscountAmount(screening)); + } +} +``` + +- **상태(변수)**는 객체가 행동하는 데 필요한 정보에 의해 결정되고, **행동(메서드)**은 협력 안에서 객체가 처리할 메시지로 결정한다. +- Movie는 영화를 예매하기 위한 협력에 참여 하고있다. +- Movie가 기본 요금인 fee와 할인 정책인 discountPolicy라는 인스턴스 변수를 상태의 일부로 포함하는 이유는 요금 계산이라는 행동을 수행하는데 해당 상태가 필요하기 때문이다. + + + +# ✅책임 + +- 협력이 갖춰진 다음 할 행동은, 협력에 필요한 행동을 수행할 수 있는 적절한 객체를 찾는 것이다. 이 때 객체가 수행하는 행동을 **책임**이라고 부른다. + +- 객체의 책임은 ‘하는 것’과 ‘아는 것’의 두가지 범주로 나누어 세분화된다. + + ![캡처.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/%25EC%25BA%25A1%25EC%25B2%2598.jpg) + + +- 하는 것 + - Screening의 책임은 영화를 예매하는 것 + - Movie의 책임은 요금을 계산하는 것 +- 아는 것 + - Screening은 `영화를 예매하기 위해` 자신이 상영할 영화를 알고 있어야 한다. + - Movie는 `요금을 계산하기 위해` 가격과 어떤 할인 정책이 적용됐는지도 알고 있어야 한다. + +- 🤓 : 객체의 책임은 하나라는 SRP는, 하는 것과 아는 것을 한 세트로 치는 것이다. + +- 위 예시를 보면 알 수 있듯이 객체는 자신이 맡은 책임을 수행하는 데 필요한 정보를 알고 있을 책임이 있다. +- 협력 안에서 객체에게 할당한 책임이 외부의 인터페이스(메서드)와 내부의 속성(변수)을 결정한다. + + + +![객체지향 설계간에 적용해보는건 어떨까???](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/8.jpg) + +객체지향 설계간에 적용해보는건 어떨까??? + +- 자율적인 객체를 만드는 가장 기본적인 방법은 책임을 수행하는 데 필요한 정보를 가장 잘 알고있는 전문가에게 그 책임을 할당하는 것이다. → **Information expert 패턴** + - 일상생활에서 문제가 생기면 누구에게 도움을 청하나?? 가장 전문가에게 청하지 않는가?? 이와 똑같다. + +- **책임 주도 설계** → 위 같이 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식 + 1. 시스템이 사용자에게 제공하는 기능을 시스템이 담당할 하나의 책임으로 바라본다. 이게 **협력**이다. + 1. ex) 시스템은 사용자에게 영화를 예매할 수 있게 해야해 + 2. 시스템 책임을 더 작은 책임으로 분할한다. + 3. 분할 된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다. + 1. 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요하면 이를 책임질 적절한 객체 또는 역할을 찾는다. + 4. 2~3번을 반복한다. + +- ex) 영화 예매 시스템을 예로 정보 전문가에게 책임을 할당하는 방법 +1. 시스템은 사용자에게 영화를 예매하는 기능을 제공해야 해 → “예매해라” 라는 시스템의 책임으로 할당 → 책임을 할당한다는 것은 메시지의 이름을 결정하는 것과 같다. → “예매해라”라는 협력 + + ![9.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/9.jpg) + +2. “예매해라”라는 협력을 처리할 수 있는 객체를 선택해야 한다. → 예매에 관련 된 정보를 가장 많이 알고 있는 객체(정보 전문가)인 Screening에게 할당한다. + + ![10.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/10.jpg) + +3. 예매하기 위해서는 가격을 계산 해야 하는데 Screening 힘으로는 못하기에, 외부 객체에게 가격 계산 요청 → “가격을 계산해라”라는 협력을 처리할 수 있는 객체를 선택 + + ![11.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/11.jpg) + +4. 2~3번을 반복한다. + +- 책임을 할당할 때 고려해야 할 두 가지 요소 + 1. 메시지가 객체를 결정한다. + 2. 행동이 상태를 결정한다. + +### 메시지가 객체를 결정한다. + +- 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 하는 두 가지 이유 + - ex) “예매해라”라는 메시지에 가장 알맞은 정보 전문가 객체를 선택 + 1. 객체가 최소한의 인터페이스를 가질 수 있게 된다. + 1. 메시지를 수신하기 전까지 객체에 어떤 것도 추가 하지 않기에 + 2. 객체는 충분히 추상적인 인터페이스를 가질 수 있게 된다. + 1. 메시지는 외부의 객체가 요청하는 무언가를 의미하기 때문에 메시지를 먼저 식별하면 무엇을 수행할지에 초점을 맞추는 인터페이스를 얻을 수 있다. + +### 행동이 상태를 결정한다. + +- 객체가 존재하는 이유는 협력에 참여하기 위해서이고, 객체는 따라서 협력에 필요한 행동을 제공해야 한다. + + + +- 객체지향 초보자들은 먼저 객체에 필요한 상태가 무엇인지를 결정하고, 그 후에 상태에 필요한 행동을 결정한다. + - 이런 방식은 객체의 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기에 캡슐화를 저해한다. + + +# ✅역할 + +- 객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합을 **역할**이라고 부른다. + - 협력을 모델링할 때는 특정한 객체가 아니라 역할에게 책임을 할당한다고 생각하는 것이 좋다. + +- 영화 예매 협력에서 “예매하라”라는 메시지를 처리하기 위한 과정은 사실은 두 독립적인 단계가 합쳐졌다. + 1. 영화를 예매할 수 있는 적절한 역할이 무엇인가를 찾는다. + 2. 역할을 수행할 객체로 Screening 인스턴스를 선택한다. + +- 🤓 : 아니 굳이?? 그냥 **역할** 없이 메시지를 수행할 객체를 찾으면 안돼?? 설계가 한 단계 더 생기잖슴 + - **상속**을 생각하면 왜 역할이 필요한지 알게 될 걸~!! + - 그냥 역할 없이 메시지를 수행할 객체를 찾는다고 가정하자. + - 예를 들어서 “할인 요금을 계산하라”라는 메시지를 Movie 객체가 보냈다고 치자. 그러면 메시지를 해결할 수 있는 객체를 찾을 거야. 이 때 해결할 수 있는 객체는 AmountDiscountPolicy랑 PercentDiscountPolicy 2개잖아. 그러면 두 종류의 객체가 참여하는 협력을 개별적으로 만들어줘야겠네? + + ![12.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/12.jpg) + + - 이렇게 두 협력을 구현하면 AmountDiscountPolicy와 PercentDiscountPolicy의 코드가 대부분 중복하지 않겠니?? + - 그렇기에 해결할 수 있는 객체가 아니라 “할인 요금을 계산하라”라는 메시지를 해결할 책임에 초점을 맞추고 봐야해! + - 그러면 AmountDiscountPolicy와 PercentDiscountPolicy 모두 할인 요금 계산이라는 동일한 책임을 수행하고있다는 것을 알 수 있지. 그러면 생각나는거 있지 않니?? **상속을 통한 추상화** + - 또한 **추상화**는 협력 안에서 두 종류의 객체를 교대로 바꿔 끼울 수 있는 일종의 **슬롯**으로 생각할 수 있지. + - 추상화 == 슬롯 == 역할 + + ![13.JPG](3%E1%84%8C%E1%85%A1%E1%86%BC%20%E1%84%8B%E1%85%A7%E1%86%A8%E1%84%92%E1%85%A1%E1%86%AF,%20%E1%84%8E%E1%85%A2%E1%86%A8%E1%84%8B%E1%85%B5%E1%86%B7,%20%E1%84%92%E1%85%A7%E1%86%B8%E1%84%85%E1%85%A7%E1%86%A8%20ac70c50055b8409cbc305c2bd4b837ce/13.jpg) + + +- 🤓 : ㅇㅋㅇㅋ 알겠어. 그런데 만약 메시지를 해결할 수 있는 객체가 하나밖에 없는데도 굳이 역할이 필요해?? + - 협력에 적합한 책임을 수행하는 대상이 한 종류라면 간단하게 역할이 아니라 객체로 간주하고, 위 같이 여러 객체들이 참여할 수 있다면 역할이라고 불러!! + + + +- 실 설계간에는 아래와 같이 해보자 + - 처음에 특정 시나리오에 대한 협력을 구상한다면 아마도 도메인 모델에 있는 개념들을 후보로 선택해서 직접 책임을 할당할 것이다. + - 이 때 다양한 시나리오를 설계로 옮기면서 협력을 지속적으로 정제하다 보면 두 개 이상의 협력이 거의 유사한 구조(같은 메시지를 응답할 수 있다는 것)를 보인다는 것을 발견할텐데 이 때 역할을 사용해보자! + +- 역할의 가장 큰 장점은 설계의 구성 요소를 추상화할 수 있다는 것이다. + +- 하나의 배역을 여러 배우가 연기할 수 있는 것처럼 동일한 역할을 수행하는 하나 이상의 객체들이 존재할 수 있다. + - 이것은 협력 관점에서 동일한 역할을 수행하는 객체들은 서로 대체 가능하다는 것을 의미한다. → **다형적** +- 또한 객체는 여러 협력에 참여하면서 다양한 역할을 수행할 수 있으나, 협력 안에서는 하나의 역할로 보여진다. + + \ No newline at end of file diff --git a/d b/d new file mode 100644 index 0000000..a37c368 --- /dev/null +++ b/d @@ -0,0 +1,47 @@ +commit 9592907c998f7ce9790af9e1e0080e3fd5f58d46 (HEAD -> master, origin/master, origin/HEAD) +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 21:10:03 2022 +0900 + + chores : 간격 수정 + +commit 0b6d57b14d7057d00fe42bfa73de14e136e080be +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 21:08:37 2022 +0900 + + add : 책 미션 진행 방법 내용 추가 + +commit 25cbb4c0d0aab09550d604b5f9e4c68c6ba51d82 +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 20:59:03 2022 +0900 + + chores : README.md 레포 이름 수정 + +commit cf74f9ed9e47f4aac33c9cb50fbecb2e3ceeaa30 +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 20:55:20 2022 +0900 + + add : 1주차 추가 + +commit 78d5e243925258900e844741ab31ed4f15117056 +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 20:55:07 2022 +0900 + + add : 미션 제출 방법 추가 + +commit 3101420774f49668ab1dca3a01a879d03b08917d +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 20:22:56 2022 +0900 + + add : 숫자 야구 미션 추가 + +commit 1515a15dca160ff740abd300ee0520ee323daffe +Author: JeonginKim <47661695+mywnajsldkf@users.noreply.github.com> +Date: Sat Nov 19 20:16:03 2022 +0900 + + add : .gitignore 추가, README.md 작성 + +commit 46966f567b122b98eae019d698b5a0fb8767cbb7 +Author: mywnajsldkf +Date: Thu Nov 17 09:48:39 2022 +0900 + + first commit