캡슐화와 접근 제어는 객체를 두 부분으로 나눈다.
인터페이스와 구현의 분리 원칙은 훌륭한 객체지향 프로그램을 만들기 위해 따라야 하는 핵심 원칙이다.
컴파일 시간 의존성과 실행 시간 의존성을 생각해보자.
컴파일 시점에는 Movie가 DiscountPolicy를 의존하지만
런타임 시점에는 구현 객체인 Amount 또는 Percent 할인정책을 의존할 것 이다.
즉 코드 상에서는 Movie는 DiscountPolicy에 의존한다.
또한 런타임 시점에 의존성이 서로 다를 수 있다.
런타임 시점에 의존성이 달라지면 객체를 생성하고 연결하는 부분을 찾아야 한다.
즉 이해하기는 어려우나 유연해지며 확장 가능해진다.
이와 같이 의존성의 양면성은 설계와 트레이드오프의 산물이라는 사실을 잘 보여준다.
상속이 가치 있는 이유는 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있기 때문이다.
허나 대부분의 사람들은 상속의 목적이 메서드나 인스턴스 변수를 재사용하는 것이라고 생각한다.
인터페이스는 객체가 이해할 수 있는 메시지의 목록을 정의한다는 것을 기억하라. 상속을 통해 자식 클래스는 자신의 인터페이스에 부모 클래스의 인터페이스를 포함하게 된다. 결과적으로 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다. 이 부분은 LSP 원칙을 말한다.
[오브젝트 - p61]
다형성은 객체지향 프로그램의 컴파일 시간 의존성과 실행 시간 의존성이 다를 수 있다.
다형성이란 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력을 의미한다.
메시지와 메서드를 실행 시점에 바인딩한다는 것이다. 이를 지연 바인딩 또는 동적 바인딩이라고 부른다.
대부분의 사람들은 코드 재사용을 상속의 주된 목적이라고 생각하지만 이것은 오해다.
인터페이스를 재사용할 목적이 아니라 구현을 재사용할 목적으로 상속을 사용하면 변경에 취약한 코드를 낳게 될 확률이 높다.
즉 구현 상속은 피하고 인터페이스 상속을 선택하자.
추상화를 사용하면 세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현할 수 있다.
추상화의 이런 특징은 세부사항에 억눌리지 않고 상위 개념만으로도 도메인의 중요한 개념을 설명할 수 있다.
할인 정책이나 할인 조건의 새로운 자식 클래스들은 추상화를 이용해서 정의한 상위의 협력 흐름을 그대로 따르게 된다.
이 개념은 매우 중요한데, 재사용 가능한 설계의 기본을 이루는 디자인 패턴(design pattern)이나 프레임워크(framework) 모두 추상화를 이용해 상위 정책을 정의하는 객체지향의 메커니즘을 활용하고 있기 때문이다.
스프링 프레임워크를 생각해보면 모두 인터페이스와 구현을 분리시켰다. 즉 추상화를 이용했기 때문에 사용자가 간단하게 원하는 기능을 추가할 수 있다.
합성(composition)은 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용하는 방법을 말한다.
상속은 객체지향에서 코드를 재사용하기 위해 널리 사용되는 기법이다. 하지만 두 가지 관점에서 설계에 안 좋은 영향을 미친다.
상속을 이용하기 위해서는 부모 클래스의 내부 구조를 잘 알고 있어야 한다. 또한 추상 메서드가 있을 경우 호출한다는 사실을 알고 있어야 한다.
결과적으로 부모 클래스의 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화된다. 그러다보니 부모 클래스와 자식 클래스가 강하게 결합된다. 강한 결합은 코드를 변경하기 어렵다.
상속은 부모 클래스의 코드와 자식 클래스의 코드를 컴파일 시점에 하나의 단위로 강하게 결합하는 데 비해 인터페이스 상속은 약하게 결합된다.
실제로 Movie는 DiscountPolicy가 외부에 calculateDiscountAmount 메서드를 제공한다는 사실만 알고 내부 구현에 대해서는 전혀 알지 못한다.
이 처럼 인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법을 합성이라고 부른다.
합성은 상속이 가지는 두 가지 문제점을 모두 해결한다.
상속은 클래스의 관계를 강하게 결합 시키지만 인터페이스는 느슨한 결합된다.
적절한 협력을 식별하고 협력에 필요한 역할을 정의한 후에 역할을 수행할 수 있는 적절한 객체에게 적절한