# 14장 일관성 있는 협력

객체지향 설계의 목표는 `적절한 책임`을 수행하는 객체들의 협력을 기반으로 `결합도`가 낮고 `재사용` 가능한 코드 구조를 창조하는 것이다. 유사한 요구사항을 계속 추가해야 하는 상황에서 각 협력이 서로 다른 패턴을 다를 경우에는 전체적인 설계의 `일관성`이 서서히 무너지게 된다.

하지만 `재사용`은 공짜로 얻어지지 않는다. 재사용을 위해서는 객체 들의 `협력` 방식을 일관성 있게 만들어야 한다. 특정한 문제를 `유사한 방법`으로 해결하고 있다는 사실을 알면 문제를 이해하는 것만으로도 코드의 구조를 예상할 수 있게 된다.

대부분의 사람들은 `유사한 요구사항`을 구현하는 코드는 `유사한 방식`으로 구현될 것이라고 예상한다. 하지만 유사한 요구사항이 서로 다른 방식으로 구현돼 있다면 요구사항이 유사하다는 사실 자체도 의심하게 될 것이다.

결론은 유사한 기능을 서로 다른 방식으로 구현 해서는 안 된다는 것이다. `일관성 없는 설계`와 마주한 개발자는 여러 가지 해결 방법 중에서 현재의 요구사항을 해결하기에 가장 적절한 방법을 찾아야 하는 `부담`을 안게 된다.

유사한 기능은 유사한 방식으로 구현해야 한다. 객체 지향에서 기능을 구현하는 유일한 방법은 객체 사이의 `협력`을 만드는 것 뿐이므로 `유지보수` 가능한 시스템을 구축하는 첫걸음은 협력을 일관성 있게 만드는 것이다.

## 설계에 일관성 부여하기

일관성 있는 설계를 만드는 데 가장 훌륭한 조언은 `다양한 설계 경험`을 익히라는 것이다. 두 번째 조언은 널리 알려진 `디자인 패턴`을 학습하고 변경이라는 문맥 안에서 디자인 패턴을 적용해 보라는 것이다.

디자인 패턴이 반복적으로 적용할 수 있는 설계 구조를 제공한다고 하더라도 모든 경우에 적합한 패턴을 찾을 수 있는 것은 아니다. 따라서 `협력`을 일관성 있게 만들기 위해 다음과 같은 기본 지침을 따르는 것이 도움이 될 것이다.

* **변하는 개념을 변하지 않는 개념으로부터 분리하라**
* **변하는 개념을 캡슐화하라**

### 캡슐화 다시 살펴보기

`캡슐화`란 변하는 어떤 것이든 감추는 것이다. 캡슐화의 가장 대표적인 예는 객체의 `퍼블릭 인터페이스`와 구현을 분리하는 것이다.

![https://i.ibb.co/BgW99DV/3.png](https://i.ibb.co/BgW99DV/3.png)

소프트웨어 안에서 변할 수 있는 어떤 '`개념`'이라도 감추는 것이다. 이 사실을 기억하면서 다음 그림을 살펴보자

![https://i.ibb.co/0C12DKb/4.png](https://i.ibb.co/0C12DKb/4.png)

위 그림에는 다음과 같은 다양한 종류의 `캡슐화`가 공존한다.

* **데이터 캡슐화** : Movie 클래스의 `인스턴스 변수` title의 가시성은 private이기 때문에 외부에서 직접 접근할 수 없다. 이 속성에 접근할 수 있는 유일한 방법은 `메서드`를 이용하는 것뿐이다. 다시 말해 클래스는 내부에 관리하는 데이터를 캡슐화한다.
* **메서드 캡슐화** : `DiscountPolicy` 클래스에서 정의돼 있는 `getDiscountAmount` 메서드의 가시성은 `protected`다. 클래스의 외부에서는 이 메서드에 직접 접근할 수 없고 클래스 내부와 서브 클래스에서만 접근이 가능하다. **따라서 클래스 외부에 영향을 미치지 않고 메서드를 수정할 수 있다.** 다시 말해 클래스의 내부 행동을 캡슐화하고 있는 것이다.
* 객체 캡슐화 : `Movie` 클래스는 `DiscountPolicy` 타입의 인스턴스 변수 `discountPolicy`를 포함한다. 이 인스턴스 변수는 private 가시성을 가지기 때문에 Movie와 DisvountPolicy 사이의 관계를 변경 하더라도 외부에는 영향을 미치지 않는다. **다시 말해서 객체와 객체 사이의 관계를 캡슐화한다.** 눈치가 빠른 사람이라면 객체 캡슐화가 `합성`을 의미한다는 것을 눈치챘을 것이다.
* 서브타입 캡슐화 : `Movie`는 `DiscountPolicy`에 대해서는 알고 있지만 `AmountDiscountPolicy`와 `PercentDiscountPolicy`에 대해서는 알지 못한다. 그러나 실제로 실행 시점에는 이 클래스들의 인스턴스와 협력할 수 있다. 이것은 파생 클래스인 `DiscountPolicy`와의 추상적인 관계가 `AmountDiscountPolicy`와 `PercentDiscountPolicy`의 존재를 감추고 있기 때문이다. 다시 말해 서브타입의 종류를 캡슐화하고 있는 것이다. 눈치가 빠른 사람이라면 서브타입 캡슐화가 `다형성`의 기반이 된다는 것을 알 수 있을 것이다.

### 구체적인 협력 구현하기

유사한 기능에 대해 유사한 협력 패턴을 적용하는 것은 객체지향 시스템에서 `개념적 무결성(Conceptual Integrity)`을 유지할 수 있는 가장 효과적인 방법이다. 시스템이 일관성 있는 몇 개의 협력 패턴으로 구성된다면 시스템을 이해하고, 수정하고, 확장하는 데 필요한 노력과 시간을 아낄 수 있다.

### 지속적으로 개선하라

협력은 고정된 것이 아니다. 만약 현재의 `협력 패턴`이 변경의 무게를 지탱하기 어렵다면 변경을 수용할 수 있는 협력 패턴을 향해 과감하게 리팩터링하라. 요구 사항의 변경에 따라 협력 역시 지속적으로 `개선`해야 한다. 중요한 것은 현재의 설계에 맹목적으로 일관성을 맞추는 것이 아니라 달라 지는 변경의 방향에 맞춰 지속적으로 코드를 개선하려는 의지다.
