# 9장 유연한 설계

## 개방-폐쇄 원칙

소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.

여기서 키워드는 '`확장`'과 '`수정`'이다. 이 둘은 순서대로 애플리케이션의 '`동작`'과 '`코드`'의 관점을 반영한다.

* `확장에 대해 열려 있다.` 애플리케이션의 요구사항이 변경될 때 이 변경에 맞게 새로운 '`동작`'을 추가해서 애플리케이션의 기능을 확장할 수 있다.
* `수정에 대해 닫혀 있다.` 기존의 '`코드`'를 수정하지 않고도 애플리케이션의 동작을 추가하거나 변경할 수 있다.

### 추상화가 핵심이다

개방-폐쇄 원칙의 핵심은 `추상화`에 의존하는 것이다. 추상화를 사용하면 생략된 부분을 문맥에 적합한 내용으로 채워넣음으로써 각 문맥에 적합하게 기능을 구체화하고 `확장`할 수 있다.

### 순수한 가공물에게 책임 할당하기

도메인 모델은 설계를 위한 중요한 출발점이지만 단지 출발점이라는 사실을 명심해야 한다. 실제로 동작하는 애플리케이션은 데이터베이스 접근을 위한 객체와 같이 도메인 개념들을 초월하는 기계적인 개념들을 필요로 할 수 있다.

모든 책임을 도메인 객체에게 할당하면 `낮은 응집도`, `높은 결합도`, `재사용성 저하`와 같은 심각한 문제점에 봉착하게 될 가능성이 높아진다. 크레이그 라만은 이처럼 책임을 할당하기 위해 창조되는 도메인과 무관한 인공적인 객체를 `PURE FABRICATION(순수한 가공물)`이라고 부른다.

어떤 행동을 추가하려고 하는데 이 행동을 책임질 마땅한 도메인 개념이 존재하지 않는다면 PURE FABRICATION을 추가하고 이 객체에게 책임을 할당하라. 따라서 PURE FABRICATION은 표현적 분해보다는 행위적 분해에 의해 생성되는 것이 일반적이다.

`도메인 개념을 표현하는 객체와 순수하게 창조된 가공의 객체들이 모여 자신의 역할과 책임을 다하고 조화롭게 협력하는 애플리케이션을 설계하는 것이 목표여야 한다.`

### 의존성 주입

의존성 해결은 컴파일타임 의존성과 런타임 의존성의 차이점을 해소하기 위한 다양한 매커니즘을 포괄한다. 따라서 의존성 주입에서는 의존성을 해결하는 세 가지 방법을 가리키는 별도의 용어를 정의한다.

* 생성자 주입(constructor injection) : 객체를 생성하는 시점에 생성자를 통한 의존성 해결
* setter 주입(setter injection) : 객체 생성 후 setter 메서드를 통한 의존성 해결
* 메서드 주입(method injection) : 메서드 실행 시 인자를 이용한 의존성 해결

## 의존성 역전 원칙

### 추상화와 의존성 역전

객체 사이의 협력이 존재할 때 그 협력의 본질을 담고 있는 것은 `상위 수준의 정책`이다. 어떤 협력에서 중요한 정책이나 의사결정, 비즈니스의 본질을 담고 있는 것은 상위 수준의 클래스다.

상위 수준의 변경에 의해 하위 수준이 변경되는 것은 납득할 수 있지만 하위 수준의 변경으로 인해 상위 수준이 변경돼서는 곤란하다.

### 의존성 역전 원칙과 패키지

`의존성 역전 원칙`에 따라 상위 수준의 협력 흐름을 `재사용`하기 위해서는 추상화가 제공하는 인터페이스의 소유권 역시 역전시켜야 한다.

유연하고 재사용 가능하며 `컨텍스트`에 독립적인 설계는 전통적인 패러다임이 고수하는 의존성의 방향을 역전시킨다. 전통적인 패러다임에서는 `상위 수준 모듈`이 `하위 수준 모듈`에 의존했다면 객체지향 패러다임에서는 상위 수준 모듈과 하위 수준 모듈이 모두 추상화에 의존한다.

## 유연성에 대한 조언

### 유연한 설계는 연성이 필요할 때만 옳다

유연성은 항상 `복잡성`을 수반한다. 유연하지 않은 설계는 단순하고 명확하다. 유연한 설계는 복잡하고 암시적이다. 특정 시점의 객체 구조를 파악하는 유일한 방법은 클래스를 사용하는 클라이언트 코드 내에서 객체를 생성하거나 변경하는 부분을 직접 살펴보는 것뿐이다.
