5장 | 책임과 메시지
🔥 객체의 책임을 자율적으로 만드는 것이 품질의 핵심이다.
🔥 객체들 간 주고받는 메시지가 객체의 책임과 설계를 결정한다.
객체지향 어플리케이션의 중심 사상은 연쇄적으로 메시지를 전송하고 수신하는 객체들 사이의 협력 관계를 기반으로 사용자에게 유용한 기능을 제공하는 것이다.
💻 자율적인 책임
- 객체가 자율적이기 위해서는 객체에게 할당되는 책임의 수준 역시 자율적이어야 한다.
- 너무 포괄적이고 추상적인 책임은 오히려 협력의 의도를 명확하게 표현하지 못하기 때문에 문제가 될 수 있다.
- 어떻게(how)가 아닌 무엇(what)을 해야 하는지에 대해 설명해야 한다.
💻 메시지와 메서드
메시지
- 메시지는 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다.
- 객체가 메시지를 수신할 수 있다는 것은 객체가 해당하는 책임을 수행할 수 있다는 것을 의미한다.
- 객체는 메시지를 처리하기 위한 방법을 자율적으로 선택할 수 있다.
메서드
- 메시지의 수신자 객체는 메시지를 처리할 방법으로 특정 메서드를 실행한다.
- 메시지의 수신자 객체가 런타임에 수행할 메서드를 결정하고 선택한다.
다형성
- 서로 다른 객체들이 다형성을 만족시킨다는 것은 객체들이 동일한 책임을 공유한다는 것을 의미한다.
- 서로 다른 타입의 객체들이 동일한 메시지에 대해 서로 다른 메서드를 이용해 메시지를 처리하는 메커니즘이다.
- 객체지향 패러다임이 강력한 이유는 다형성을 이용해 협력을 유연하게 만들 수 있기 때문이다.
💻 메시지를 따라라
- 클래스가 코드를 구현하기 위해 사용할 수 있는 중요한 추상화 도구인 것은 맞지만, 객체지향의 강력함은 클래스가 아닌 객체들이 주고받는 메시지로부터 나온다.
- 클래스는 객체의 속성과 행위를 담는 틀일 뿐이다.
데이터-주도 설계
- 객체 자체에 초점을 맞출 경우 가장 흔히 범하게 되는 실수는, 협력이라는 문맥을 배제한 채 객체 내부의 데이터 구조를 먼저 생각한 후 데이터 조작에 필요한 오퍼레이션을 나중에 고려하는 것이다.
- 데이터를 중심으로 객체를 설계하는 방식은 객체의 내부 구조를 객체 정의의 일부로 만들기 때문에 객체의 자율성을 저해한다.
- 따라서, 시스템의 기능을 구현하기 위해 객체가 다른 객체에게 제공해야 하는 메시지에 대해 고민해야 한다.
책임-주도 설계
- 객체들 간 주고받는 메시지를 기반으로 적절한 역할과 책임, 협력을 발견하는 것이 기본 아이디어다.
- 메시지가 수신자의 책임을 결정한다.
What/Who 사이클
- 협력 관계 설계를 위해서는 어떤 행위(what) 가 필요한지를 먼저 결정한 후에 누가(who) 그 행위를 수행할 지 결정해야 한다.
- 협력이라는 문맥 안에서 객체의 책임을 결정하는 것은 메시지다. 책임이 먼저 오고 객체가 책임을 따른다.
- 이렇게 메시지를 먼저 결정함으로써 객체의 인터페이스를 발견할 수 있다.
Tell, Don't Ask
- 메시지를 결정하는 시점에는 메시지 송신자는 수신자 객체의 내부 상태를 알 수 없기 때문에 두 객체는 느슨하게 결합된다.
- 송신자는 수신자가 자신이 전송한 메시지를 잘 처리할 것이라고 믿고 메시지를 전송할 수 밖에 없으며, 이 때 각 객체는 자율적이다.
💻 객체 인터페이스
메시지가 인터페이스를 결정한다
- 객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성된다.
- 객체가 어떤 메시지를 수신할 수 있는지가 객체가 제공하는 인터페이스의 모양을 결정한다.
공용 인터페이스
- 공용 인터페이스: 객체가 외부로부터 수신할 수 있는 메시지
- 내부 인터페이스: 객체가 자기 자신으로부터 주고받을 수 있는 메시지
💻 인터페이스와 구현의 분리
구현(Implementation)
객체의 내부 구조와 작동 방식. 공용 인터페이스 외의 모든 부분
인터페이스와 구현의 분리 원칙
- 소프트웨어는 항상 변경되기 때문에, 객체의 내부 구현을 변경에 대한 안전 지대로 만들어 공용 인터페이스와 분리해야 한다.
- 객체 외부에 영향을 미치는 변경은 객체의 공용 인터페이스를 수정할 때뿐이어야 한다.
- 메시지와 송신자와 수신자가 구체적인 구현이 아니라 느슨한 인터페이스에 대해서만 결합되어야 한다.
캡슐화
객체의 자율성을 보존하기 위해 구현을 외부로부터 감추는 것
상태와 행위의 캡슐화
- 데이터 캡슐화(data encapsulation)
- 객체는 상태와 행위를 하나의 자율적인 단위로 묶은 후, 외부에서 반드시 접근해야만 하는 행위만 골라 공용 인터페이스에 노출한다.
사적인 비밀의 캡슐화
- 객체는 스스로 변경하고 조작할 수 있는 비밀, 즉 객체의 내부를 가지고 있다.
- 외부의 객체가 자신의 내부 상태를 직접 관찰하거나 제어할 수 없도록 공용 인터페이스를 경계 삼아 비밀을 격리한다.
💻 책임의 자율성이 협력의 품질을 결정한다
- 자율적이고 적절하게
추상화
된 책임은 협력을 단순하게 만든다.
- 자율적인 책임은 객체의 외부인 공용 인터페이스와 내부를 명확하게 분리하고
캡슐화
한다.
- 책임이 자율적인 경우 책임을 수행하는 내부적인 방법을 변경하더라도 외부에 영향을 미치지 않아
결합도
가 낮아진다.
- 자율적인 책임은 협력의 대상을 다양하게 선택할 수 있도록 하기 때문에 설계가
유연
해지고 재사용성
이 높아진다.
- 객체가 수행하는 책임들이 자율적일수록 객체의
응집도
를 높은 상태로 유지하기가 쉬우며 역햘을 이해하기 쉽다.
📝 느낀점
협력 관계에서 주고받는 메시지가 객체의 책임을 결정하고, 그 책임을 자율적으로 만드는 것이 어플리케이션의 품질을 결정한다는 것을 배웠다. 객체의 자율성은 곧 객체의 캡슐화와 연결된다. 객체는 협력을 위해 필수로 제공해야 하는 행동(메서드)는 공용 인터페이스를 통해 공개하고 나머지 내부 상태와 행동은 외부에서 접근할 수 없게 숨겨야 한다. 그렇게 캡슐화함으로써 객체는 객체지향 공동체와 느슨한 결합을 유지하게 되고 쉽고 유연하게 협력을 할 수 있게 된다. 이러한 내용을 클린 코드를 읽으면서 간접적으로 알고는 있었는데, 이번 기회에 정확하게 설명을 읽고 설계의 단계에 대해 알 수 있어서 유익했던 장이었다.