Post

[객체지향의 사실과 오해] 04 역할, 책임, 협력

04 역할, 책임, 협력

4장에서는 최후통첩 게임과 앨리스 이야기로 역할, 책임, 협력을 보다 자세히 풀어나가며, 객체지향 설계 기법에 대해 설명한다.

__베르너 귀스의 최후통첩 게임(Ultimatum Game)_

두 사람 중 한 사람에게 일정한 금액을 제공하고, 그 금액을 자유로운 비율로 나머지 한 사람과 나눠가지도록 한다. 특정한 금액을 제안 받은 응답자는 제안자의 제안을 수용하거나 거부할 수 있는데, 이를 거부하게 되면 둘 다 아무런 금액도 받을 수 없게 된다.

전통적인 경제학 관점에 따르면 인간은 항상 자신에게 최대의 이익이 돌아올 수 있도록 합리적인 의사결정을 내리려고 노력한다. 이러한 관점에서 제안자는 응답자에게 최소한의 금액을 제시함으로써 자신에게 돌아올 이익을 최대화할 것이고, 응답자는 제안자의 금액이 아무리 적더라도 받지 못하는 것보다는 낫기 때문에 제안을 받아들여야 한다. 그러나 실험 결과는 ‘금액의 30% 이상을 제안한 사람들의 비율이 80%를 웃돌았다’ 는 것이다. 이는 인간이 처해 있는 상황을 고려하지 않고 단순히 인간의 가지고 있는 본질적인 특성만 고려했기 때문인데, 각 개인이 처해 있는 ‘상황’이 인간의 행동 방식을 결정하므로 이를 무시한 채 각 개인의 반응을 독립적으로 예상하고 관찰하는 것은 무의미하다는 것이다.

객체에서도 마찬가지로, 협력이라는 문맥이 객체의 행동 방식을 결정한다. 저자는 위의 예시를 통해서 중요한 것은 개별 객체가 아니라 객체들 간의 협력이며, 어떤 협력에 참여하는지가 객체에 필요한 행동을 결정하고 이는 곧 객체의 상태로 귀결됨을 강조한다.

__이상한 나라의 앨리스_

하트 잭에 대한 공판이 열리는 대목에서, 하트 왕은 토끼에게 증인을 데려 오라 명하고 토끼가 모자 장수를 증인으로 소환하자 모자 장수는 증인으로 참석해 증언을 한다. 왕이 다음 죄인을 불러오라 하자 토끼에 부름에 의해 공작 부인의 요리사가 소환되었고, 머리가 아파진 왕은 하트 여왕에게 증인 심문을 위임하고 다음 증인으로는 앨리스가 소환된다.

이를 정리하면 다음과 같은데, 이 짧은 대목으로 역할, 책임, 협력이 모두 설명이 가능하다.

image (책에서는 법원 사무관 대신 토끼라고 적혀 있으나 토끼 또한 역할로 정정하는 것이 의미 있을 것 같아서 수정하였고, 해당 역할을 법원 사무관이라고 명명하는 것이 정확한 지는 잘 모르겠음)

협력

협력은 한 사람이 다른 사람에게 도움을 요청할 때 시작되고, 요청을 받은 사람은 일을 처리한 후 요청한 사람에게 필요한 지식이나 서비스를 제공하는 것으로 요청에 응답한다. 협력은 다수의 요청과 응답으로 구성되며 전체적으로 협력은 다수의 연쇄적인 요청과 응답의 흐름으로 구성된다.

앨리스 이야기에서는 하트 잭을 재판한다는 동일한 목적을 달성하기 위해 왕과 여왕, 토끼, 그리고 여러 등장인물이 요청과 응답을 주고 받으며 협력하고 있다.

여기서 협력 안의 요청과 응답에 초점을 맞춰본다면 결국 어떤 등장인물들이 특장한 요청을 받아들일 수 있는 이유는 그 요청에 대해 적절히 응답하는 데 필요한 지식과 행동 방식을 가지고 있기 때문이다. 이렇듯 요청과 응답은 협력에 참여하는 객체가 수행할 책임을 정의한다.

