Close
Close full mode
logo만렙 개발자 키우기

4장. 역할, 책임, 협력

Git RepositoryEdit on Github
Last update: 10 months ago by nowwaterReading time: 10 min

인간이 가지고 있는 본연의 특성이라는 관점에서 인간은 이기적이고 합리적인 존재다. 그러나 타인과 관계를 맺는 과정 속에서 인간은 본연의 특성을 배제하고 자신의 이익을 최소화하는 불합리한 선택을 하기도 한다.

결론적으로 인간이 어떤 본질적인 특성을 지니고 있느냐가 아니라 어떤 상황에 처해 있느냐가 인간의 행동을 결정한다. 즉, 각 개인이 처해 있는 정황 또는 문맥(Context)이 인간의 행동 방식을 결정한다는 것이다.

여기서 인간의 행동을 결정하는 문맥은 타인과의 협력이다. 협력에 얼마나 적절한지에 따라 행동의 정합성이 결정되며 협력이라는 문맥이 인간의 행동 방식을 결정한다.

객체 세계에서도 협력이라는 문맥이 객체의 행동 방식을 결정한다. 가장 흔한 실수가 협력이라는 문맥을 고려하지않은 채 객체가 가져야할 상태와 행동부터 고민하기 시작한다는 것이다.

객체지향 설계에서 전체 품질을 결정하는 것은 여러 객체들이 모여 이뤄내는 협력의 품질. 협력이 자리를 잡으면 객체의 행동이 드러나고, 뒤이어 적절한 객체의 상태가 결정된다.

즉, 훌륭한 객체지향 설계란 조화를 이루며 적극적으로 상호작용하는 협력적인 객체를 창조하는 것.

어떤 협력에 참여하는지 -> 객체에 필요한 행동을 결정 -> 객체의 상태 결정


협력

협력의 본질은 요청과 응답으로 연결되는 사람들의 네트워크다. 문제를 해결하는 과정에 여러 사람이 참가하여 요청과 응답의 연쇄적인 흐름이 발생한다.

한 사람이 다른 사람에게 도움을 요청하면, 요청을 받은 사람은 일을 처리한 후 요청한 사람에게 필요한 지식이나 서비스를 제공하는 것으로 요청에 응답한다.

하트 왕이 빵을 훔친 하트 잭에 대한 재판을 하는 상황을 예를 들 수 있다.

재판 시작 요청을 받은 왕은 하얀 토끼에게 목격자를 불러오도록 요청하고, 하얀 토끼는 모자 장수에게 입장할 것을 요청하며, 왕이 다시 모자 장수에게 증언할 것을 요청하는 과정을 통해 협력이 이뤄진다.

어떤 등장인물들이 특정 요청을 받아들일 수 있는 이유는 그 요청에 대해 적절한 방식으로 응답하는데 필요한 지식과 행동 방식을 가지고 있기 때문이다. 요청과 응답은 협력에 참여하는 객체가 수행할 책임을 정의한다.


책임

객체지향 세계에서는 어떤 객체가 어떤 요청에 대해 대답해줄 수 있거나, 적절한 행동을 할 의무가 있는 경우 해당 객체가 책임을 가진다고 말한다.

결국 어떤 대상에 대한 요청은 그 대상이 요청을 처리할 책임이 있음을 암시한다.

책임은 객체지향 설계의 품질을 결정하는 가장 중요한 요소다.

책임은 객체 외부에 제공해 줄 수 있는 정보와 외부에 제공해 줄 수 있는 서비스의 목록이다. 따라서 책임은 객체의 공용 인터페이스를 구성한다.

분류

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

1. 하는 것(Doing)

  • 객체를 생성하거나 계산 하는 등의 스스로 하는 것
  • 다른 객체의 행동을 시작시키는 것
  • 다른 객체의 활동을 제어하고 조절하는 것

2. 아는 것(Knowing)

  • 개인적인 정보에 관해 아는 것
  • 관련된 객체에 관해 아는 것
  • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

다시 앞선 재판장의 예를 들어 객체의 책임을 확인할 수 있다.

왕은 재판 집행이라는 책임을 수행하기 위해 먼저 하얀 토끼에게 목격자를 불러오도록 요청 후 목격자인 모자 장수에게 증언을 요청한다.

  • 왕은 재판에 참여하는 다른 객체들의 활동을 제어하고 조율 => 하는 것 과 관련된 책임을 수행

