Role & Responsibility & Cooperation
객체지향 설계의 핵심은 동적인 객체 간의 협력으로, 협력을 통해 객체의 행동(책임)이 드러나고, 역할이 정의된다.
협력(Cooperation)
섹션 제목: “협력(Cooperation)”협력은 어떤 객체가 다른 객체에게 무언가를 요청할 때 시작되며, 두 객체 사이의 요청과 응답의 연쇄적인 흐름이 모여 애플리케이션의 기능을 구현한다.
- 협력의 문맥(Context)
- 객체가 존재하는 이유는 협력에 참여를 목적으로 함
- 협력은 객체가 필요한 이유와 객체가 수행해야 할 행동의 문맥 제공
- 요청과 응답
- 협력은 다수의 요청과 응답으로 구성
- 특정 요청을 받아들일 수 있다는 것은 그 요청을 처리할 책임이 있음을 의미
책임(Responsibility)
섹션 제목: “책임(Responsibility)”책임은 객체가 협력에 참여하기 위해 수행하는 행동이자, 객체가 유지해야 하는 정보의 집합으로, 크게 두 가지로 분류할 수 있다.
- 정보(Knowing)
- 자신의 정보
- 관련된 객체의 정보
- 자신이 유도하거나 계산할 수 있는 정보
- 주로 필드(Field)나 객체 간의 연관 관계(Link)로 구현
- 서비스(Doing)
- 객체를 생성하거나 계산을 수행하는 등 스스로 하는 것
- 다른 객체의 행동을 시작시키거나 제어하는 것
- 주로 메서드(Method) 호출로 구현
책임은 객체의 공용 인터페이스(Public Interface)를 구성하며, 이는 객체 내부의 구현을 숨기고 외부에는 약속된 동작만 노출하는 캡슐화의 기반이 된다.
인터페이스 설계 원칙
섹션 제목: “인터페이스 설계 원칙”좋은 객체지향 설계를 위해서는 인터페이스를 만들 때 다음 세 가지 원칙을 고려해야 한다.
- 추상적인 인터페이스
- 구체적인 방법(How)이 아니라 의도(What)를 명확하게 표현
- 너무 상세한 메시지는 객체의 자율성을 저해하므로, 추상적인 수준의 메시지를 수신하도록 설계
- 최소 인터페이스
- 외부에서 사용할 필요가 없는 인터페이스는 최대한 은닉
- 인터페이스와 구현의 분리
- 인터페이스: 객체가 수행해야 할 책임의 목록(외부 공개)
- 구현: 책임을 수행하기 위한 내부 동작 및 상태(내부 은닉)
- 변경될 가능성이 높은 구현 부분은 내부에 감추고, 안정적인 인터페이스만 노출
역할(Role)
섹션 제목: “역할(Role)”역할은 객체가 특정한 협력 안에서 수행하는 책임의 집합이다.
- 동일한 역할을 수행한다면, 이들을 포괄하는 추상적인 개념으로 역할을 정의 가능
- 이를 확장하여 객체지향 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념이 됨
협력의 추상
섹션 제목: “협력의 추상”역할을 사용하면 구체적인 객체가 아니라 추상적인 개념에 의존하게 되므로 협력을 단순화하고 유연하게 만들 수 있다.
- 구체적인 객체 대신 역할 사용
- 어떠한 역할이 있다면 그 역할을 수행하는 모든 객체가 협력에 참여 가능
- 이를 통해 다양한 객체들이 동일한 협력에 참여
대체 가능성
섹션 제목: “대체 가능성”역할은 협력 안에서 구체적인 객체로 대체될 수 있는 추상적인 협력자이므로, 다른 객체에 의해 대체 가능함을 의미한다.
- 역할을 대체하는 객체는 해당 역할이 가진 모든 책임을 수행 가능
- 대체하는 객체는 역할이 요구하는 책임 외에 다른 책임을 가질 수 있음
- 역할의 대체 가능성은 행위의 호환성을 의미하며, 이는 동일한 인터페이스를 구현하는 것으로 기술
객체의 모양을 결정하는 협력
섹션 제목: “객체의 모양을 결정하는 협력”많은 개발자가 데이터(DB 스키마)를 먼저 고민하고 객체를 설계하지만, 데이터는 객체가 행위를 수행하는 데 필요한 재료일 뿐이다.
- 올바른 설계 순서
- 객체가 가져야 할 상태나 데이터가 아니라, 객체가 참여해야 할 협력을 먼저 결정
- 협력이라는 문맥이 객체의 행동을 결정하고, 행동이 객체의 상태를 결정
- 데이터 중심 설계의 문제점
- 객체의 행동보다 데이터를 먼저 결정하면 객체의 구현이 인터페이스에 노출
- 캡슐화를 위반하고 객체 간의 결합도를 높이는 원인
협력을 따라 흐르는 객체의 책임
섹션 제목: “협력을 따라 흐르는 객체의 책임”올바른 객체지향 설계는 적절한 책임을 적절한 객체에게 할당하는 과정이다.
- 협력이라는 문맥 안에서 객체가 수행해야 할 책임을 먼저 식별
- 책임을 수행할 적절한 객체를 선택 및 생성
- 책임을 완수하기 위해 필요한 행동(인터페이스)을 정의
- 행동을 수행하는 데 필요한 데이터(상태)를 결정
- 를 클래스와 메서드로 구현
객체지향 설계 기법
섹션 제목: “객체지향 설계 기법”역할, 책임, 협력 관점에서 애플리케이션을 설계하기 위해 주로 세 가지 기법이 사용된다.
책임-주도 설계(Responsibility-Driven Design)
섹션 제목: “책임-주도 설계(Responsibility-Driven Design)”시스템의 기능을 더 작은 규모의 책임으로 분할하고, 이를 수행할 적절한 객체에게 할당하는 하향식 설계 방식이다.
- 설계 흐름
- What(무엇을 해야 하는가)을 먼저 결정하고 Who(누가 할 것인가)를 나중에 결정
- 메시지를 먼저 결정하고 그 메시지를 수신할 객체를 선택
- 장점
- 객체의 내부 구현을 먼저 고민하지 않으므로 캡슐화가 자연스럽게 이루어짐
- 메시지 송신자는 수신자의 구체적인 타입을 알 필요가 없으므로 결합도가 낮아짐
디자인 패턴(Design Pattern)
섹션 제목: “디자인 패턴(Design Pattern)”책임-주도 설계의 결과를 패턴화하여 정리한 것으로, 반복적으로 발생하는 문제 상황에서 역할, 책임, 협력을 어떻게 구성하는 것이 좋은지에 대한 모범 답안이다.
- 처음부터 패턴을 강제로 적용하는 것이 아니라, 책임 주도 설계를 진행하다가 특정 문제 상황에 직면했을 때 적절한 패턴 참고
- 각 패턴은 특정한 협력 방식을 제안하며, 이를 통해 설계를 빠르고 안정적으로 진행할 수 있음
테스트-주도 개발(Test-Driven Development)
섹션 제목: “테스트-주도 개발(Test-Driven Development)”TDD는 단순한 테스트 기법이 아니라 객체지향 설계를 위한 강력한 도구로, 테스트 코드를 통해 역할, 책임, 협력을 명확히 정의하고 검증하는 방법론이다.
- 테스트 코드를 먼저 작성하기 위해 테스트 대상 객체가 수행해야 할 행동(책임)과 결과, 그리고 협력하는 객체를 먼저 고민
- 코드를 작성하기 전에 역할, 책임, 협력을 먼저 생각하게 강제