책임

어떤 객체가 어떤 요청에 대해 대답해 줄 수 있거나, 적절한 행동을 할 의무가 있는 경우 해당 객체가 책임을 가진다고 말한다. 책임은 객체지향 설계에서 가장 중요한 재료이며 크레이그 라만은 “객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것” 이라고 말하기도 했다. 책임은 ‘객체가 알아야 하며 외부에 제공해줄 수 있는 정보‘와 ‘객체가 외부에 제공해줄 수 있는, 수행할 수 있는 행위‘에 대해 개략적으로 서술한 문장이다. 즉, 객체가 ‘무엇을 알고 있는가(knowing)’ 와 ‘무엇을 할 수 있는가(doing)’로 구성된다.

크레이그 라만의 하는 것과 아는 것의 두 가지 분류는 다음과 같다.

  • 하는 것(doing)

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

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

객체가 다른 객체에게 주어진 책임을 수행하도록 요청을 보내는 것을 메시지 전송 이라고 하며, 두 객체 간의 협력은 메시지를 통해 이뤄진다. 메시지를 전송함으로써 협력을 요청하는 객체를 송신자 라고 하고, 메시지를 받아 요청을 처리하는 객체를 수신자 라고 한다. 메시지는 협력을 위해 한 객체가 다른 객체로 접근할 수 있는 유일한 방법이기도 하다.

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

역할

어떤 객체가 수행하는 책임의 집합은 객체가 협력 안에서 수행하는 역할을 암시한다. 앨리스 이야기에서 판사는 왕이 되기도 하고 여왕이 되기도 하며, 증인은 모자 장수가 되었다가 요리사가 되었다가 앨리스가 되기도 한다. 역할을 통해 세 번의 재판 과정을 하나의 협력으로 추상화할 수 있으며, 역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이기도 하다. 그러나 어떤 객체라도 이 역할을 대체할 수 있는 것은 아니다. 모자장수가 판사가 될 수 없듯이 역할을 대체하기 위해서는 각 역할이 수신할 수 있는 메시지를 동일한 방식으로 이해할 수 있어야 한다. 즉, 객체가 역할을 대체 가능하기 위해서는 협력 안에서 역할이 수행하는 모든 책임을 동일하게 수행할 수 있어야 한다. 덧붙여 객체가 역할이 암시하는 책임보다 더 많은 책임(주어진 책임 이외에 다른 책임)을 수행할 수 있다는 사실도 잊지 말아야 한다. 동일한 역할을 수행하는 객체들이 동일한 메시지를 수신할 수 있기 때문에 동일한 책임을 수행할 수 있다는 것은 보다 다양한 객체들이 협력에 참여할 수 있다는 것을 의미하고, 이는 협력을 더 유연하고 재사용성 높게 만드므로 매우 중요하다.

객체지향 설계 기법

객체지향 설계 기법을 설명하기에 앞서, 사람들이 흔히 가지고 있는 두 가지 선입견을 저자는 다음과 같이 반박한다.

  1. 시스템에 필요한 데이터를 저장하기 위해 객체가 존재한다.

    -> 객체가 상태의 일부로 데이터를 포함하는 것은 사실이나, 데이터는 단지 객체가 행위를 수행하는 데 필요한 재료일 뿐이며 객체는 행위를 수행하며 협력에 참여하기 위해 존재한다.

  2. 객체지향은 정적(클래스와 클래스 간의 관계를 표현하는) 측면에 중점을 둔다.

    -> 중요한 것은 정적인 클래스가 아닌 동적인 객체이며, 클래스는 시스템에 필요한 객체를 표현하고 생성하기 위한 구현 매커니즘 중 하나이다.

