Study/오브젝트

[오브젝트03] 역할, 책임, 협력

voider 2021. 4. 7. 18:52

역할, 책임, 협력

객체지향 패러다임의 핵심은 역할, 책임, 협력이다. 이것이 구현보다 중요하다.

다시 영화 예매 시스템을 보자.

조영호, 오브젝트(위키북스) 74p

위 표를 보면 알 수 있듯이 객체지향 원칙을 따르는 애플리케이션은 어떤 하나의 객체가 통제하지 않는다. 다양한 객체들이 자신의 일을 수행하면서 애플리케이션의 전체 기능을 완성한다. 여기서 중요한 점은 객체들이 '메세지'를 통해 상호작용한다는 것인데, 이것을'협력'이라고 한다. '책임'은 협력에 참여하기 위해 수행하는 로직이다. 객체가 가진 책임들이 모여 그 객체가 수행하는 '역할'을 구성한다.

협력

협력은 객체가 다른 객체에게 도움을 요청할 때 시작된다. 이것을 메세지 전송이라고 하는데, 객체가 협력할 때 사용하는 유일한 방법이다. 객체는 다른 객체의 내부 구현에 접근할 수 없고, 오직 메세지를 통해 요청할 수만 있다. 메세지를 수신한 객체는 적절한 메서드를 실행함으로써 요청에 응답한다.
객체는 자율적이어야 한다. 자신의 일은 자신이 직접 처리하고, 자신이 처리할 수 없는 일은 다른 객체에게 부탁해야 한다. 다른 객체의 내부 구현에 간섭해서는 안 된다. 객체를 자율적으로 만들 수 있는 기본 원칙이 캡슐화다.
객체가 자율적이라고 해서 자신의 인터페이스를 직접 결정하는 것은 아니다. 그것은 객체가 참여하고 있는 협력 속에서 결정해야 한다. Movie라는 객체는 당연히 play라는 행동을 수행할 것이라고 생각하겠지만 아니다. Movie는 예매를 위한 협력에 참여하고 그 안에서 요금을 계산하는 책임을 지고 있다. Movie는 예매를 위한 협력에 참여하고 있으니 그에 맞는 인터페이스를 제공해야 한다. 협력이라는 문맥을 고려하지 않고 Movie의 행동을 결정하는 것은 의미가 없다. 협력이 존재하기 때문에 객체가 존재한다. 협력은 설계하는 데 필요한 문맥을 제공한다.(77p)

책임

협력에 필요한 객체가 실제로 수행하는 행동을 책임이라고 한다. 객체의 책임은 무엇을 알고 있는가와 무엇을 할 수 있는가로 구성할 수 있다. 책임의 관점에서 '아는 것'과 '하는 것'은 밀접한 연관이 있다. 객체는 자신이 아는 것을 이용해서 자신의 책임을 수행한다.
객체에게 적절한 책임을 할당하는 것은 매우 중요하다. 책임을 할당하는 가장 기본적인 방법으로 정보 전문가(Information Expert) 패턴이라는 게 있다. 그 책임을 수행하는 데 필요한 정보를 가장 잘 알고 있는 객체에게 그 책임을 할당하는 패턴이다.
영화 예매 애플리케이션을 예를 들어 보자. 가장 먼저 협력을 시작할 메세지를 결정해야 한다. 예매하라는 메세지로 협력을 시작하자.

  • 예매하라 --> ??

이제 이 메세지를 해석할 수 있도록 예매하라, 는 책임을 어딘가에 할당해야 한다. 기본 전략은 정보 전문가에게 할당하는 것이다. 영화 예매와 관련된 정보를 가장 많이 알고 있는 것은 Screening이었다.

  • 예매하라 --> Screening

Screening은 예매할 수 있지만 가격을 계산할 수는 없다. 가격에 대해 가장 잘 알고 있는 정보 전문가에게 또 계산하라는 메세지를 보내 협력을 요청해야 한다. 가격을 계산하기 위해 가격과 할인 정책이 필요하다. 이 정보는 Movie가 가장 많이 알고 있다.

  • 예매하라 --> Screening --계산하라--> Movie

Movie도 역시 할인 정책에 대해서는 잘 모르기 때문에 또다른 정보 전문가에게 협력을 요청해야 한다. 이처럼 객체지향 설계는 먼저 메세지를 찾고, 메세지를 적절히 처리할 수 있는 객체를 선택하는 과정을 통해 이루어진다. 메세지가 메세지를 수신할 객체의 책임을 결정한다. 메세지를 먼저 만들어야 구현에 얽매이지 않는 충분히 추상화된 인터페이스를 만들 수 있다. 또한 메세지를 만든 다음 그 메세지를 처리할 인터페이스를 만들기 때문에 최소한의 인터페이스, 반드시 필요한 인터페이스만 만들 수 있다.

책임 주도 설계(Responsibility Driven Design)
이와 같은 설계 방법을 책임 주도 설계라고 부른다. 절차는 이렇다.

  1. 시스템이 사용자에게 제공하는 기능인 시스템 책임을 파악
  2. 시스템 책임을 더 작은 책임으로 분할
  3. 분할된 책임을 수행하는 적절한 객체 또는 역할을 찾아 책임 할당
  4. 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다
  5. 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력

객체는 협력하기 위해 존재한다. 이 객체가 협력에 얼마나 적합한 지 판단하는 기준은 객체의 상태가 아니라 행동이다. 이 객체가 어떤 상태를 가지고 있느냐, 가 아니라 이 객체가 어떤 행동을 할 수 있느냐, 다. 행동이 아니라 상태에 초점을 맞춰서 설계하면 캡슐화를 저해하기 쉽다. 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문이다. 내부 구현에 초점을 맞춘 설계는 객체지향설계가 아니라 데이터주도설계다. 언제나 상태보다 행동이 중요하다는 것을 알아야 한다. 이 객체가 어떤 행동을 하느냐, 가 이 객체에 상태를 결정한다. 상태가 행동을 결정하는 것이 아니다.

역할

역할은 협력 안에서 객체가 가지는 책임의 집합이다. 실제 모델링 시 객체가 아니라 역할에 책임을 할당한다고 생각하는 게 좋다. 역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다. 역할은 객체를 추상화하는 것이다. 위에서 책임 주도 설계를 설명할 때 썼던 예제를 다시 보자.

  • 예매하라 --> Screening --계산하라--> Movie

여기서 정확한 요금을 계산하기 위해서 할인 정책을 알아야 한다. 할인 정책은 구체적으로 금액 할인과 비율 할인 두 가지가 있었다. 우리는 이것을 '할인'이라는 것으로 추상화했기 때문에 Movie는 그게 금액 할인인지, 비율 할인인지 몰라도 된다.

역할로 뽑아내기 전 할인 조건

  • 예매하라 --> Screening --계산하라--> Movie --비율 할인-->PercentDiscountPolicy

또는

  • 예매하라 --> Screening --계산하라--> Movie -- 금액 할인-->AmountDiscountPolicy

하지만 우리는 이것을 DiscountPolicy라는 역할로 추상화 했기 때문에

  • 예매하라 --> Screening --계산하라--> Movie --할인-->DiscountPolicy

이렇게 사용할 수 있는 것이다.

역할의 구현
역할을 구현은 추상 클래스나 인터페이스로 한다. 협력의 관점에서 추상클래스/인터페이스는 구체클래스가 따라야 하는 책임의 집합을 서술한 것이다.