Chapter 3. 역할, 책임, 협력

객체지향 패러다임의 관점에서 핵심은 역할, 책임, 협력이다.

애플리케이션의 기능을 구현하기 위해 수행하는 상호작용을 협력, 객체 자체가 협력에 참여하기 위해 수행하는 로직을 책임이라 한다.

객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다

객체지향의 본질은 협력하는 객체들의 공동체를 창조하는 것이다.

클래스와 상속은 객체들의 책임과 협력이 어느 정도 자리를 잡은 후에 사용할 수 있는 구현 메커니즘일 뿐이다.

역할, 책임 협력이 제자리를 찾지 못한 상태라면 응집도 높은 클래스와 중복 없는 상속 계층을 구현한다고 하더라도 애플리케이션이 침몰하는 것을 구원하지는 못한다.

협력

객체지향 시스템은 자율적인 객체들의 공동체다.

두 객체 사이의 협력은 하나의 객체가 다른 객체에게 도움을 요청할 때 시작된다.

객체는 다른 객체의 상세한 내부에 접근할 수 없기 때문에 오직 메시지 전송을 통해서만 자신의 요청을 전달할 수 있다.

객체 사이의 협력을 설계할 때는 객체를 서로 분리된 인스턴스가 아닌 협력하는 파트너로 인식해야 한다.

메시지를 수신한 객체는 메서드를 실행해 요청에 응답하고 여기서 객체가 메시지를 처리할 방법을 스스로 선택한다는 점이 중요하다.

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

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

캡슐화를 통해 변경에 대한 파급효과를 제한할 수 있기 때문에 자율적인 객체는 변하기도 쉬워진다.

협력이 설계를 위한 문맥을 결정한다

객체란 상태와 행동을 함께 캡슐화하는 실행 단위다.

애플리케이션 안에 어떤 객체가 필요하다면 그 이유는 단 하나다.

그 객체가 어떤 협력에 참여하고 있고 협력에 필요한 적절한 행등을 보유하고 있기 때문이다.

결론적으로 객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력이다.

협력이 바뀌면 객체가 제공해야 하는 행동 역시 바뀌어야 한다.

협력은 객체가 필요한 이유와 객체가 수행하는 행동의 동기를 제공한다.

책임이란 무엇인가

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

책임이란 객체에 의해 정의되는 응집도 있는 행위의 집합으로 객체가 유지해야 하는 정보와 수행할 수 있는 행동에 대해 개략적으로 서술한 문장이다.

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

  • 하는 것(doing)
    • 객체를 생성하거나 계산을 수행하는 등의 스스로 하는 것
    • 다른 객체의 행동을 시작시키는 것
    • 다른 객체의 활동을 제어하고 조절하는 것
  • 아는 것(knowing)
    • 사적인 정보에 관해 아는 것
    • 관련된 객체에 관해 아는 것
    • 자신이 유도하거나 계사할 수 있는 것에 관해 아는 것

일반적으로 책임과 메시지의 크기는 다르다.

책임은 메시지보다 추상적이고 개념적으로도 더 크다.

여러 개의 메시지로 분할되기도 하고 하나의 객체가 수행할 수 있다고 생각했던 책임이 나중에는 여러 객체들이 협력해야만 하는 커다란 책임으로 자라는 것이 일반적이다.

어떤 책임을 수행하기 위해서는 그 책임을 수행하는데 필요한 정보도 함께 알아야 할 책임이 있는 것이다.

책임은 객체지향 설계의 핵심 이다.

객체에게 얼마나 적절한 책임을 할당하느냐가 설계의 전체적인 품질을 결정한다.

책임 할당

INFORMATION EXPERT(정보 전문가) 패턴

책임 할당을 위한 GRASP 패턴

일상 생활에서 도움을 전문가에게 도움을 요청하는 것처럼 객체의 세계에서도 협력에 필요한 지식과 방법을 가장 잘 알고 있는 객체에게 도움을 요청한다.

이 요청에 응답하기 위해 필요한 이 행동이 객체가 수행할 책임으로 이어지는 것이다.

객체지향 설계는 시스템의 책임을 완료하는 데 필요한 더 작은 책임을 찾아내고 이를 객체들에게 할당하는 반복적인 과정을 통해 모양을 갖춰간다.

정보 전문가에게 책임을 할당하는 것만으로도 상태와 행동을 함께 가지는 자율적인 객체를 만들 가능성이 높아진다.

책임 주도 설계

책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법을 책임 주도 설계(Responsibility-Driven Design, RDD)라고 한다.

What is responsibility-driven design?

책임 주도 설계 방법의 과정

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

책임 주도 설계는 자연스럽게 객체의 구현이 아닌 책임에 집중할 수 있게 한다.

구현이 아닌 책임에 집중하는 것이 중요한 이유는 유연하고 견고한 객체지향 시스템을 위해 가장 중요한 재료가 바로 책임이기 때문이다.

메시지가 객체를 결정한다

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

  1. 객체가 최소한의 인터페이스(minimal interface)를 가질 수 있게 된다. 즉 꼭 필요한 크기의 퍼블릭 인터페이스를 가질 수 있다.

  2. 객체는 추상적인 인터페이스(abstract interface)를 가질 수 있게 된다. 객체는 무엇을 하는지는 표현해야 하지만 어떻게 수행하는지는 노출해서는 안된다.

객체가 충분히 추상적이면서 미니멀리즘을 따르는 인터페이스를 가지게 하고 싶다면 메시지가 객체를 선택하게 한다.

행동이 상태를 결정한다

객체가 존재하는 이유는 협력에 참여하기 위해서다.

