본문 바로가기

도서/Object

[Object] Chapter 03 - 역할, 책임, 협력

반응형

01 협력

협력이란 어떤 객체가 다른 객체에게 무엇인가를 요청하는 것이다. 한 객체는 어떤 것이 필요할 때 다른 객체에게 전적으로 위임하거나 서로 협력한다. 즉, 두 객체가 상호작용을 통해 더 큰 책임을 수행하는 것이다.

 

메시지 전송을 통해 자신의 요청을 전달할 수 있다. 메시지를 수신한 객체는 메서드를 실행해 요청에 응답한다.

여기서 객체가 메시지를 처리할 방법을 스스로 선택한다는 점이 중요하다.

외부 객체는 메시지만 전할 수 있을 뿐, 메시지를 어떻게 처리하는지는 메시지를 수신한 객체가 결정한다.

이는 객체는 자신의 일을 스스로 처리하는 자율적인 존재라는 것을 의미한다.

객체를 자율적으로 만드는 가장 기본적인 방법은 내부 구현을 캡슐화하는 것이다.

 

정리하자면, 자율적인 객체는 외부의 도움이 필요한 경우 적절한 객체에게 메시지를 전송하여 협력 요청을 한다.

메시지를 수신한 객체 역시 메시지를 처리하던 중 직접 처리할 수 없을 경우 다른 객체에게 도움을 요청한다.

이처럼 객체들 사이의 협력을 구성하는 일련의 요청과 응답의 흐름을 통해 애플리케이션의 기능이 구현된다.

 

02 책임

협력에 참여하기 위해 객체가 수행하는 행동을 책임이라고 부른다.

객체의 책임은 객체가 '무엇을 알고 있는가'와 '무엇을 할 수 있는가'로 구성된다.

적절한 협력이 적절한 책임을 제공하고, 적절한 책임을 적절한 객체에게 할당해야만 단순하고 유연한 설계를 창조할 수 있다.

자율적인 객체를 만드는 가장 기본적인 방법은 책임을 수행하는 데 필요한 정보를 가장 잘 알고 있는 전문가에게 그 책임을 할당하는 것이다.

객체가 책임을 수행하게 하는 유일한 방법은 메시지를 전송하는 것이므로 책임을 할당한다는 것은 메시지의 이름을 결정하는 것과 같다.

"예매하라"

위와 같은 메시지를 선택했으면 메시지를 처리할 적절한 전문가 객체를 선택해야 한다.

상영 시간과 기본요금을 아는 객체로 지정한다.

하지만 예매를 위해서는 가격 계산을 해야 하는 데, 이 객체는 그 정보에 대해서는 전문가가 아니다. 따라서 새로운 메시지가 필요하다.

"가격을 계산하라"

가격 계산에 적합한 객체를 찾아 선택해야 한다.

이처럼 객체지향 설계는 협력에 필요한 메시지를 찾고, 메시지에 적합한 객체를 선택하는 반복적인 과정을 통해 이뤄진다.

그리고 이런 메시지가 메시지를 수신할 객체의 책임을 결정한다.

 

위와 같이 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법을 책임 주도 설계라고 한다.

책임 주도 설계 과정을 정리하면 다음과 같다.

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

책임 주도 설계는 자연스럽게 객체의 구현이 아닌 책임에 집중할 수 있게 한다. 책임을 할당할 때는 다음 두 가지 요소를 고려해야 한다.

 

1. 메시지가 객체를 결정한다.

 

메시지가 객체를 선택해야 하는 두 가지 중요한 이유

  • 객체가 최소한의 인터페이스를 가질 수 있게 된다. 필요한 메시지가 생기기 전까지 추가되지 않기 때문에 크지도, 작지도 않은 꼭 필요한 크기의 퍼블릭 인터페이스를 가질 수 있다.
  • 객체는 충분히 추상적인 인터페이스를 가질 수 있다. 객체의 인터페이스는 무엇(what)을 하는지는 표현해야 하지만 어떻게(how) 수행하는지를 노출해선 안 된다. 메시지는 외부의 객체가 요구하는 무언가를 의미하기 때문에 메시지를 식별하면 무엇을 수행할지에 초점을 맞춘 인터페이스를 얻을 수 있다.

 

 

