Role & Responsibility & Cooperation(역할 & 책임 & 협력)

객체지향 설계의 핵심은 동적인 객체 간의 협력으로, 협력을 통해 객체의 행동(책임)이 드러나고, 역할이 정의된다.

협력(Cooperation)

협력은 어떤 객체가 다른 객체에게 무언가를 요청할 때 시작되며, 두 객체 사이의 요청과 응답의 연쇄적인 흐름이 모여 애플리케이션의 기능을 구현한다.

  • 협력의 문맥(Context)

    • 객체가 존재하는 이유는 협력에 참여를 목적으로 함

    • 협력은 객체가 필요한 이유와 객체가 수행해야 할 행동의 문맥 제공

  • 요청과 응답

    • 협력은 다수의 요청과 응답으로 구성

    • 특정 요청을 받아들일 수 있다는 것은 그 요청을 처리할 책임이 있음을 의미

책임(Responsibility)

책임은 객체가 협력에 참여하기 위해 수행하는 행동이자, 객체가 유지해야 하는 정보의 집합으로, 크게 두 가지로 분류할 수 있다.

  • 정보(Knowing)

    • 자신의 정보

    • 관련된 객체의 정보

    • 자신이 유도하거나 계산할 수 있는 정보

    • 주로 필드(Field)나 객체 간의 연관 관계(Link)로 구현

  • 서비스(Doing)

    • 객체를 생성하거나 계산을 수행하는 등 스스로 하는 것

    • 다른 객체의 행동을 시작시키거나 제어하는 것

    • 주로 메서드(Method) 호출로 구현

책임은 객체의 공용 인터페이스(Public Interface)를 구성하며, 이는 객체 내부의 구현을 숨기고 외부에는 약속된 동작만 노출하는 캡슐화의 기반이 된다.

인터페이스 설계 원칙

좋은 객체지향 설계를 위해서는 인터페이스를 만들 때 다음 세 가지 원칙을 고려해야 한다.

  1. 추상적인 인터페이스

    • 구체적인 방법(How)이 아니라 의도(What)를 명확하게 표현

    • 너무 상세한 메시지는 객체의 자율성을 저해하므로, 추상적인 수준의 메시지를 수신하도록 설계

  2. 최소 인터페이스

    • 외부에서 사용할 필요가 없는 인터페이스는 최대한 은닉

  3. 인터페이스와 구현의 분리

    • 인터페이스: 객체가 수행해야 할 책임의 목록(외부 공개)

    • 구현: 책임을 수행하기 위한 내부 동작 및 상태(내부 은닉)

    • 변경될 가능성이 높은 구현 부분은 내부에 감추고, 안정적인 인터페이스만 노출

역할(Role)

역할은 객체가 특정한 협력 안에서 수행하는 책임의 집합이다.

  • 동일한 역할을 수행한다면, 이들을 포괄하는 추상적인 개념으로 역할을 정의 가능

  • 이를 확장하여 객체지향 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념이 됨

협력의 추상

역할을 사용하면 구체적인 객체가 아니라 추상적인 개념에 의존하게 되므로 협력을 단순화하고 유연하게 만들 수 있다.

  • 구체적인 객체 대신 역할 사용

  • 어떠한 역할이 있다면 그 역할을 수행하는 모든 객체가 협력에 참여 가능

  • 이를 통해 다양한 객체들이 동일한 협력에 참여

대체 가능성

역할은 협력 안에서 구체적인 객체로 대체될 수 있는 추상적인 협력자이므로, 다른 객체에 의해 대체 가능함을 의미한다.

  • 역할을 대체하는 객체는 해당 역할이 가진 모든 책임을 수행 가능

  • 대체하는 객체는 역할이 요구하는 책임 외에 다른 책임을 가질 수 있음

  • 역할의 대체 가능성은 행위의 호환성을 의미하며, 이는 동일한 인터페이스를 구현하는 것으로 기술

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

많은 개발자가 데이터(DB 스키마)를 먼저 고민하고 객체를 설계하지만, 데이터는 객체가 행위를 수행하는 데 필요한 재료일 뿐이다.

  • 올바른 설계 순서

    • 객체가 가져야 할 상태나 데이터가 아니라, 객체가 참여해야 할 협력을 먼저 결정

    • 협력이라는 문맥이 객체의 행동을 결정하고, 행동이 객체의 상태를 결정

  • 데이터 중심 설계의 문제점

    • 객체의 행동보다 데이터를 먼저 결정하면 객체의 구현이 인터페이스에 노출

    • 캡슐화를 위반하고 객체 간의 결합도를 높이는 원인

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

올바른 객체지향 설계는 적절한 책임을 적절한 객체에게 할당하는 과정이다.

  1. 협력이라는 문맥 안에서 객체가 수행해야 할 책임을 먼저 식별

  2. 책임을 수행할 적절한 객체를 선택 및 생성

  3. 책임을 완수하기 위해 필요한 행동(인터페이스)을 정의

  4. 행동을 수행하는 데 필요한 데이터(상태)를 결정

  5. 를 클래스와 메서드로 구현

객체지향 설계 기법

역할, 책임, 협력 관점에서 애플리케이션을 설계하기 위해 주로 세 가지 기법이 사용된다.

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

시스템의 기능을 더 작은 규모의 책임으로 분할하고, 이를 수행할 적절한 객체에게 할당하는 하향식 설계 방식이다.

  • 설계 흐름

    • What(무엇을 해야 하는가)을 먼저 결정하고 Who(누가 할 것인가)를 나중에 결정

    • 메시지를 먼저 결정하고 그 메시지를 수신할 객체를 선택

  • 장점

    • 객체의 내부 구현을 먼저 고민하지 않으므로 캡슐화가 자연스럽게 이루어짐

    • 메시지 송신자는 수신자의 구체적인 타입을 알 필요가 없으므로 결합도가 낮아짐

디자인 패턴(Design Pattern)

책임-주도 설계의 결과를 패턴화하여 정리한 것으로, 반복적으로 발생하는 문제 상황에서 역할, 책임, 협력을 어떻게 구성하는 것이 좋은지에 대한 모범 답안이다.

  • 처음부터 패턴을 강제로 적용하는 것이 아니라, 책임 주도 설계를 진행하다가 특정 문제 상황에 직면했을 때 적절한 패턴 참고

  • 각 패턴은 특정한 협력 방식을 제안하며, 이를 통해 설계를 빠르고 안정적으로 진행할 수 있음

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

TDD는 단순한 테스트 기법이 아니라 객체지향 설계를 위한 강력한 도구로, 테스트 코드를 통해 역할, 책임, 협력을 명확히 정의하고 검증하는 방법론이다.

  • 테스트 코드를 먼저 작성하기 위해 테스트 대상 객체가 수행해야 할 행동(책임)과 결과, 그리고 협력하는 객체를 먼저 고민

  • 코드를 작성하기 전에 역할, 책임, 협력을 먼저 생각하게 강제

참고자료

Last updated