객체의 행동은 객체가 협력에 참여할 수 있는 유일한 방법이다.

얼마나 적절한 객체를 만들고 얼마나 적절한 책임을 할당했고 책임이 얼마나 적절한지는 협력에 얼마나 적절한가에 달려있다.

개별 객체의 상태와 행동이 아닌 시스템의 기능을 구현하기 위한 협력에 초점을 맞춰야 응집도가 높고 결합도가 낮은 객체들을 만들수 있다.

상태는 단지 객체가 행동을 정상적으로 수행하기 위한 재료일 뿐이다.

역할과 협력

객체는 협력이라는 주어진 문맥 안에서 특정한 목적을 갖게 된다. 객체의 목적은 협력 안에서 객체가 맡게 되는 책임의 집합으로 표시된다.

어떤 특정한 협력 안에서 수행하는 책임의 집합을 역할이라고 한다.

실제로 협력을 모델링할 때는 특정한 객체가 아니라 역할에게 책임을 할당한다고 생각하는게 좋다.

유연하고 재사용 가능한 협력

역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다.

동일한 책임을 수행하는 역할을 기반으로 두 개의 협력을 하나로 통합할 수 있다.

역할을 이용하면 불필요한 중복 코드를 방지하고 유연한 협력을 만든다.

책임과 역할을 중심으로 협력을 바라보는 것이 바로 변경과 확장이 용이한 유연한 설계로 나아가는 길이다.

역할의 구현

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

추상 클래스는 책임의 일부를 구현해 놓은 것이고 인터페이스는 일체의 구현 없이 책임의 집함만을 나열해 놓았다는 차이가 있지만 협력의 관점에서는 둘 모두 역할을 정의할 수 있는 구현 방법이라는 공통점을 공유한다.

추상 클래스와 인터페이스는 동일한 책임을 수행하는 다양한 종류의 클래스들을 협력에 참여시킬 수 있는 확장 포인트를 제공하고 이들은 동일한 책임을 수행할 수 있는 객체들을 협력 안에 수용할 수 있는 역할이다.

역할이 다양한 종류의 객체를 수용할 수 있는 일종의 슬롯이자 구체적인 객체들의 타입을 캡슐화하는 추상화라는 것이다.

객체에게 중요한 것은 행동이며 역할은 객체를 추상화해서 객체 자체가 아닌 협력에 초점을 맞출수 있게 한다.

객체 대 역할

역할은 객체가 참여할 수 있는 일종의 슬롯이다.

협력에 적합한 책임을 수행하는 대상이 한 종류라면 간단하게 객체로 간주하고 만약 여러 종류의 객체들이 참여할 수 있다면 역할이라 부르면 된다.

협력은 역할들의 상호작용으로 구성되고 협력을 구성하기 위해 역할에 적합한 객체가 선택되며 객체는 클래스를 이용해 구현되고 생성된다.

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

다양한 객체들이 협력에 함여한다는 것이 확실하다면 역할로 시작하고 정확한 결정을 내리기 어려운 상황이라면 구체적인 객체로 시작하자.

다양한 시나리오를 탐색하고 유사한 협력들을 단순화하고 합치다 보면 자연스럽게 역할이 그 모습을 들어낼 것이다.

역할은 객체와 클래스에 비해 상대적으로 덜 알려져 있으며 그다지 주목받지 못한 개념이다.

대부분의 객체지향 언어들은 역할을 구현할 수 있는 언어적인 편의 장치를 제공하지 않는다.

그럼에도 유연하고 확장 가능하며 일관된 구조를 가지는 시스템을 구축하는 데 역할은 매우 중요하다.

Object role modeling

역할과 추상화

추상화를 이용한 설계가 가질 수 있는 두가지 장점

  1. 추상화 계층만을 이용하면 중요한 정책을 상위 수준에서 단순화할 수 있다.
  2. 설계가 좀 더 유연해 진다.

역할은 공통의 책임을 바탕으로 객체의 종류를 숨기기 때문에 이런 관점에서 역할을 객체의 추상화로 볼수 있다.

따라서 추상화가 가지는 두 가지 장점은 협력의 관점에서 역할에도 동일하게 적용될 수 있다.

객체에게 중요한 것은 행동이다. 역할이 중요한 이유는 동일한 협력을 수행하는 객체들을 추상화할 수 있기 때문이다.

협력 안에서 동일한 책임을 수행하는 객체들은 동일한 역할을 수행하기 때문에 서로 대체 가능하다.

따라서 역할은 다양한 환경에서 다양한 객체들을 수용할 수 있게 해주므로 협력을 유연하게 만든다.

배우와 배역

연극 안에서 배역을 연기하는 배우라는 협력 안에서 역할을 수행하는 객체라는 관점이 가진 입체적인 측면들을 훌륭하게 담아낸다.

협력은 연극과 동일하고 코드는 극본과 동일하다.

배우는 연극이 시작되면 배역이라는 특정한 역할을 연기한다. 객체는 협력이라는 실행 문맥 안에서 특정한 역할을 수행한다.

하지만 연극이 끝나면 자신의 배역을 잊고 원래 자기 자신으로 돌아온다.

객체는 협력이 끝나면 협력에서의 역할은 잊고 원래 자기 자신으로 돌아올 수 있다.

일반적으러 역할은 객체가 협력에 참여하는 잠시 동안에만 존재하는 일시적인 개념이다.

따라서 동일한 객체라고 하더라도 객체가 참여하는 협력에 따라 객체의 얼굴은 계속해서 바뀐다.

객체는 다수의 역할을 보유할 수 있지만 객체가 참여하는 특정 협력은 객체의 한 가지 역할만 바라볼 수 있다.