객체의 세계에서 객체의 행동을 결정하는 개별 객체의 특성이 아닌 협력이다.
→ 결국 객체들은 하나의 공통된 목표를 위해 협력하는 것들이기 때문에, 협력을 하는 과정에서 어떻게 요청을 보내고 어떻게 요청에 응답할지가 중요하다.
→ 따라서, 객체가 어떤 협력에 참여하는지가 중요하고, 그 협력이 객체의 행동을 결정하고, 객체의 행동이 객체의 상태와 특성을 결정한다.
협력
요청하고 응답하며 협력하는 사람들
✔️ 협력이라는 것은 특정 사람이 누군가에게 도움을 요청하면서 시작된다.
✔️ 도움을 요청받은 사람은 요청을 한 사람에게 자신의 지식을 공유하고 서비스를 제공한다.
→ 협력은 다수의 요청과 다수의 응답으로 구성되고 이것들은 연쇄적이다.
example)
- 손님이 카페의 사장에게 돈을 지불하며 아메리카노 한잔을 요청한다.
- 사장은 알바생에게 아메리카노 한잔 내릴 것을 요청한다.
- 알바생은 버튼을 눌러 커피머신에게 커피를 내려줄 것을 요청한다.
- 커피머신은 어떠한 작업을 통해 커피를 내려 알바생의 요청에 응답한다.
- 알바생은 커피머신이 만든 아메리카노 한잔을 사장에게 전해주며 사장의 요청에 응답한다.
- 사장은 아메리카노를 전해주며 손님의 요청에 응답한다.
→ 각자의 적절한 행동을 통해 요청에 응답하고, 이 과정은 연쇄적이다.
책임
✔️ 어떤 객체가 어떤 요청에 응답할 수 있거나, 행동할 의무가 있는 경우에 해당 객체가 책임을 가진다고 한다.
위의 아메리카노 예를 보면 요청에 응답할 수 있거나, 의무가 있는 객체들이 행동을 통해 자신의 책임을 수행한다.
💡 따라서, 어떤 객체에게 요청하는 것은 해당 객체가 요청에 대한 책임이 있음을 암시한다.
✔️책임은 객체지향에서 가장 중요한 재료
→ 책임을 어떤 방식의 행동으로 수행할지는 객체와 책임이 설계된 후에 생각해도 된다.
책임의 분류
협력하는 객체들은 공통의 목표를 위해 필요한 책임을 수행한다.
✔️객체의 책임은 협력 속에서 '객체가 아는 것'과 '객체가 수행할 수 있는 것'으로 나뉜다.
크레이그 라만의 객체의 책임에 대한 분류 체계
하는 것 ( doing )
- 객체를 생성하거나 계산하는 등의 스스로 하는 것
- 다른 객체의 행동을 시작시키는 것
- 다른 객체의 활동을 제어하고 조절하는 것
아는 것 ( knowing )
- 개인적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
example)
- 사장이 알바생에게 커피머신이 커피를 내리도록 하라고 지시하는 것 - 하는 것 3번째
- 알바생은 커피머신으로 커피를 내리는 것을 알고 있다 - 아는 것 2번째
알바생은 커피머신의 버튼을 눌러 커피머신이 커피를 내리게 한다 - 하는 것 2번째
- 커피머신은 내부적으로 어떻게 커피를 만드는지 안다 ( 설계되있겠지만 ) - 아는 것 3번째
커피머신은 아메리카노라는 객체를 생성한다 - 하는 것 1번째
💡 이렇게 명확한 책임이 객체지향에서의 핵심이자 품질을 결정하는 요소이다.
✔️ 객체의 책임은 외부에서 접근 가능한 공용 서비스의 관점에서 얘기된다.
즉, 책임은 외부에 제공해 줄 수 있는 아는 것과 외부에 제공해 줄 수 있는 서비스로 구성된다.
→ 결과적으로 책임은 객체의 공용인터페이스르 구성한다. 어떤 것을 외부에 제공할 지를 구성한다는 것이다.
책임과 메세지
✔️ 협력 속의 객체는 다른 객체에게서 요청을 수신했을 때만 책임에 대한 행동을 수행한다.
ex) 알바생은 사장에게서 아메리카노 한잔이라는 요청을 받았을 때만, 커피머신의 버튼을 클릭한다.
✔️ 다른 객체에게 주어친 책임을 수행하도록 요청하는 것을 메시지 전송이라고 한다.
→ 객체 간의 협력의 유일한 방법은 메시지
송신자 : 메시지를 전송하며 협력을 요청하는 객체
수신자 : 메시지를 수신하며 요청을 처리하는 객체
✔️ 메시지는 협력에 참여하는 두 객체 사이의 관계를 강조한 것
→ 메시지는 수신자와 송신자의 상호 협력이라는 관계의 맥락을 강조한다.
ex) 알바생이 사장의 아메리카노 한잔 요청 메시지에 응답할 수 있는 것은 사장은 알바생이 이해할 수 있는 메시지를 전송할 수 있고, 알바생은 사장이 전송하는 메시지에 대한 적절한 책임을 수행할 수 있기 때문에 상호협력 가능
❗️책임과 메시지의 수준은 같지 않다
책임은 객체가 협력에 있어 수행해야하는 행위를 상위 수준에서 개략적으로 서술한 것
✔️책임을 결정하고 실제로 정제하면서 이를 메시지로 변환할 때는 하나의 책임이 여러 메시지로 분할
→ 하나의 책임을 위해서 여러 가지 요청들이 발생할 수 있고, 책임이 다른 책임을 발생시키면서 또 다른 요청들이 발생할 수 있다는 뜻으로 이해
역할
책임의 집합이 의미하는 것
✔️ 책임의 집합은 협력 속에서 객체가 수행하는 역할을 암시한다.
ex) 알바생은 사장에게 요청을 받아 커피 머신으로 하여금 커피를 내리도록 하는 책임이 있는데 이것은 협력 속에서 알바생이 커피머신과 사장의 중간 다리 역할을 한다.
역할이 답이다
위의 예에서 사장이 아파 출근을 못하고 사장의 부인이 출근했다고 가정해보자. 사장 부인이 해당 역할을 수행할 수 있는 능력이 있다면 협력은 아무런 문제가 되지 않는다.
또한 오늘은 어제와 다른 알바생이 출근했다고 가정해보자. 오늘의 알바생도 역시 역할을 수행할 수 있으니 협력에 문제는 없다.
또, 커피머신이 A회사의 기계에서 B회사의 기계로 바꼈다고 생각해보자. 아무런 문제가 없다.
→ 협력 속에서의 역할은 해당 역할을 수행할 수 있는 어떤 객체라도 대신 할 수 있다.
또한 사장의 역할을 '고객주문을 받는 역할', 알바생의 역할을 '고객주문을 전달받아 커피머신을 가동시키는 역할', 커피머신의 역할을 ' 커피를 만드는 역할'이라고 추상화할 수 있다.
💡 해당 역할을 수행할 수 있다고 하는 것의 의미
→ 각 역할이 수신하는 메시지를 동일한 방식으로 이해해야 하고 처리할 수 있어야 한다. 더불어, 누군가에게 동일한 방식으로 메시지를 전송하며 요청할 수 있어야한다.
✔️결국, 동일한 역할을 수행할 수 있다는 것은 협력내에서 동일한 책임의 집합을 수행할 수 있다는 것이다.
역할이라는 개념을 사용했을 때의 장점
✔️ 단순성 : 유사한 협력을 추상화해 인지 과부하 ⬇️
✔️ 유연성 : 다양한 객체들이 협력에 참여할 수 있기 때문에 협력의 유연성 ⬆️
✔️ 재사용성 : 다양한 객체들이 동일한 협력에 참여할 수 있기 때문에 재사용성 ⬆️
협력의 추상화
✔️ 역할의 가장 큰 가치는 하나의 협력 속에 역할을 수행할 수 있는 여러 객체들이 참여할 수 있게 함으로써 협력을 추상화할 수 있다는 것이다.
→ 설계자가 다뤄야하는 협력의 개수를 줄이고 구체적인 객체들을 추상화함으로써 협력의 양상을 단순화 가능
✔️ 추상화된 역할을 구체적인 객체로 대체하여 동일한 구조의 협력을 수행하도록, 여러 가지 문맥에서 재사용하게 할 수 있다.
대체 가능성
✔️ 역할은 협력에서 구체적인 객체로 대체될 수 있는 추상적인 협력자
→ 본질적인 역할은 다른 객체에 의해 대체 가능하고, 내 생각에는 동일한 협력의 구조라도 여러 구체적인 문맥들에서 협력의 구조가 사용되기 때문에 구체적인 객체로 반드시 대체되어야 한다고 생각한다.
✔️객체가 역할을 대체하기 위해서는 행동이 호환되어야 한다.
위의 예에서 오늘의 알바생이 어제의 알바생을 대체할 수 있는 이유는 둘 다 사장의 요청을 받아 커피머신의 버튼을 눌러 커피머신으로 하여금, 커피를 만들도록 하는 행동을 할 수 있기 때문이다.
→ 객체가 역할을 대체하기 위해서는 협력 안에서 역할이 수행하는 모든 책임을 동일하게 수행할 수 있어야 한다.
객체의 타입과 역할 - 일반화/특수화 관계
객체는 어떤 역할의 책임을 수행할 수 있지만, 다른 역할의 책임도 수행할 수 있다.
ex ) 알바생은 카페에서의 자신의 역할을 수행할 수 있지만, 학생으로써 공부라는 본질적인 역할을 수행할 수 있다.
→ 따라서 객체의 타입은 역할에 비해 일반적이고, 역할은 조금 더 구체적이고 특수하다.
객체의 모양을 결정하는 협력
흔한 객체지향의 오류
- 객체는 시스템에서 필요로 되는 데이터를 저장하기 위해서 존재한다.
→ 객체는 행동하며 협력에 일조하기 위해서 존재하는 것이고, 데이터는 객체가 행동할 때의 단순 재료이다.
- 객체지향의 중점은 클래스와 클래스 간의 관계를 표현하는 시스템의 정적인 측면이다.
→ 객체지향의 핵심은 객체가 협력 안에서 어떤 책임과 역할을 수행할 것인지를 결정하는 것
협력을 따라 흐르는 객체의 책임
✔️올바른 객체 설계를 위해서는
1. 견고하고 깔끔한 협력의 설계
→ 협력을 설계한다는 것은 객체들이 주고받을 요청과 응답의 흐름을 결정한다는 것, 이것들이 결정되면 각 객체들에게 책임이 할당된다.
2. 객체가 책임을 수행하기 위해 할 행동 결정
3. 객체가 행동을 수행하는데 필요로 할 데이터 결정
4. 클래스 구현 방법 결정
💡 객체의 자율성도 중요하지만 먼저 객체를 협력이라는 문맥 속에 두고, 그 안에서 충분히 자율적인 존재로 만들어주어야한다.
객체지향 설계 기법
✔️ 객체지향 시스템 개발의 목적은 사용자의 요구를 만족시킬 수 있는 기능을 제공하는 동시에, 이해하기 쉽고, 단순하며, 유연한 상호작용을 제공하는 역할과 책임을 수행하는 자율적인 객체들의 공동체를 구축하는 것이다.
✔️ 결국, 객체지향 설계란 기능을 구현하기 위한 협력 관계를 고안 → 협력에 필요한 역할과 책임을 식별 → 이를 수행할 수 있는 적절한 객체를 식별
1. 책임 - 주도 설계
✔️ 말 그래도 객체의 책임을 중심으로 시스템을 구축하는 설계 방법
- 시스템의 기능은 더 작은 규모의 책임들로 분할됨
- 각 책임들은 이를 적절히 수행할 수 있는 객체들에게 할당됨
- 모르는 정보나 스스로 처리할 수 있는 기능이 있다면 도움을 줄 수 있는 적절한 다른 객체에 필요한 작업 요청
→ 객체들 간의 협력 관계 생성
- 책임을 여러 객체가 수행할 수 있다면 이는 객체가 아닌 추상적인 역할로 대체
→ 결국, 시스템은 자신의 책임을 수행할 수 있을만큼 자율적이고 다른 객체와 협력적인 객체들로 구성된다.
2. 디자인 패턴
✔️ 책임-주도 설계의 결과물들로, 유사한 상황에서 반복적으로 적용할 수 있는 역할, 책임, 협력에 대한 템플릿
→ 책임 - 주도 설계를 하면서 반복적으로 발생하는 특정 문제와 그 문제에 대한 해답들이 적립되어 어떠한 패턴으로 정의된다. 다른 설계자는 본인의 문제를 해당 패턴으로 해결할 수 있다고 판단되면, 직접 책임-주도 설계를 처음부터 할 필요 없이 해당 디자인 패턴 구조를 이용하면 되는 것이다.
3. 테스트-주도 개발
✔️실패하는 테스트와 테스트를 통과하는 가장 간단한 코드를 작성한 후, Refactoring을 통해 중복을 제거하는 방식을 통해 작동하는 깔끔한 코드를 얻는 것
💡 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것
✔️ 책임-주도 개발의 기본 개념을 따른다
→ 협력 속에서 객체들의 책임과 역할을 알고 구현 방법에 대한 이해가 있어야 효과적인 테스트 작성이 가능하다
→ 테스트-주도 개발은 책임-주도 개발에서 테스트라는 안전장치를 통해 개발을 좀 더 견고하게 해준다.
💡 결국 객체지향의 핵심인 역할, 책임, 협력에 집중하고 객체지향의 원칙을 적용하려는 노력이 있어야만 효과적인 테스트-주도 개발을 할 수 있다.