- 중요한 것은 개별 객체가 아니라 객체들 사이에 이뤄지는 협력이다. 객체지향 설계의 전체적인 품질을 결정하는 것은 개별 객체의 품질이 아니라 여러 객체들이 모여 이뤄내는 협력의 품질이다.
- 객체의 모양을 빚는 것은 객체가 참여하는 협력이다. 어떤 협력에 참여하는지가 객체에 필요한 행동을 결정하고, 필요한 행동이 객체의 상태를 결정한다. 개별적인 객체의 행동이나 상태가 아니라 객체들 간의 협력에 집중하라
책임
- 어떤 대상에 대한 요청은 그 대상이 요청을 처리할 책임이 있음을 암시
- 책임은 객체지향 설계의 가장 중요한 재료다.
- 크레이그 라만(Craig Larman)은
객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것
이라고 말한다. - 책임을 어떻게 구현할 것인가 하는 문제는 객체와 책임이 제자리를 잡은 후에 고려해도 늦지 않다
설계를 시작하는 초반에는 어떤 객체가 어떤 책임을 가지고 어떤 방식으로 서로 협력해야 하는지에 대한 개요를 아는 것만으로도 충분하다. 책임과 협력의 구조가 자리를 잡기 전까지는 책임을 구현하는 방법에 대한 고민은 잠시 뒤로 미루는 것이 좋다. 어떤 클래스가 필요하고 어떤 메서드를 포함해야 하는지를 결정하는 것은 책임과 메시지에 대한 대략적인 윤곽을 잡은 후에 시작해도 늦지 않음
책임의 분류
- 책임은 객체에 의해 정의되는 응집도 있는 행위의 집합으로, 객체가 알아야 하는 정보와 객체가 수행할 수 있는 행위에 대해 개략적으로 서술한 문장임
- 객체의 책임 = ‘객체가 무엇을 알고 있는가’(knowing) , ‘무엇을 할 수 있는가’(doing)
- 객체의 책임을 이야기할 때는 일반적으로 외부에서 접근 가능한 공용 서비스의 관점에서 이야기함
- 즉, 책임은 객체의 외부에 제공해 줄 수 있는 정보(아는 것의 측면)와 외부에 제공해 줄 수 있는 서비스(하는 것의 측면)의 목록임
- 따라서 책임은 객체의 공용 인터페이스(public interface)를 구성함
역할
- 역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다
- 협력 안에서 역할은 “이 자리는 해당 역할을 수행할 수 있는 어떤 객체라도 대신할 수 있습니다”라고 말하는 것과 같다. ( = 대체 가능성 → 행동이 호환돼야 함)
- 동일한 역할을 수행하는 객체들이 동일한 메시지를 수신할 수 있기 때문에 동일한 책임을 수행할 수 있다는 것은 매우 중요한 개념임
역할의 개념을 사용하면
유사한 협력을 추상화
해서 인지 과부화를 줄일 수 있다.
또한 다양한 객체들이 협력에 참여할 수 있기 때문에 협력이 좀 더 유연해지며 다양한 객체들이 동일한 협력에 참여할 수 있기 때문에 재사용성이 높아진다.
역할은 객체지향 설계의 단순성(simplicity), 유연성(flexibility), 재사용성(reusability)을 뒷받침하는 핵심 개념이다.- 엘리스의 이야기에서 아래 세가지 협력은 모두 동일한 순서에 따라 메시지를 주고받으며 상호작용함 ⇒ 구체적인 객체를 추상적인 역할로 대체함으로 (판사 - 하얀 토끼 - 증인) 하나의 추상적인 협력으로 대체할 수 있음
- 왕- 하얀토끼 - 모자 장수 의 협력
- 왕 - 하얀토끼 - 요리사 의 협력
- 여왕 - 하얀토끼 - 엘리스 의 협력
객체의 모양을 결정하는 협력
흔한 오류
- 객체지향 입문자들이 데이터나 클래스를 중심으로 애플리케이션을 설계하는 이유는 협력이라는 문맥을 고려하지 않고 각 객체를 독립적으로 바라보기 때문임
- 처음에는 전형적인 왕의 모습을 빌려 소프트웨어 객체를 창조하는 것이 합리적이고 적절해 보일지 모르지만 실제로 동작하는 애플리케이션을 구축하기 위해서는 왕이 참여하는 협력을 우선적으로 고려해야 함
- 객체를 협력이라는 문맥에서 떼어놓은 채 섬으로 바라보지 말라.
협력을 따라 흐르는 객체의 책임
- 객체의 행위에 초점을 맞추기 위해서는 협력이라는 실행 문맥 안에서 책임을 분배해야 한다
- 각 객체가 가져야 하는 상태와 행위에 대해 고민하기 전에 그 객체가 참여할 문맥인 협력을 정의하라
- 객체지향 시스템에서 가장 중요한 것은 충분히 자율적인 동시에 충분히 협력적인 객체를 창조하는 것이다.
객체지향 설계 기법
책임-주도 설계
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
- 시스템 책임을 더 작은 책임으로 분할
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당
- 객체가 책임을 수행하는 중에 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함 으로써 두 객체가 협력하게 한다.
역할, 책임, 협력은 유연하고 견고한 객체지향 시스템을 만드는 데 필요한 가장 중요한 재료다. 그 외의 장치는 단지 역할, 책임, 협력을 보완하고 애플리케이션의 복잡도를 줄이기 위해 필요한 보조 재료일 뿐이다.
디자인 패턴
책임-주도 설계는 객체의 역할, 책임, 협력을 고안하기 위한 방법과 절차를 제시한다.
반면 디자인 패턴은 책임-주도 설계의 결과를 표현한다. 패턴은 모범이 되는 설계다
- 앨러스터 코오번에 따르면 효과적으로 일하는 사람들의 한 가지 특징은 아무것도 없는 상태에서 작업을 시작하지 않고 이전의 훌륭한 결과물을 모방하고 약간의 수정을 거쳐 원하는 결과물을 만들어 낸다는 것이다[Cockburn 2001]
- 디자인 패턴은 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿이다. 만약 특정한 상황에 적용 가능한 디자인 패턴을 잘 알고 있다면 책임-주도 설계의 절차를 순차적으로 따르지 않고도 시스템 안에 구현할 객체들의 역할과 책임, 협력 관계를 빠르고 손쉽게 포착할 수 있을 것이다.
- 디자인 패턴은 책임-주도 설계의 결과물인 동시에 지름길이다
테스트 주도 개발
- 기본 흐름
- 실패하는 테스트를 작성하고
- 테스트를 통과하는 가장 간단한 코드를 작성한 후 ( 이 시간 동안에는 중복이 있어도 무방함)
- 리팩토링을 통해 중복을 제거
- 테스트-주도 개발은 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각하라고 충고한다. 그러나 이 같은 종류의 충고는 역할, 책임, 협력의 관점에서 객체를 바라보지 않을 경우 무의미하다.
- 테스트-주도 개발은 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것임
- 테스트-주도 개발은 책임-주도 설계를 통해 도달해야 하는 목적지를 테스트라는 안전장치를 통해 좀 더 빠르고 견고한 방법으로 도달할 수 있도록 해주는 최상의 설계 프랙티스다.
- 테스트-주도 개발은 책임-주도 설계의 기본 개념과 다양한 원칙과 프랙티스, 패턴을 종합적으로 이해하고 좋은 설계에 대한 감각과 경험을 길러야만 적용할 수 있는 설계 기법이다. 역할, 책임, 협력에 집중하고 객체지향의 원칙을 적용하려는 깊이 있는 고민과 노력을 통해서만 테스트-주도 개발의 혜택을 누릴 수 있다