개요
객체지향의 본질은 협력하는 객체들의 공동체를 참조하는 것이다.
객체지향의 설계의 핵심은 협력을 구성하기 위해 적절한 객체를 찾고 적절한 책임을 할당하는 과정에서 드러난다. 애플리케이션의 기능을 구현하기 위해서 어떤 협력이 필요하고 협력을 위해 어떤 역할과 책임이 필요한지를 고민하지 않은 채 너무 이른 시기에 구현체 초점을 맞추는 것은 변경하기 어렵고 유연하지 못한 코드를 낳는 원인이 된다.
객체지향 패러다임의 관점인 역할(role), 책임(responsibility), 협력(collaboration)에 대해서 알아보자.
협력
객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용
협력은 객체지향 세계에서 기능을 구현할 수 있는 유일한 방법이다. 객체 자체는 자율적으로 동작하기 때문이다. 자율적인 객체는 자신에게 할당된 책임을 수행하는데에 필요한 정보를 알지 못하거나 외부의 도움이 필요한 경우 적절한 객체에게 도움을 요청한다. 이렇게 도움을 요청하는 수단을 메시지 전송(message sending) 이라고 부른다. 이러한 관계를 협력 관계라고 한다.
메시지를 수신한 객체는 메서드를 실행해서 요청에 응답하고, 또 다시 다른 객체의 도움이 필요하다면 메시지를 전송해서 협력한다. 이 처럼 애플리케이션 안에 어떤 객체가 필요하다면 객체가 협력에 참여하고 있다는 것이고, 이는 객체가 협력에 참여하기 위한 어떠한 행동을 보유하고 있다는 것을 의미한다.
즉, 객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력(어떠한 처리해야하는 메시지)에 의해서 결정되고, 객체의 상태는 그 객체가 어떠한 행동을 수행하는데에 필요한 정보로 결정된다.
책임
객체가 협력에 참여하기 위해 수행하는 로직(행동)
객체지향 설계에서 가장 중요한 것은 책임이다. 얼마나 적절하게 책임을 할당하느냐에 따라서 설계의 전체적인 품질이 결정된다. 따라서, 구현 방법은 책임을 적절하게 할당한 이후에 고민해도 늦지 않는다.
책임은 분류체계에 따라 책임을 '하는 것(doing)' 과 '아는 것(knowing)' 으로 분류한다.
하는 것
- 객체를 생성하거나 계산을 수행하는 등의 스스로 하는 것
- 다른 객체의 행동을 시작시키는 것
- 다른 객체의 활동을 제어하고 조절하는 것
아는 것
- 사적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
협력 안에서 객체에게 할당한 책임이 외부의 인터페이스와 내부의 속성을 결정한다. 메시지와 책임을 구분했을 때, 책임은 객체가 수행할 수 있는 행동을 종합적이고 간략하게 서술하기 때문에 메시지보다 추상적이다. 즉, 어떠한 큰 책임을 수행하기 위해서 다양한 메시지로 분할 되는 것이다.
자율적인 객체로 만들기 위한 가장 기본적인 방법은 책임을 수행하는 데 필요한 정보를 가장 잘 알고있는 전문가에게 그 책임을 할당하는 것이다. 이를 정보 전문가(Information Expert) 패턴 이라고 한다.
따라서 객체에게 책임을 할당하기 위해서는 먼저 협력이라는 문맥을 정의해야 한다. 즉, 메시지가 어떠한 책임을 결정한다고 봐도 좋다. 제일 먼저 시스템이 사용자에게 제공하는 기능을 시스템이 담당할 하나의 책임으로 보고, 이러한 큰 책임을 더 작은 책임으로 구체화하여 객체에게 적절하게 할당해야 한다. 책임을 할당할 때 고려해야 할점은 메시지가 객체를 결정하고, 행동이 상태를 결정한다는 것을 고려해야 한다.
메시지가 객체를 결정하도록 하는 이유
1. 객체가 최소한의 인터페이스를 가질 수 있게 된다.
- 메시지가 식별될 때까지 객체의 퍼블릭 인터페이스에 어떤 것도 추가되지 않기 때문이다.
2. 객체는 충분히 추상적인 인터페이스를 가질 수 있게 된다.
- 객체의 인터페이스는 무엇을 하는지는 표현해야 하지만 어떻게 수행하는지는 노출하면 안되는데, 무엇을 수행할지에 초점을 맞출 수 있다.
책임 주도 설계(Responsibility-Driven Design, RDD)
위처럼 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 것을 책임 주도 설계라고 부른다. 책임주도 설계 방법의 과정은 다음과 같다.
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
- 시스템 책임을 더 작은 책임으로 분할
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임 할당
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할 탐색
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력
이렇게 위처럼 객체의 구현이 아닌 책임에 집중한다면 유연하고 견고한 객체지향 시스템을 만들 수 있다.
역할
객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할
역할이 중요한 이유는 역할을 통해서 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다. 현실 세계에 대한 예시로 배우와 배역이 있다고 했을 때, 배역은 고정이지만 배우는 달라질 수 있다. 그리고 배우에 따라 배역에 대한 대본은 같을지 언정 연기는 달라질 수 있다. 이처럼 역할을 정해놓으면 유연한 교체가 가능해진다. 이 처럼 역할은 추상화라는 관점에서 바라볼 수 있다.
역할의 구현
위에서 추상화라는 말이 나온것처럼 역할을 구현하기 위한 가장 간단한 방법은 추상 클래스와 인터페이스를 사용하는 것이다. 협력의 관점에서 봤을 때, 추상 클래스와 인터페이스는 책임의 집합을 서술한 것이다. 추상 클래스는 책임의 일부를 구현 해놓은 것이고, 인터페이스는 구현 없이 책임의 집합만을 나열한 것이다. (물론 default를 통해 구현도 가능하다.)
이렇듯 역할은 간단하게 객체가 참여 할 수 있는 어떠한 슬롯이라고 생각해도 좋다. 하지만 협력에 참여하는 후보가 한 개만 존재 할 경우에는 역할을 생각하지 않고, 객체 관점으로만 바라보는 것이 좋다.
객체는 다양한 역할을 가질 수 있고, 협력에 참여할 때 협력 안에서 하나의 역할로 보여진다. 스타크래프트라는 게임의 관점으로 본다면 Marine 객체는 지상 유닛이라는 역할도 가질 수 있으며 치료 가능이라는 역할도 가질 수 있다. 그리고 해당 역할들은 협력에 참여할 때 협력 안에서 하나의 역할로 보여진다. 만약 다른 협력에 참여한다면 또 다른 역할로 보여질 것이다. 지상에서 이동한다면 그저 지상 유닛이라는 역할로써 보일 것이고, 치료를 한다면 치료 가능 이라는 역할로써 보일 것이다.
위처럼 역할은 특정한 객체의 종류를 캡슐화하기 때문에 동일한 역할을 수행할 수 있고, 이러한 객체들은 다형적이다.
느낀점
이전에 객체지향의 사실과 오해에서도 협력, 책임, 역할에 대해서 다루었지만 약간 모호했다면 오브젝트에서는 확실히 객체지향 적으로 올바르게 설계 된 코드를 보고 읽으니 더욱 와닿는다는 느낌이 들었다. 실제로 협력, 책임, 역할에 초점을 잡으면서 올바른 설계를 위해 클린코드, TDD 과제를 진행중인데 생각보다 쉽지 않은 것 같고, 해당 과제를 여신 시니어 개발자님도 굉장히 어렵다고 말씀하셨다! 그래도 조금씩 보인다는 것에 초점을 두자.
'도서 > 오브젝트' 카테고리의 다른 글
4. 설계 품질과 트레이드 오프 (0) | 2023.06.04 |
---|---|
2. 객체지향 프로그래밍 (0) | 2023.05.24 |
1. 객체, 설계 (0) | 2023.05.18 |