하얀 토끼는 모자 장수가 목격자임을 알고 있으며, 동시에 모자 장수가 증인석에 입장하도록 요청한다.

  • => 관련된 객체에 대해 아는 것, 다른 객체의 행동을 시작 시키는 것

모자 장수는 스스로 증인석에 입장해야 하는 책임과 자신이 알고 있는 사실을 증언해야 할 책임을 갖는다.

  • => 객체를 생성하거나 계산을 하는 등의 스스로 하는 것, 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

책임과 메시지

한 객체가 다른 객체에게 요청을 전송하면, 그 요청을 수신한 객체의 책임이 수행되게 한다. 이를 메시지 전송이라고 한다.

두 객체 간의 협력은 메시지를 통해 이뤄진다. 이때 객체는 송신자와 수신자로 구분된다.

다시 법정의 예에서, 왕과 모자 장수가 협력할 수 있는 이유는 왕이 모자 장수가 이해할 수 있는 메시지를 전송할 수 있고, 모자 장수는 왕이 전송하는 메시지에 대해 적절한 책임을 수행할 수 있기 때문이다.

한 가지 주의할 점은 책임과 메시지의 수준이 같지는 않다는 점이다.

  • 책임은 객체가 협력에 참여하기 위해 수행해야 하는 행위를 상위 수준에서 개략적으로 서술한 것이다.
  • 책임을 결정 후 실제로 협력을 정제하면서 메시지로 변환할 때 하나의 책임이 여러 메시지로 분할되는 것이 일반적이다.

객체지향 설계는 협력에 참여하기 위해 어떤 객체가 어떤 책임을 수행해야 하고 어떤 객체로부터 메시지를 수신할 것인지를 결정하는 것으로부터 시작된다.


역할

왕이 '재판한다' 는 책임을 지고 목격자를 불러오도록 요청한 후 증언하도록 요구하는 것과, 모자 장수가 '증인석에 입장한다' 와 '증언한다'라는 책임을 가진다는 것은 재판이라는 협력에 참여하기 위해 모자 장수는 증인이라는 역할을 수행하고 있고 왕은 판사라는 역할을 수행하고 있음을 의미한다.

결론적으로 어떤 객체가 수행하는 책임의 집합은 객체가 협력 안에서 수행하는 역할을 암시한다. 그렇게 역할을 나누는 것은, 재사용 가능하고 유연한 객체지향 설계를 낳는 매우 중요한 구성요소이기 때문이다.

판사증인 이라는 역할을 사용하면 예시에서의 협력을 모두 포괄할 수 있는 하나의 협력으로 추상화할 수 있다.

역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다.

역할을 대체할 수 있는 객체는 동일한 메시지를 이해할 수 있는 객체로 한정된다. => 동일한 책임을 수행할 수 있다.

역할을 객체지향 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념이다.

장점

  • 유사한 협력을 추상화해서 인지 과부하를 줄일 수 있다.
  • 다양한 객체들이 협력에 참여할 수 있기 때문에 협력이 좀 더 유연해지며 재사용성이 높아진다.

협력의 추상화

협력을 추상화함으로써 설계자가 다뤄야 하는 협력의 개수를 줄이는 동시에 구체적인 객체를 추상적인 역할로 대체함으로써 협력의 양상을 단순화한다.

대체 가능성

역할은 다른 객체에 의해 대체 가능하다.

객체가 역할을 대체하기 위해서는 행동이 호환돼야 한다. 즉, 객체는 협력 안에서 역할이 수행할 수 있는 모든 책임을 그대로 수행할 수 있어야 한다.

또한 객체는 역할이 암시하는 책임보다 더 많은 책임을 가질 수 있다. 따라서 대부분의 경우에 객체의 타입과 역할 사이에는 일반화/특수화 관계가 성립하는 것이 일반적이다.

요약하면 역할의 대체 가능성은 행위 호환성을 의미하고, 행위 호환성은 동일한 책임의 수행을 의미한다.

객체의 모양을 결정하는 협력

흔한 오류

첫 번째 선입견

많은 사람들은 시스템에 필요한 데이터를 저장하기 위해 객체가 존재한다는 선입견을 가지고 있다.

물론 객체가 상태의 일부로 데이터를 포함하는 것은 사실이지만 데이터는 단지 객체가 행위를 수행하는데 필요한 재료일 뿐이다.