올바른 객체를 설계하기 위해서는 견고하고 깔끔한 협력을 설계해야 하는데, 다음과 같은 순서로 수행된다.

  1. 설계에 참여하는 객체들이 주고받을 요청과 응답의 흐름을 결정한다. 여기서 요청과 응답의 흐름은 객체가 협력에 참여하기 위해 수행될 책임이 된다.
  2. 객체에게 책임을 할당한다. 책임은 객체가 외부에 제공하게 될 행동이 된다.
  3. 객체가 수행하게 될 적절한 책임과 행동을 결정한다.
  4. 행동을 수행하는 데 필요한 데이터를 고민한다.
  5. 클래스의 구현 방법을 결정한다.

정리하면, 협력을 구성하는 데 필요한 일련의 책임을 고안하고 나면 그 책임을 수행하는 데 필요한 객체를 선택하게 되는데 할당된 책임은 객체들이 외부에 제공하게 될 행등을 정의하게 되고, 행동이 결정되면 각 객체가 필요로 하는 데이터를 정의할 수 있다. 데이터와 행동이 결정된 후에야 이를 클래스를 개발할 수 있는 것이다. 협력에 필요한 책임을 결정하고 객체에게 책임을 할당하는 과정을 얼마나 합리적이고 적절하게 수행했는지가 객체지향 설계의 품질을 결정하게 된다.

협력,역할,책임의 관점에서 애플리케이션을 설계하는 유용한 세가지 기법은 다음과 같다.

  • 책임-주도 설계(Responsibility-Driven Design): 협력에 필요한 책임들을 식별하고 적합한 객체에게 할당하는 방식으로 애플리케이션을 설계

  • 디자인 패턴(Design Pattern): 전문가들이 반복적으로 사용하는 해결 방법을 정의해 놓은 설계 탬플릿 모음

  • 테스트-주도 개발(Test-Driven Development): 테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하면서 애플리케이션을 완성해가는 방식

책임-주도 설계(Responsibility-Driven Design)

책임-주도 설계는 말 그대로 객체의 책임을 중심으로 시스템을 구축하는 설계 방법이다. 시스템의 책임을 객체의 책임으로 변환하고, 각 객체가 책임을 수행하는 중에 필요한 정보나 서비스를 제공해줄 협력자를 찾아 해당 협력자에게 책임을 할당하는 순차적인 방식으로 객체들의 협력 공동체를 구축한다. 책임-주도 설계는 개별적인 객체의 상태가 아니라 객체의 책임과 상호작용에 집중한다. 이러한 절차를 정리하면 다음과 같다.

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

디자인 패턴(Design Pattern)

디자인 패턴은 책임-주도 설계의 결과를 표현하는 것이다. 패턴은 모범이 되는 설계이며 특정한 상황에서 설계를 돕기 위해 모방하고 수정할 수 있는 과거의 설계 경험이다. 일반적으로 디자인 패턴은 반복적으로 발생하는 문제와 그 문제에 대한 해법의 쌍으로 구성된다. 디자인 패턴은 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿이고 이를 잘 알고 있다면 책임-주도 설계의 절차를 순차적으로 따르지 않고도 객체들의 역할과 책임, 협력 관계를 빠르게 포착할 수 있게 된다.

테스트-주도 개발(Test-Driven Development)

애자일 방법론의 한 종류인 XP의 기본 프랙티스로 소개되면서 주목받기 시작한 설계 기법으로, 테스트-주도 개발은 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다. 테스트-주도 개발은 책임-주도 설계의 기본 개념을 따르며 이를 통해 도달해야 하는 목적지를 테스트라는 안전장치를 통해 좀 더 빠르고 견고한 방법으로 도달할 수 있도록 해주는 최상의 설계 프랙티스다. 그러나 이는 객체지향에 대한 깊이 있는 지식을 요구하며 책임-주도 설계의 기본 개념과 다양한 원칙과 프랙티스, 패턴을 종합적으로 이해하고 좋은 설계에 대한 감각과 경험을 길러야만 적용할 수 있는 깊이 있는 설계 기법이다.

This post is licensed under CC BY 4.0 by the author.