본 책의 저자인 조영호님께서 쓰셨던 또 다른 책인 "객체지향 사실과 오해"라는 책은 내 생각에 불필요한 비유가 너무 많은 것 같아서 조금 읽다가 책을 덮었었는데, 이 책은 반대로 중요한 의미를 담고 있는 문장들을 밀도 있게 꾹꾹 눌러담은 느낌이었다. 같은 의미의 말을 여러 표현으로 반복되서 나오는 경향도 있었는데, 별로라는 생각이 들다가도 같은 의미의 말이지만 어떻게 표현하냐에 따라 더 와닿고, 나에게 더 큰 인사이트를 주는 경우가 있어서 충분히 많은 것을 얻은 책이라고 생각한다. 다만 책의 내용이 너무 밀도 있게 구성되어 있어서 읽다 보면 집중력이 떨어지는 면이 없잖아 있었다 ..
우리가 짜는 프로그램은 두 가지 요구사항을 만족시켜야 한다. 우리는 오늘 완성해야 하는 기능을 구현하는 코드를 짜야 하는 동시에 내일 쉽게 변경할 수 있는 코드를 짜야 한다. 즉 코드는 고객을 만족시키는 동시에 개발자를 만족시킬 수 있어야 한다. 좋은 설계란 오늘 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계이다.
객체지향 시스템은 자율적인 객체들의 공동체다. 객체는 다른 객체들과 협력하는 사회적인 존재이다. 협력은 객체지향의 세계에서 기능을 구현할 수 있는 유일한 방법이다. 그리고 메세지 전송은 객체 사이의 협력을 위해 사용할 수 있는 유일한 수단이다.
객체지향 설계를 할 때, 객체가 가질 수 있는 상태와 행동을 어떤 기준으로 결정할 것인가? 객체를 설계할 때 어떤 행동과 상태를 할당했다면 그 이유는 무엇인가?
객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력이다. 협력이 바뀌면 객체가 제공해야 하는 행동 역시 바뀌어야 한다. 그리고 이어서 객체의 행동이 무엇이냐에 따라 필요한 상태가 정해진다. 따라서 협력은 객체를 설계하는 데 필요한 문맥을 제공한다.
책임이란 객체에 의해 정의되는 응집도 있는 행위의 집합으로, 객체가 유지해야 하는 정보와 수행할 수 있는 행동에 대해 개략적으로 서술한 문장이다. 객체의 책임은 크게 두 가지로 '무엇을 할 수 있는가'와 '무엇을 알고 있는가'로 구성된다.
- 하는 것
- 객체를 생성하거나 계산을 수행하는 등의 스스로 하는 것
- 다른 객체의 행동을 시작시키는 것
- 다른 객체의 활동을 제어하고 조절하는 것
- 아는 것
- 사적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
객체의 행동과 상태 중, 행동은 '하는 것'에 대응되고 상태는 '아는 것'에 대응된다고 할 수 있다.
객체지향 설계에서 가장 중요한 것은 책임이다. 객체에게 얼마나 적절한 책임을 할당하느냐가 설계의 전체적인 품질을 결정한다. 협력이 중요한 이유도 위에서 말했듯이 결국 객체에게 어떤 책임을 할당할 것인지에 대한 문맥을 제공하기 때문이다.
객체에게 책임을 할당할 때, 필요한 메시지를 먼저 식별하고 메시지를 처리할 객체를 나중에 선택해야 한다. 다시 말해 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다.
메시지가 객체를 선택하게 해야 하는 두 가지 중요한 이유는 다음과 같다.
- 객체가 최소한의 인터페이스를 가질 수 있게 된다. 필요한 메시지가 식별될 때 까지 객체의 퍼블릭 인터페이스는 어떤 것도 추가되지 않은 채로 남아 있기 때문에 꼭 필요한 기능만을 객체에 담을 수 있다.
- 객체가 충분히 추상적인 인터페이스를 가질 수 있게 된다. 객체는 무엇을 하는지는 표현해야 하지만 어떻게 수행하는지를 노출해서는 안된다. 메시지를 먼저 식별하면 무엇을 수행할지에 초점을 맞춰 책임을 할당할 수 있다.
역할과 책임은 매우 비슷한 말로 들릴 수 있는데, 역할과 책임의 관계는 아래와 같이 표현할 수 있다.
역할은 다른 것으로 교체할 수 있는 책임의 집합이다.
코드의 관점에서 생각하면, 책임은 개별의 구체적인 클래스에게 할당되는 것이고, 역할은 추상 클래스 혹은 인터페이스에 할당된다고 생각하면 쉬울 것 같다. 대체 가능한 여러 책임을 하나의 역할로 묶음으로써 우리는 유용한 객체들 사이의 협력 관계를 재사용할 수 있다.
역할과 책임의 관계를 쉽게 설명하기 위해 책에서는 배우와 배역 간의 관계를 설명하고 있다. 뮤지컬이나 연극을 보면 하나의 배역을 여러 배우가 날짜별로 번갈아가면서 연기하는 것을 볼 수 있다. 이처럼 여러 가지의 객체도 각자의 '책임'을 가지고 같은 '역할'을 수행할 수 있는 것이다. 한마디로, 역할은 객체의 페르소나다.
책임 주도 설계의 흐름은 다음과 같다.
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.
- 시스템 책임을 더 작은 책임으로 분할한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 한다.
이 책임 주도 설계에서 핵심이 되는 것은 당연하게도 '어느 객체에게 어떤 책임을 할당할 것인가'일텐데, 책임을 적절하게 할당하기 위한 패턴으로 GRASP 패턴이 소개되어 있다.
General Responsibility Assignment Software Pattern
객체는 행동과 행동에 필요한 상태를 함께 담고 있는 자율적인 존재이다. 할당된 책임에 맞는 행동을 갖고 있으며, 이 행동을 구현하기 위해 필요한 상태들을 모두 알고 있다. 따라서 책임을 할당할 때는 책임을 수행할 정보를 가장 잘 알고 있는 객체에게 책임을 할당해야 하는데, 이를 '정보 전문가 패턴'이라고 부른다.
모듈간 결합도가 높으면 결합에 따른 의존성으로 인해 의존받는 객체가 변경되면 의존하는 객체도 변경되어야 한다. 변경의 전파를 막기 위해 결합도는 낮게 유지되어야 한다.
응집도란 서로 관련 있는 객체들이 얼마나 조밀하게 뭉쳐있는지에 관한 것이다. 높은 응집도는 특정 객체가 변경되었을 때, 이 변경에 영향을 받는 객체들이 한데 모여있어 이들에 대한 추가적인 변경을 쉽게 할 수 있도록 해준다.
객체의 암시적인 타입에 따라 행동을 분기해야 한다면 암시적인 타입을 명시적인 클래스로 정의하고 행동을 나눔으로써 응집도 문제를 해결할 수 있다. 다형성은 역할과 책임이 서로 다른 의미를 갖게 하는 장본인이며, 객체들 간의 협력을 재사용할 수 있게 해준다.
시스템에서 변하는 부분은 캡슐화에 의해 보호받아야 한다. 변하지 않은 인터페이스를 만들어 쉽게 변하는 구현을 캡슐화해야 한다.