diff --git a/README.md b/README.md
index 3273008..ac7c98c 100644
--- a/README.md
+++ b/README.md
@@ -9,3 +9,4 @@
> 3. 특별한 훈련 없이도 개발자가 쉽게 읽고 이해해야 한다."
> - Robert C Matin -
+# [2. 코드로 이해하는 객체지향 설계](https://github.com/ca1af/object/blob/main/src/chapter_two/chapter_two.md)
diff --git a/object.iml b/object.iml
index 000cc3d..43f9de8 100644
--- a/object.iml
+++ b/object.iml
@@ -7,6 +7,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/chapter_two/Customer.java b/src/chapter_two/Customer.java
new file mode 100644
index 0000000..086de54
--- /dev/null
+++ b/src/chapter_two/Customer.java
@@ -0,0 +1,4 @@
+package chapter_two;
+
+public class Customer {
+}
diff --git a/src/chapter_two/Money.java b/src/chapter_two/Money.java
new file mode 100644
index 0000000..2537f5e
--- /dev/null
+++ b/src/chapter_two/Money.java
@@ -0,0 +1,40 @@
+package chapter_two;
+
+import java.math.BigDecimal;
+
+/**
+ * 1 장에서는 금액을 구현하기 위해 Long 타입을 사용했다.
+ * Money 객체로 바꿈으로써 어떤 것을 얻었을까?
+ */
+// TODO: 12/6/23 바꿈으로써 얻는 이익을 내 언어로 설명하기
+public class Money {
+ public static final Money ZERO = Money.wons(0);
+ private final BigDecimal amount;
+
+ public Money(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public static Money wons(long amount){
+ return new Money(BigDecimal.valueOf(amount));
+ }
+ public Money minus(Money amount) {
+ return new Money(this.amount.subtract(amount.amount));
+ }
+
+ public Money plus(Money amount){
+ return new Money(this.amount.add(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 isGreaterOrEqual(Money other){
+ return amount.compareTo(other.amount) >= 0;
+ }
+}
diff --git a/src/chapter_two/Movie.java b/src/chapter_two/Movie.java
new file mode 100644
index 0000000..34da052
--- /dev/null
+++ b/src/chapter_two/Movie.java
@@ -0,0 +1,30 @@
+package chapter_two;
+
+import chapter_two.discount.policy.DiscountPolicy;
+
+import java.time.Duration;
+
+public class Movie {
+ private String title;
+ private Duration runningTime;
+ private Money fee;
+ private DiscountPolicy discountPolicy;
+
+ public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
+ this.title = title;
+ this.runningTime = runningTime;
+ this.fee = fee;
+ this.discountPolicy = discountPolicy;
+ }
+
+ public Money getFee() {
+ return fee;
+ }
+
+ /**
+ * 이 매서드에서는 어떤 할인정책을 정할 것인지 정하지 않는다.
+ */
+ public Money calculateMovieFee(Screening screening) {
+ return fee.minus(discountPolicy.calculateDiscountPolicy(screening));
+ }
+}
diff --git a/src/chapter_two/Reservation.java b/src/chapter_two/Reservation.java
new file mode 100644
index 0000000..ad5d226
--- /dev/null
+++ b/src/chapter_two/Reservation.java
@@ -0,0 +1,7 @@
+package chapter_two;
+
+public class Reservation {
+ public Reservation(Customer customer, Screening screening, Money money, int audienceCount) {
+
+ }
+}
diff --git a/src/chapter_two/Screening.java b/src/chapter_two/Screening.java
new file mode 100644
index 0000000..250a4b6
--- /dev/null
+++ b/src/chapter_two/Screening.java
@@ -0,0 +1,37 @@
+package chapter_two;
+
+import java.time.LocalDateTime;
+
+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();
+ }
+
+ public Reservation reserve(Customer customer, int audienceCount){
+ return new Reservation(customer, this, calculateFee(audienceCount), audienceCount);
+ }
+
+ private Money calculateFee(int audienceCount) {
+ return movie.calculateMovieFee(this).times(audienceCount);
+ }
+
+
+}
diff --git a/src/chapter_two/chapter_two.md b/src/chapter_two/chapter_two.md
new file mode 100644
index 0000000..6313374
--- /dev/null
+++ b/src/chapter_two/chapter_two.md
@@ -0,0 +1,139 @@
+# 객체 지향 프로그래밍
+
+## 협력, 객체, 클래스
+
+> 객체지향은 객체를 지향하는 것이다.
+
+- 클래스를 먼저 고려하는 것이 아니라, 객체에 초점을 맞추는 것이 옳다. 이 방법은 아래와 같다.
+
+1. 어떤 클래스가 필요한지 고민하기 전에, 어떤 객체들이 필요한지 고민하라.
+- 클래스는 공통적인 상태/행동 을 공유하는 객체들을 __추상화__ 한 것이다.
+- 따라서 __상태와 행동__을 가지는지를 먼저 결정하라.
+ - 이는 설계를 단순하고 깔끔하게 만든다.
+
+2. 둘째, 객체를 협력하는 공동체의 일원으로 보라.
+- 다른 객체에게 도움을 주거나, 의존하는 것이 객체이다.
+ - 객체를 공동체의 일원(협력체)으로 생각하면 유연하며, 확장 용이한 설계가 가능하다.
+
+3. 객체들의 공통 특성과 상태를 종합하여 클래스를 구현해라. 훌륭한 클래스는 훌륭한 객체로부터 시작한다.
+
+
+---
+
+## 도메인의 구조를 따르는 프로그램 구조
+
+### 도메인이란 무엇인가
+
+> "문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야"
+> -p41
+
+- 도메인은 일종의 개념이고, 이 개념을 구현하기 위해 클래스를 사용하는 경우가 대부분이다.
+
+예를 들어 영화 상영을 위해
+
+1. 영화
+2. 상영
+3. 할인 정책
+
+등이 있다고 하면, 각각
+
+1. Movie class
+2. Screening class
+3. Discount Policy class(interface)
+
+로 대응 가능하다.
+
+여기서 중요한 것은 __클래스의 구조__가 __도메인의 구조__와 유사한 형태를 띄어야 한다는 것.
+
+도메인이 -> 클래스로 구현된다는 사실이다.
+
+---
+
+### 자율적인 객체
+
+1. 객체는 상태와 행동을 가진다.
+2. 객체는 스스로 판단하고 행동하는 자율적인 존재이다.
+
+이 두가지 사실은 깊이 연관되어 있다.
+
+- 데이터와 기능ㅇ을 객체 내부로 함께 묶는 것을 __캡슐화__ 라고 한다.
+- 상태와 행동을 캡슐화하는 것에서 한 걸음 더 나아가, 객체지향 언어는 __접근제어__ 메커니즘을 제공한다.
+- 객체 내부에 대한 접근을 제어(통제)함으로써 객체는 더 자율적이 된다. 스스로 상태를 관리하고, 행동하는 객체가 되는 것을 돕는다.
+
+위의 캡슐화와 접근제어는 객체를 두 부분으로 나누는 역할을 한다.
+
+1. 외부에서 접근 가능한 부분, __Public Interface__ : 외부에 공개하는 public method
+2. 오직 내부에서 접근 가능한 부분, __Implementattion(구현)__ : 공개하지 않는 그 모든 부분과, 객체의 속성(필드)
+
+---
+
+### 프로그래머의 자유
+
+프로그래머의 역할을 두 개로 분리하는 것이 유리하다.
+
+1. 클래스 작성자(class creator) - 내부 구현 및 외부 인터페이스를 정의한다
+2. 클라이언트 프로그래머(client programmer) - 정의된 외부 인터페이스를 통해 기능을 작성한다.
+
+객체의 내부와 외부를 분리함으로써
+
+1. 클라이언트는 외부 인터페이스만을 알아도 된다(내부 구현은 신경 쓰지 않아도 된다)
+2. 클래스 작성자는 외부에 미치는 영향을 걱정하지 않아도 된다.
+
+---
+
+### 메시지와 매서드
+
+1. 객체가 다른 객체와 상호작용 할 수 있는 유일한 방법은 메시지를 전송하는 것이다.
+2. 메시지를 수신한 객체는 스스로의 결정에 따라 자율적으로 메싲리르 처리할 방법을 결정한다.
+3. 이 수신된 메시지를 처리하기 위한 방법을 __매서드(method)__ 라고 부른다.
+
+- 메시지와 매서드를 구분함으로써 "다형성" 개념이 출발한다.
+- Screening 이 Movie 에게 "영화 가격을 계산하라" 는 메시지를 보내면 -> Movie 는 스스로의 매서드(calculateMovieFee)로 처리 후 응답한다.
+ - Movie 스스로가 메시지에 대한 응답을 선택 가능하다(이 매서드의 내부 구현은 Movie 에게 책임이 있다.)
+
+---
+
+## 질문 할 것 - 1 Override vs Overload?
+
+## 상속과 다형성
+
+
+
+위 클래스 다이어그램을 보면
+
+1. Movie 는 DiscountPolicy 를 의존한다.
+2. 그러나 실제 계산은 DiscountPolicy 의 하위 구현체인 Amount || Percent 가 필요하다.
+
+> 하지만 코드 수준에서 Movie 클래스는 구현체에 의존하지 않는다.
+
+그렇다면 Movie 인스턴스가 코드 작성시점에는 존재조차 몰랐던 실제 구현체를 실행 시점에 협력 가능한 이유가 무엇일까?
+
+class 의 관점이 아닌 인스턴스의 관점, 즉 코드 상이 아닌 실행 시점상에서 생각해보자 -> movie Test
+
+---
+
+### 코드의 의존성과 실행시점의 의존성은 서로 다를 수 있다.
+
+- Movie 클래스는 DiscountPolicy 라는 추상을 상속하는 어떤 녀석이든, 메세지를 잘 전달받아서 매서드를 수행하는 녀석이라면 신경쓰지 않는다.
+- 따라서 Movie 클래스는 DiscountPolicy 측이 변경되든, 확장되든 같은 메시지를 보내며, 기대하는 응답 또한 받을 수 있을 것이다
+
+그리고 이와 같은 구조는 유연하지만, 대신에 실제 객체(DiscountPolicy 의 구현체) 가 하는 일을 실제로 알기 위해서는 Movie 인스턴스의 생성부를 살펴봐야 한다는 단점이 있다. 즉, 복잡하다.
+
+즉, 설계가 유연해질수록 코드를 이해하고 디버깅하기는 점점 더 어려워진다.
+
+> 따라서 유연성과 가독성 사이에서 항상 고민해야 한다. 완벽한 정답은 없다.
+
+---
+
+### 질문 2 - 상속을 언제 사용하나요?
+
+### + 전략 패턴? 합성?(Composition)
+
+합성의 정의
+
+> __인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법__
+
+
+
+### 추상 클래스인 DiscountPolicy 를 Interface 로 바꾸며, NoneDiscountPolicy 추가해보기
+
diff --git a/src/chapter_two/composition/Heritor.java b/src/chapter_two/composition/Heritor.java
new file mode 100644
index 0000000..556b8c2
--- /dev/null
+++ b/src/chapter_two/composition/Heritor.java
@@ -0,0 +1,11 @@
+package chapter_two.composition;
+
+public class Heritor extends SuperHeir {
+ public Heritor(int foo) {
+ super(foo);
+ }
+
+ public void iGotFooTo(){
+ this.printFoo();
+ }
+}
\ No newline at end of file
diff --git a/src/chapter_two/composition/SuperHeir.java b/src/chapter_two/composition/SuperHeir.java
new file mode 100644
index 0000000..4c46b14
--- /dev/null
+++ b/src/chapter_two/composition/SuperHeir.java
@@ -0,0 +1,12 @@
+package chapter_two.composition;
+
+public class SuperHeir {
+ private final int foo;
+
+ public void printFoo(){
+ System.out.println(foo + "는 내건데?");
+ }
+ public SuperHeir(int foo) {
+ this.foo = foo;
+ }
+}
diff --git a/src/chapter_two/discount/condition/DiscountCondition.java b/src/chapter_two/discount/condition/DiscountCondition.java
new file mode 100644
index 0000000..7aa5ecc
--- /dev/null
+++ b/src/chapter_two/discount/condition/DiscountCondition.java
@@ -0,0 +1,7 @@
+package chapter_two.discount.condition;
+
+import chapter_two.Screening;
+
+public interface DiscountCondition {
+ boolean isSatisfiedBy(Screening screening);
+}
diff --git a/src/chapter_two/discount/condition/PeriodCondition.java b/src/chapter_two/discount/condition/PeriodCondition.java
new file mode 100644
index 0000000..b491f0c
--- /dev/null
+++ b/src/chapter_two/discount/condition/PeriodCondition.java
@@ -0,0 +1,25 @@
+package chapter_two.discount.condition;
+
+import chapter_two.Screening;
+
+import java.time.DayOfWeek;
+import java.time.LocalTime;
+
+public class PeriodCondition implements DiscountCondition{
+ private DayOfWeek dayOfWeek;
+ private LocalTime startTime;
+ private LocalTime endTime;
+
+ public PeriodCondition(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) {
+ this.dayOfWeek = dayOfWeek;
+ this.startTime = startTime;
+ this.endTime = 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;
+ }
+}
diff --git a/src/chapter_two/discount/condition/SequenceCondition.java b/src/chapter_two/discount/condition/SequenceCondition.java
new file mode 100644
index 0000000..a42089d
--- /dev/null
+++ b/src/chapter_two/discount/condition/SequenceCondition.java
@@ -0,0 +1,19 @@
+package chapter_two.discount.condition;
+
+import chapter_two.Screening;
+
+/**
+ * 상영 순서(sequence) 에 따른 할인 조건
+ */
+public class SequenceCondition implements DiscountCondition{
+ private int sequence;
+
+ public SequenceCondition(int sequence) {
+ this.sequence = sequence;
+ }
+
+ @Override
+ public boolean isSatisfiedBy(Screening screening) {
+ return screening.isSequence(sequence);
+ }
+}
diff --git a/src/chapter_two/discount/policy/AmountDiscountPolicy.java b/src/chapter_two/discount/policy/AmountDiscountPolicy.java
new file mode 100644
index 0000000..beeb9ac
--- /dev/null
+++ b/src/chapter_two/discount/policy/AmountDiscountPolicy.java
@@ -0,0 +1,19 @@
+package chapter_two.discount.policy;
+
+import chapter_two.Money;
+import chapter_two.Screening;
+import chapter_two.discount.condition.DiscountCondition;
+
+public class AmountDiscountPolicy extends DiscountPolicy{
+ private Money discountAmount;
+
+ public AmountDiscountPolicy(Money discountAmount, DiscountCondition... conditions) {
+ super(conditions);
+ this.discountAmount = discountAmount;
+ }
+
+ @Override
+ protected Money getDiscountAmount(Screening screening) {
+ return discountAmount;
+ }
+}
diff --git a/src/chapter_two/discount/policy/DiscountPolicy.java b/src/chapter_two/discount/policy/DiscountPolicy.java
new file mode 100644
index 0000000..594b271
--- /dev/null
+++ b/src/chapter_two/discount/policy/DiscountPolicy.java
@@ -0,0 +1,38 @@
+package chapter_two.discount.policy;
+
+import chapter_two.Money;
+import chapter_two.Screening;
+import chapter_two.discount.condition.DiscountCondition;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 할인 정책은 1.정가할인 2.비율할인 이 있다.
+ * 할인 적용 조건(discountPolicy) 는 여러개가 있다.
+ */
+public abstract class DiscountPolicy {
+ private List conditions = new ArrayList<>();
+
+ public DiscountPolicy(DiscountCondition... conditions) {
+ this.conditions = List.of(conditions);
+ }
+
+ /**
+ * 부모 클래스에서 해당 매서드로 기본 동작을 정의하고
+ * 자식 클래스에서는 @Override 할 getDiscountAmount 매서드로 필요한 처리를 완료한다.
+ * 위와 같이 부모 클래스에 기본적인 알고리즘을 구현하고, 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴을
+ * Template Method Pattern 이라고 부른다.
+ */
+ public Money calculateDiscountPolicy(Screening screening){
+ for (DiscountCondition each : conditions) {
+ if (each.isSatisfiedBy(screening)){
+ return getDiscountAmount(screening);
+ }
+ }
+
+ return Money.ZERO;
+ }
+
+ protected abstract Money getDiscountAmount(Screening screening);
+}
diff --git a/src/chapter_two/discount/policy/PercentDiscountPolicy.java b/src/chapter_two/discount/policy/PercentDiscountPolicy.java
new file mode 100644
index 0000000..eec72af
--- /dev/null
+++ b/src/chapter_two/discount/policy/PercentDiscountPolicy.java
@@ -0,0 +1,19 @@
+package chapter_two.discount.policy;
+
+import chapter_two.Money;
+import chapter_two.Screening;
+import chapter_two.discount.condition.DiscountCondition;
+
+public class PercentDiscountPolicy extends DiscountPolicy{
+ private Double percent;
+
+ public PercentDiscountPolicy(Double percent, DiscountCondition... conditions) {
+ super(conditions);
+ this.percent = percent;
+ }
+
+ @Override
+ protected Money getDiscountAmount(Screening screening) {
+ return screening.getMovieFee().times(percent);
+ }
+}
diff --git a/src/chapter_two/images/img.png b/src/chapter_two/images/img.png
new file mode 100644
index 0000000..c6bf010
Binary files /dev/null and b/src/chapter_two/images/img.png differ
diff --git a/src/chapter_two/test/MovieInitTest.java b/src/chapter_two/test/MovieInitTest.java
new file mode 100644
index 0000000..ebaf9d6
--- /dev/null
+++ b/src/chapter_two/test/MovieInitTest.java
@@ -0,0 +1,34 @@
+package chapter_two.test;
+
+import chapter_two.Money;
+import chapter_two.Movie;
+import chapter_two.discount.condition.SequenceCondition;
+import chapter_two.discount.policy.AmountDiscountPolicy;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+class MovieInitTest {
+ /**
+ * Movie 클래스 어디에서도 할인 정책을 결정하는 부분이 없다. 어떻게 영화 요금 계산에 할인정책을 결정 가능할까?
+ */
+ @Test
+ void foo(){
+ new Movie(
+ "화양연화",
+ Duration.ofMinutes(120L),
+ Money.wons(10000L),
+ new AmountDiscountPolicy(
+ Money.wons(1000)
+ ));
+
+ new Movie(
+ "화양연화",
+ Duration.ofMinutes(120L),
+ Money.wons(10000L),
+ new AmountDiscountPolicy(
+ Money.wons(1000),
+ new SequenceCondition(2),
+ new SequenceCondition(10)));
+ }
+}
\ No newline at end of file