2. 행동이 상태를 결정한다

 

객체가 존재하는 이유는 협력에 참여하기 위해서다. 따라서 객체는 협력에 필요한 행동을 제공해야 한다.

객체의 행동은 객체가 협력에 참여할 수 있는 유일한 방법이다. 객체가 협력에 적합한지를 결정하는 것은 그 객체의 상태가 아니라 행동이다. 초보자들은 먼저 객체에 필요한 상태를 결정하고, 그 후에 상태에 필요한 행동을 결정한다. 이런 방식은 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문에 캡슐화를 저해한다.

협력이 객체의 행동을 결정하고 행동이 상태를 결정한다. 그리고 그 행동이 바로 객체의 책임이 된다.

 

03 역할

객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합을 역할이라고 부른다. 실제로 협력을 모델링할 때는 객체가 아니라 역할에게 책임을 할당한다고 생각하는 게 좋다.

위에서 "예매하라" 메시지를 처리하기에 적합한 객체를 선택했다.

하나의 단계처럼 보이지만 실제로는 영화를 예매할 수 있는 적절한 역할이 무엇인가를 먼저 찾고, 그 다음으로 역할을 수행할 객체를 선택하는 것이다.

 

역할이라는 개념으로 더 번거롭다고 생각할 수 있겠지만, 그렇지 않다. 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다.

위에서 "가격을 계산하라" 메시지를 처리하기 위한 객체를 설정했다. 가격을 계산하기 위해서는 할인 요금을 계산해야 하는데, 이를 위해 "할인 요금을 계산하라" 라는 메시지를 전송한다.

 

할인 요금 계산 메시지에 응답하기 위한 할당을 역할이라는 개념을 고려하지 않고 객체에게 책임을 할당한다고 가정해보자.

영화 예매 도메인에는 금액 할인 정책과 비율 할인 정책이라는 두 가지 종류의 가격 할인 정책이 존재하기 때문에, 두 가지 종류의 객체 모두 할인 요금을 계산하라 메시지에 응답할 수 있어야 한다. 그렇다면 두 종류의 객체가 참여하는 협력을 개별적으로 만들어야 할까?

안타깝게도 이런 방법은 대부분의 코드가 중복되고 마는 상황이 발생할 것이다. 코드 중복은 모든 문제의 근원이기 때문에 이런 방법은 피해야 한다.

문제 해결을 위해서는 객체가 아니라 책임에 초점을 맞춰야 한다. 순수 책임 관점에서 보면 두 종류의 객체는 할인 요금 계산이라는 동일한 책임을 수행한다는 사실을 알 수 있다. 이러한 대표자를 통해 두 협력을 하나로 통합할 수 있으며, 이러한 것이 바로 역할이며 따라서 역할은 추상적이다.

불필요한 중복 코드를 제거할 수 있으며, 협력이 더 유연해진다. 새로운 할인 정책을 추가하기 위한 새로운 협력이 필요 없다.

어떠한 객체라도 이 추상적인 역할을 수행할 수 있다면 협력에 참여할 수 있다.

역할을 구현하는 가장 일반적인 방법은 추상 클래스와 인터페이스를 사용하는 것이다.

 

그러나 오직 한 종류의 객체만 협력에 참여하는 상황에서 역할을 고려하는 것이 유용할까?

협력에 참여하는 후보가 여러 종류의 객체라면 그 후보는 역할이지만, 오직 한 종류의 객체라면 후보는 객체가 된다.

 

명확한 기준을 세우기 어렵고 정보가 부족한 설계 초반에는 어떤 것이 역할이고 어떤 것이 객체인지 결정하기 어려울 것이다.

사람들은 세상을 이해할 때 무의식적으로 개념, 객체, 역할을 뒤섞는다.

따라서 설계 초반에는 적절한 책임과 협력의 큰 그림을 탐색하는 것이 중요하며 역할과 객체를 명확하게 구분 짓는 것은 그다지 중요하지 않다.

애매하다면 단순하게 객체로 시작하고 반복적으로 책임과 협력을 정제해가면서 필요한 순간에 객체로부터 역할을 분리하는 것이 가장 좋은 방법이다.

다만 다양한 객체들이 협력에 참여한다는 것이 확실하다면 역할로 시작하면 된다.

 

 

 

반응형