From 3bc44033d305560f40b91864cdda59f5e430d9a6 Mon Sep 17 00:00:00 2001 From: mintheon Date: Tue, 7 May 2024 21:58:18 +0900 Subject: [PATCH] =?UTF-8?q?:pencil:=20chapter=208,9=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter08/minhyun/ch8.md | 133 +++++++++++++++++++++++++++++++++++++++ chapter09/minhyun/ch9.md | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 chapter08/minhyun/ch8.md create mode 100644 chapter09/minhyun/ch9.md diff --git a/chapter08/minhyun/ch8.md b/chapter08/minhyun/ch8.md new file mode 100644 index 0000000..aaa1af1 --- /dev/null +++ b/chapter08/minhyun/ch8.md @@ -0,0 +1,133 @@ +작고 응집도 높은 객체 → 책임의 초점이 명확하고 한 가지 일만 잘하는 객체 + +# 01. 의존성 이해하기 + +## 변경과 의존성 + +- **실행시점** + - 의존되는 객체가 정상적으로 동작하기 위해서 실행 시 의존 대상 객체가 반드시 존재해야 함. +- **구현 시점** + - 의존 대상 객체가 변경될 경우 의존 객체도 함께 변경됨. + +객체가 예정된 작업을 정상적으로 수행하기 위해 **다른 객체를 필요로 하는 경우 두 객체 사이에 의존성이 존재한다고 말한다.** + +- 의존성은 방향성을 가지며 항상 단방향이다. +- 의존성은 변경에 의한 영향의 전파 가능성을 암시한다. + +## 의존성 전이 + +- 의존성이 실제로 전이될지 여부는 변경의 방향과 캡슐화의 정도에 따라 달라진다. + - 내부구현을 효과적으로 캡슐화되어 있다면 변경이 전파되지 않을 것. + +- **직접 의존성** + - 한 요소가 다른 요소에 직접 의존하는 경우 +- **간접 의존성** + - 직접적인 관계는 존재하지 않지만 의존성 전이에 의해 영향이 전파되는 경우 + +## 런타임 의존성과 컴파일 타임 의존성 + +- 런타임: 애플리케이션이 실행되는 시점 +- 컴파일 타임: 작성된 코드를 컴파일 하는 시점 or 문맥에 따라선 코드 그 자체 + +***객체지향 애플리케이션에서…*** + +‘런타임’의 주인공은 ‘객체’ + +→ 런타임 의존성이 다루는 주제는 객체 사이의 의존성 + +‘컴파일’의 주인공은 ‘클래스’ + +→ 컴파일타임 의존성이 다루는 주제는 클래스 사이의 의존성 + +**유연하고 재사용 가능한 설계를 창조하기 위해선 동일한 소스코드 구조를 가지고 다양한 실행 구조를 만들 수 있어야 한다.** + +- 어떤 클래스의 인스턴스가 다양한 클래스의 인스턴스와 협력하기 위해선 협력할 인스턴스의 구체적 클래스를 알아선 안된다. → 런타임에 해결해야 함. + +## 컨텍스트 독립성 + +- 유연하고 확장 가능한 설계를 만들기 위해선 컴파일타임 의존성과 런타임 의존성이 달라야한다. +- 구체적인 클래스를 알수록 그 클래스가 사용되는 특정한 문맥에 강하게 결합되기 때문에 협력 객체의 구체적 클래스에 대해 알아선 안된다. + +- 클래스가 사용될 특정한 문맥에 대해 최소한의 가정만으로 이뤄져 있다면 다른 문맥에서 재사용하기가 더 수월해지고 이를 ‘컨텍스트 독립성’이라고 한다. + +## 의존성 해결하기 + +- 컴파일 타임 의존성을 실행 컨텍스트에 맞는 적절한 런타임 의존성으로 교체하는 것을 ‘의존성 해결’ 이라고 부른다. 일반적으로 3가지방법이 있다. + 1. 객체를 생성하는 시점에 생성자를 통해 의존성 해결 + 2. 객체 생성 후 setter 메서드를 통해 의존성 해결 + 3. 메서드 실행 시 인자를 이용해 의존성 해결 + +# 02. 유연한 설계 + +## 의존성과 결합도 + +- 의존성은 ‘재사용성’과 관련이 있다. + - 컨텍스트에 독립적인 의존성은 바람직한 의존성임. + - 특정 컨텍스트에 강하게 결합된 의존성은 바람직하지 않은 의존성임. + +→ 느슨하거나 약한 결합도를 가져야 한다. + +## 지식이 결합을 낳는다. + +- 서로에 대해 알고 있는 지식의 양이 결합도를 결정한다. +- 결합도를 느슨하게 만들기 위해선 협력 대상에 대해 필요한 정보 외엔 최대한 감추는 것이 중요하다. → 추상화 + +## 추상화에 의존하라 + +- **추상화** + - 어떤 양상, 세부사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법 + +- 추상화와 결합도의 관점에서 의존 대상을 다음과 같이 구분한다. (아래로 갈수록 느슨한 결합도를 가진다) + - 구체 클래스 의존성 + - 추상 클래스 의존성 + - 인터페이스 의존성 + +## 명시적인 의존성 + +- 의존성은 명시적으로 표현되어야 한다. +- 의존성을 구현 내부에 숨겨두지 말아라. +- 유연하고 재사용 가능한 설계란 퍼블릭 인터페이스를 통해 의존성이 명시적으로 드러나는 설계이다. +- 경계해야할 것은 의존성 자체가 아니라 의존성을 감추는 것이다 → 숨겨져 있는 의존성을 밝은 곳으로 드러내어 널리 알리자. + +- 의존성이 명시적이지 않으면 의존성을 파악하기 위해 내부 구현을 직접 보아야함. + + → 고통스러움… + + +## new는 해롭다 + +- new 연산자를 사용시 구체 클래스의 이름을 직접 기술해야 함. + - 구체 클래스에 의존할 수 밖에 없기 떄문에 결합도가 높아짐. +- new 연산자는 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 함. + - 클라이언트가 알아야하는 지식의 양이 늘어나기 때문에 결합도가 높아짐. + +**해결 방법은 인스턴스를 생성하는 로직과 생성된 인스턴스를 사용하는 로직을 분리하는 것.** + +→ 생성의 책임을 클라이언트로 옮긴다. + +***훌륭한 설계란..*** + +- 사용과 생성의 책임을 분리 +- 의존성을 생성자에 명시적으로 드러냄 +- 구체 클래스가 아닌 추상 클래스에 의존하게 함 +- 객체를 생성하는 책임을 객체 내부가 아닌 클라이언트로 옮김 + +## 가끔은 생성해도 무방함 + +생성자를 체인처럼 연결하여 사용한다. (⇒ 오버로딩 할 때도 사용가능) + +(첫번째 생성자에서 두번째 생성자를 ‘this’로 호출하여 구체 클래스를 다르게 삽입) + +## 표준 클래스에 대한 의존은 해롭지 않다 + +의존성이 불편한 이유는 변경에 대한 영향을 암시하기 때문인데, 변경될 확률이 거의 없는 클래스라면 의존성이 문제가 되지 않는다. + +(e.g. JDK에 포함된 표준 클래스) + +해당 클래스들은 구체 클래스에 의존하거나 직접 인스턴스를 생성하더라도 문제가 없다. + +--- + +- 유연하고 재사용 가능한 설계는 작은 객체들의 행동을 조합함으로써 새로운 행동을 이끌어 낼 수 있는 설계. +- 훌륭한 객체지향 설계란 객체가 어떻게 하는질 표현하는 것이 아니라 객체들의 조합을 선언적으로 표현함으로써 객체들이 무엇을 하는지 표현하는 설계. +- 이런 설계를 창조하는데 핵심은 의존성을 관리하는 것. \ No newline at end of file diff --git a/chapter09/minhyun/ch9.md b/chapter09/minhyun/ch9.md new file mode 100644 index 0000000..2a6e0fb --- /dev/null +++ b/chapter09/minhyun/ch9.md @@ -0,0 +1,132 @@ +# 01. 개방-폐쇄 원칙(Open Close Principle) + +소프트웨어 개체는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다. + +‘확장’ → 동작 + +‘수정’ → 코드 + +- **확장에 대해 열려있다** + - 요구사항 변경시 해당 변경에 맞게 새로운 ‘동작’을추가하여 애플리케이션의 기능 확장 +- 수**정에 대해 닫혀있다.** + - 기존의 ‘코드’를 수정하지 않고도 동작을 추가하거나 변경 가능 + +## 컴파일타임 의존성을 고정시키고 런타임 의존성을 변경하라 + +앞에서 말한 런타임 의존성과 컴파일타임 의존성에 관련된 이야기이다. + +앞쪽에서 진행했던 `OverlappedDiscountPolicy` , `NoneDiscountPolicy`의 구현처럼 기존 코드는 전혀 손대지 않은 채 클래스를 추가함으로써 동작 확장을 하였다. + +## 추상화가 핵심이다 + +**개방-폐쇄 원칙의 핵심은 추상화에 의존하는 것**이다. + +- 상속을 통해 생략된 부분을 구체화함으로써 할인 정책을 확장할 수 있다. +- 변하지 않는 부분을 고정하고 변하는 부분을 생략하는 추상화 메커니즘이 개방-폐쇄 원칙의 기반이 된다. +- 개방-폐쇄 원칙에서 폐쇄를 가능하게 하는 것은 의존성의 방향이다. + +## 02. 생성 사용 분리 + +- 동일 클래스 안에서 객체 생성과 사용이라는 두가지 이질적인 목적을 가진 코드가 공존하는 것은 문제이다. + + → 객체에 대한 생성과 사용을 분리 해야 한다. + + → 객체를 생성할 책임을 클라이언트로 옮기는 것. + + +금액 할인 정책을 적용할지, 비율 할인 정책을 적용할 지를 알고 있는 것은 그 시점에 Movie와 협력할 클라이언트이기 때문에 조금만 생각해보면 이 방법이 타당한 걸 알 수 있다. + +→ 컨텍스트에 관한 결정권을 가지고 있는 클라이언트로 컨텍스트에 대한 지식을 옮김으로써 특정한 클라이언트에 결합되지 않고 독립적일 수 있다. + +## Factory 추가하기 + +## 순수한 가공물에게 책임 할당하기 + +- 책임 할당의 기본 원칙은 책임을 수행하는데 필요한 정보를 가장 많이 알고있는 ‘INFORMATION EXPERT’에게 책임을 할당하는 것. + +- 표현적 분해 + - 도메인에 존재하는 사물 또는 개념을 표현하는 객체들을 이용해 시스템을 분해하는 것 + - 도메인 모델에 담겨있는 개념과 관계를 따르며 도메인과 소프트웨어 사이의 표현적 차이를 최소화하는 것을 목적으로 함. +- 행위적 분해 + +**모든 책임을 도메인 객체에게 할당하면 낮은 응집도, 높은 결합도, 재사용성 저하와 같은 심각한 문제점에 봉착하게 될 가능성이 높아진다.** + +이 경우 도메인 개념을 표현한 객체가 아닌 설계자가 **편의를 위해 임의로 만들어낸 가공의 객체에게 책임을 할당하여 문제를 해결**해야 함 + +→ **PURE FABRICATION(순수한 가공물)** + +어떤 행동을 추가하려 하는데 이 행동을 책임질 마땅한 도메인 개념이 존재하지 않는다면 ‘순수한 가공물’ 을 추가하고 이 객체에게 책임을 할당하라 + +→ 특정한 행동을 표현하는게 일반적이며, 행위적 분해에 의해 생성되는 것이 일반적. + +# 03. 의존성 주입 + +외부의 독립적인 객체가 인스턴스를 생성한 후 이를 전달해서 의존성을 해결하는 방법. + +- 생성자 주입 +- setter 주입 +- 메서드 주입 + +## 숨겨진 의존성은 나쁘다 + +널리 사용되는 방법은 ‘SERVICE LOCATOR 패턴’ 이다. + +→ 객체가 직접 ‘service locator’에게 의존성을 해결해 줄 것을 요청함 (의존성 주입과 반대) + +필자는 해당 패턴을 선호하지 않는데 이유는 ‘의존성을 감추기 때문’ 이라고 한다. + +- 해당 문제가 컴파일타임이 아닌 런타임에 가서야 발견이 된다. + - 이해가 어렵고 디버깅이 어려운 이유는 코드 작성 시점이 아닌 실행 시점으로 문제 발견 시점을 미루기 때문. + - 의존성의 대상을 설정하는 시점과 의존성이 해결되는 시점을 멀리 떨어뜨려 놓음. +- 내부 정적변수를 사용해 객체를 관리하여 모든 단위 테스트가 locator 상태를 공유하게 되어, 단위 테스트가 고립되어야 한다는 기본 원칙이 위반됨. +- 캡슐화가 위반됨. + - 의존성을 이해하기 위해 내부 구현을 이해할 것을 강요함. + +어쩔 수 없이 해당 패턴을 사용해야 하는 경우도 있다. + +- 의존성 주입을 지원하는 프레임워크를 사용하지 못하는 경우 +- 깊은 호출 계층에 걸쳐 동일한 객체를 계속해서 전달해야 하는 고통을 견디기 어려운 경우 + +**가능한 의존성을 명시적으로 표현할 수 있는 기법을 사용하라.** + +# 04. 의존성 역전 원칙 + +- 협력에서 중요한 정책이나 의사결정, 비즈니스의 본질을 담고 있는 것은 상위 수준의 클래스다. +- 상위 수준의 클래스는 하위 클래스에 의존해선 안된다. + +**이걸 해결하기 위한 만능 열쇠는 ‘추상화’~** + +- **의존성 역전원칙 (Dependency Inversion Principle)** + - 상위 수준의 모듈은 하위 수준의 모듈에 의존해선 안된다. 둘 모두 추상화에 의존해야 한다. + - 추상화는 구체적인 사항에 의존해선 안된다. 구체적인 사항은 추상화에 의존해야 한다. + +## 의존성 역전 원칙과 패키지 + +추상화를 별도의 독립적인 패키지가 아닌 클라이언트가 속한 패키지에 포함시켜야 한다. 그리고 재사용 될 필요가 없는 클래스들은 별도의 독립적인 패키지에 모아야 한다. + +**→ SEPARATED INTERFACE 패턴** + +잘 설계된 객체지향 애플리케이션은 인터페이스의 소유권을 서버가 아닌 클라이언트에 위치시킨다. + +- 전통적인 패러다임 + - 상위수준 모듈이 하위수준 모듈에 의존 + - 인터페이스가 하위 수준 모듈에 속함 +- 객체지향 패러다임 + - 상위수준 모듈과 하위 수준 모듈이 모두 추상화에 의존 + - 인터페이스가 상위수준 모듈에 속함 + +# 05. 유연성에 대한 조언 + +## 유연한 설계는 유연성이필요할 때만 옳다. + +- 유연한 설계 == 복잡한 설계 +- 미래에 변경이 일어날지도 모른다는 막연한 불안감은 불필요하게 복잡한 설계를 낳는다. **아직 일어나지 않은 변경은 변경이 아니다.** +- 유연성은 항상 복잡성을 수반한다. + - 유연하지 않은 설계는 단순하고 명확함. + - 유연한 설께는 복잡하고 암시적. + +## 협력과 책임이 중요하다. + +- 설계를 유연하게 만들기 위해선 협력에 참여하는 객체가 다른 객체에게 어떤 메시지를 전송하는지가 중요하다. +- 역할, 책임, 협력에 초점을 맞춰야 한다. +- 객체를 생성할 책임을 담당할 객체나 객체 생성 메커니즘을 결정하는 시점은 책임 할당의 마지막 단계로 미뤄야만 한다. \ No newline at end of file