다형성 → 지연(동적) 바인딩
상속 → 코드 재사용할 수 있는 가장 유명한 방법. 그러나 캡슐화 측면에서 합성이 더 좋다.
객체지향의 핵심은 역할, 책임, 협력이다.
이 세가지의 본질이 흐려지면 응집도 높고 중복 없는 상속을 구현해도 애플리케이션의 유지보수가 어렵다.
3장에서는 왜 역할, 책임, 협력에 집중해야 하는지 다룬다.
협력은 객체 설계의 문맥을 제공한다.
협력은 객체의 행동을 결정하고, 행동은 객체의 상태를 결정한다. 자율적인 객체는 상태와 행동이 함께 한다. 따라서 협력은 객체지향적인 설계 문맥을 제공한다.
협력
책임
역할
협력은 메시지 전송과 메서드 실행이라는 일련의 과정으로 이루어진다.
요청을 보내는 측에서 메시지를 전송하면 객체가 스스로 메서드를 결정하여 실행한다.
앞서 살펴봤던 영화 예매 시스템에서 Screening과 Movie가 영화 요금을 계산하는 과정을 보자.
public Money calculateFee(int audienceCount) {
return movie.calculateMovieFee(this).times(audienceCount);
}
Screening 클래스가 Movie 클래스에게 calculateMovieFee라는 메시지를 전송하여 내부 메서드를 실행하도록 요청한다.
왜 Screening에서 바로 요금을 계산하지 않고 Movie에게 처리를 위임할까?
객체 자율성이 훼손되는 이유가 크다. 정보와 행동은 항상 같이 움직여야 한다. 정보를 제공하는 객체와 행동하는 객체가 다르면 자율적이지 않은 객체이다.
⇒ 객체를 자율적으로 만드는 가장 기본적인 방법은 캡슐화이다. (변경으로 인한 파급효과 제한)
자율적인 객체는 자신의 상태를 직접 관리하고 스스로의 결정에 따라 행동한다.
협력은 객체의 행동을 결정하고, 행동은 객체의 상태를 결정한다. 이러한 관점에서 결국 협력은 객체 설계를 관리한다.
책임은 협력에 참여하기 위해서 객체가 참여하는 행동이다.
주의할 것은 메시지가 객체를 결정하고, 행동이 상태를 결정하도록 해야 한다.
협력이라는 문맥이 갖춰졌다면 그 다음은 적절한 객체를 찾아야 한다. 그렇게 협력에 참여하기 위해서 객체가 참여하는 행동을 책임이라고 한다.
책임은 두 가지로 구분할 수 있다. 이 두 가지는 밀접하게 연관되어 있다.
하는 것, 행동 | 아는 것, 상태 |
---|---|
객체 생성 or 행동 | 사적인 정보 |
다른 객체의 행동을 시작함 (메시지 전송) | 관련된 객체 |
객체 활동 제어 및 조절 | 자신이 유도하거나 계산할 수 있는 것을 아는 것 |
Screening의 책임은 영화를 예매하는 것, Movie의 책임은 영화표 가격을 계산하는 것이다.
이러한 책임은 외부 인터페이스와 내부 속성을 결정한다.
책임과 메시지는 다르다
책임이 메시지보다 더 크고 추상적인 개념이다. 객체지향의 협력, 책임, 역할 중 가장 중요한 것은 책임인데, 적절한 책임을 할당하는 것이 전체 설계 품질을 결정하기 때문이다.
책임 할당을 위한 정보 전문가 패턴에서
자율적인 객체는 책임 수행에 필요한 정보를 가장 잘 알고있는 전문가에게 책임을 할당한다.
아래의 Screening과 Movie의 책임 수행 과정을 보자.
처음부터 객체를 식별하지 않고 예매하라는 메시지를 먼저 남긴다. 그 다음에 적절한 객체를 찾아서 책임을 할당한다.
마찬가지로 가격을 계산하는 경우에도 가격을 계산하라는 메시지를 먼저 남기고 그 뒤에 적절한 객체인 Movie를 찾아서 책임을 할당한다.
항상 정보 전문가 패턴을 따르지는 않지만,
책임 할당의 기본 전략은 이 책임을 수행할 정보 전문가를 찾는 것이다.
책임 할당 시 고려해야 하는 두 가지 요소
메시지가 객체를 결정한다는 것은 메시지 먼저 식별한 뒤에 객체를 나중에 처리한다는 것이다.
위에서 살펴본 정보 전문가 패턴과 같이 최소한의 인터페이스를 가질 수 있고, 충분히 추상적인 인터페이스를 가질 수 있게 된다.
행동이 상태를 결정한다는 것은 객체를 객체답게 만드는 것은 상태가 아닌 행동이라는 의미이다.
상태 중심 설계는 캡슐화를 저하한다. 상태에 필요한 행동을 결정하게 되면 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만든다. 따라서 캡슐화를 저하한다.
이와같이 객체 내부 구현에 초점을 맞춘 설계 방법을 데이터주도설계(DDD)라고 부른다.
중요한 것은 상태가 아니라 행동이다.
역할은 협력 안에서 수행하는 책임의 집합이다
왜 역할을 사용해서 설계를 더 복잡하게 만드는 걸까?
역할을 사용하면 더 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다.
객체가 아닌 책임에 초점을 맞춰야 한다 → 두 협력을 하나로 통합할 수 있게 된다.
⇒ 협력 안에서 두 종류의 객체를 교대로 바꿔 끼울 수 있는 슬롯, 슬롯이 역할이다.
역할은 두 종류의 구체적인 객체를 포괄하는 추상화이다. 두 개념을 포괄할 수 있는 추상적인 이름은 DiscountPolicy 정도로 정의할 수 있다.
핵심은, 동일한 책임을 수행하는 역할을 기반으로 두개의 협력을 하나로 통합할 수 있다는 점이다.