실제로 중요한 것은 객체의 행동, 즉 책임이다.

두 번째 선입견

객체지향이 클래스와 클래스 간의 관계를 표현하는 시스템의 정적인 측면에 중점을 둔다는 것이다.

중요한 것은 정적인 클래스가 아니라 협력에 참여하는 동적인 객체다.

클래스는 단지 시스템에 필요한 객체를 표현하고 생성하기 위해 프로그래밍 언어가 제공하는 구현 매커니즘일 뿐이다.

객체지향의 핵심은 클래스를 어떻게 구현할 것인가가 아니라 객체가 협력 안에서 어떤 책임과 역할을 수행할 것인지를 결정하는 것

협력을 따라 흐르는 객체의 책임

협력을 설계한다는 것은 설계에 참여하는 객체들이 주고받을 요청과 응답의 흐름을 결정한다는 것을 의미한다.

이렇게 결정된 요청과 응답의 흐름은 객체가 협력에 참여하기 위해 수행될 책임이 된다. 객체에게 책임을 할당하고 나면 책임은 객체가 외부에 제공하게 될 행동이 된다.

적절한 행동을 결정한 후에 그 행동을 수행하는데 필요한 데이터를 고민해야 한다. 그리고나서 클래스의 구현 방법을 결정해야 한다.


앨리스의 이야기에서 누군가는 재판을 진행해야 하고 누군가는 증인을 증인석으로 불러야 하며 누군가는 증언해야 한다. 이처럼 협력을 구성하는 일련의 책임을 먼저 고안하고 나면 그 책임을 수행하는데 필요한 객체를 선택하게 된다.

그리고 특정 객체들에게 책임을 할당하고, 객체들이 외부에 제공하게 될 행동을 정의한다. 행동이 결정됐으니 각 객체가 필요로 하는 데이터를 정의할 수 있다. 이렇게 데이터와 행동이 결정된 후에야 클래스를 개발할 수 있을 것이다.

객체를 충분히 협력적으로 만든 후 협력이라는 문맥 안에서 객체를 충분히 자율적으로 만들어라.


객체지향 설계 기법

책임-주도 설계

객체지향 시스템은 역할과 책임을 수행하는 자율적인 객체들의 공동체다. 결국 객체지향 설계란 애플리케이션의 기능을 구현하기 위한 협력 관계를 고안하고 협력에 필요한 역할과 책임을 식별한 후 이를 수행할 수 있는 적절한 객체를 식별해 나가는 과정이다.

핵심은 올바른 책임을 올바른 객체에게 할당하는 것. 그리고 객체의 상태가 아니라 객채의 책임과 상호작용에 집중한다.

시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한 뒤, 시스템의 기능은 더 작은 규모의 책임으로 분할되고, 각 책임은 책임을 수행할 적절한 객체에게 할당된다. 객체가 책임을 수행하는 도중 스스로 처리할 수 없는 정보나 기능이 필요한 경우 적절한 객체를 찾아 작업을 위임한다. 그럼 이제 요청된 작업을 수행하는 일은 위임받은 객체의 책임으로 변환된다.

디자인 패턴

책임-주도 설계의 결과를 표현한다.

디자인 패턴은 반복적으로 발생하는 문제와 그 문제에 대한 해법의 쌍으로 정의된다. 그리고 공통적으로 사용할 수 있는 역할, 책임, 협력의 템플릿이다.

패턴은 해결하려고 하는 문제가 무엇인지를 명확하게 서술하고, 패턴을 적용할 수 있는 상황과 적용할 수 없는 상황을 함께 설명한다.

테스트-주도 개발

애자일 방법론의 한 종류인 XP의 기본 프랙티스인 설계 기법이다.

기본 흐름은 시랲하는 테스트 코드를 작성하고, 테스트를 통과하는 가장 간단한 코드를 작성한 후 리팩토링을 통해 중복을 제거하는 것이다. 이를 통해 작동하는 깔끔한 코드 를 얻을 수 있다.

팁 : 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각

테스트 주도 개발은 테스트를 작성하는 것이 아니라, 책임을 수행할 객체가 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것

객체지향을 강력하게 만드는 비밀은 책임메시지에 숨어있다.

Previous
3장. 타입과 추상화
Next
5장. 책임과 메시지