4. 역할, 책임, 협력
협력
- 요청하고 응답하며 협력하는 사람들
- 협력은 다른사람에게 요청을 할 때 시작되며, 응답으로 마무리 된다.
객체지향의 세계는 동일한 목적을 위해 협력하는 객체들의 공동체이다.
재판 속의 협력 장면을 한 번 들여다보자. 왕, 하얀토끼, 모자 장수 등 다양한 인물들은 재판이라는 동일한 목적을 위해 협력하고 있다.
- 누군가 왕에게 재판을 요청
- 왕이 토끼에게 증인을 부르라 요청
- 토끼는 모자장수에게 증인석으로 입장할 것으로 요청
- 모자장수는 증인석에 입장함으로써 응답, 토끼장수는 이로써 왕의 요청에 응답
- 왕은 모자장수에게 증언을 요청
- 모자장수는 증언을 함으로써 응답
요청은 책임을 정의한다.
- 왕이 재판을 요청 받은 이유는, 재판을 수행할 의무가 있고 이에 대한 지식이 있어서다.
- 토끼가 요청받은 이유는, 목격자에 대해 알고 있으며 부를 의무가 있어서다.
- 모자장수가 요청받은 이유는, 사건에 대해 알고 있으며 증언할 의무가 있어서다.
즉, 요청을 처리할 수 있는 능력과 지식이 있기 때문에 요청을 받은 것
책임
어떤 객체가 요청에 대해 대답해줄 수 있거나 적절한 행동을 할 의무가 있는 경우 해당 객체가 책임을 가진다고 말한다.
- 책임을 어떻게 구현할 것인가 하는 문제는 객체와 책임이 제자리를 잡은 후에 고려해도 늦지 않다.
- 책임은 객체가 '무엇을 알고 있는가(knowing)'과 '무엇을 할 수 있는가(doing)'로 구성된다
- 하는 것
- 객체를 생성하거나 계산을 하는 등의 스스로 하는 것
- 다른 객체의 행동을 시작시키는 것
- 다른 객체의 활동을 제어하고 조절하는 것
- 아는 것
- 개인적인 정보에 관해 아는 것
- 관련된 객체에 대해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
책임은 객체지향 설계의 품질을 결정하는 가장 중요한 요소로, 좋은 객체지향은 적절한 객체에게 적절한 책임을 할당하는 데 있다. 객체의 외부에 제공해 줄 수 있는 정보(아는 것)와 줄 수 있는 서비스(하는 것)으로 구성되어 있다.
- 책임은 객체의 공용 인터페이스를 구성한다
- 메시지 송수신이 가능하면 상호 협력 관계가 형성된다
✨
객체지향 설계는 협력에 참여하기 위해 어떤 객체가 어떤 책임을 수행해야 하고 어떤 객체로부터 메시지를 수신할 것인지를 결정하는 것으로부터 시작된다. 어떤 클래스가 필요하고 어떤 메서드를 포함해야 하는지를 결정하는 것은 책임과 메시지에 대한 대략적인 윤곽을 잡은 후에 시작해도 늦지 않다.
역할
책임의 집합이 의미하는 것
단순성 + 유연성 + 재사용성
- 어떤 객체가 수행하는 책임의 집합은 객체가 협력 안에서 수행하는 역할을 암시한다
이제 다시 앨리스가 증언하는 이야기로 돌아가보자
왕은 여왕에게 재판을 대신해줄 것을 요청했고, 증언을 모자장수가 아닌 앨리스에게 할 것으로 요청했다.
왕 → 여왕 / 모자장수 → 앨리스
등장인물만 바뀌었을 뿐이지 재판 과정은 동일하다. 즉 역할이 답이다.
우리는 역할(role)로서 협력을 추상화할 수 있다. 해당 역할을 할 수 있는 어떤 객체라도 대신할 수 있다. 다만 역할이 수신할 수 있는 동일한 메시지를 이해할 수 있는 객체뿐이다.
- 누군가 판사에게 재판을 요청
- 판사이 토끼에게 증인을 부르라 요청
- 토끼는 증인에게 증인석으로 입장할 것으로 요청
- 증인은 증인석에 입장함으로써 응답, 토끼장수는 이로써 판사의 요청에 응답
- 판사는 증인에게 증언을 요청
- 증인은 증언을 함으로써 응답
협력의 추상화
역할의 가장 큰 가치는 하나의 협력 안에서 여러 종류의 객체가 참여할 수 있게 함으로써 협력을 추상화할 수 있다는 것이다. 구체적인 객체를 역할로 대체함으로써 단순화한다.
대체 가능성
역할은 협력 안에서 구체적인 객체로 대체될 수 있는 추상적인 협력자다. 객체가 역할을 대체하기 위해선 역할의 행동(동일한 책임)을 그대로 할 수 있어야 한다
흔한 오류
- 데이터는 단지 객체가 행위를 수행하는 데 필요한 재료일 뿐이다.
- 객체가 존재하는 이유는 행위를 수행하며 협력에 참여하기 위해서다.
- 클래스는 단지 시스템에 필요한 객체를 표현하고 생성하기 위한 것일 뿐이다.
- 객체지향의 핵심은 클래스를 어떻게 구현할 것인가가 아니라 객체가 협력 안에서 어떤 책임과 역할을 수행할 것인지를 결정하는 것이다.
협력을 따라 흐르는 객체의 책임
- 객체들이 주고받을 요청과 응답의 흐름을 결정할 깔끔한 협력을 설계해야 한다.
- 객체에게 책임을 할당하고 나면 책임은 객체가 외부에게 제공하게 될 행동이 된다.
- 행동을 결정한 후에 그 행동을 수행하는 데 필요한 데이터를 고민해야 한다.
- 필요한 데이터와 행동이 어느 정도 결정된 후에 클래스의 구현 방법을 결정해야 한다.
객체지향 설계 기법
책임-주도 설계
- 협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다
- 시스템 책임을 더 작은 책임으로 분할한다
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다
- 다른 객체의 도움이 필요한 경우, 객체 또는 역할에게 책임을 할당함으로써 협력하게 한다.
디자인 패턴
- 반복적으로 사용하는 해결 방법을 정의 해놓은 설계 템플릿 모음
테스트-주도 개발
- 테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하면서 애플리케이션 완성
- 리팩터링을 통해 중복을 제거하는 깔끔한 코드를 얻을 수 있다
- 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각해야 한다.
- 응집도가 높고 결합도가 낮은 클래스로 구성된 시스템을 개발할 수 있게 하는 최상의 프랙티스