[책 공부] 객체지향의 사실과 오해

3 minute read

책 소개

객체지향의 사실과 오해

인터페이스와 구현을 분리하자. 자연스레 테스트코드 작성이 쉬워지고, 도메인 모델이 설계가 유연하며, 변경이 용이한 시스템을 만들 수 있다. 객체를 클래스로 보지 않고 객체간의 협력 관계를 생각하고 그 협력 안에서 메시지가 어떻게 전달되고 받는지 행동을 먼저 생각하자.

이 책은 신입이 볼 때와 3년차가 볼 때와 또 다릅니다. 꼭 읽었으면 하는 책이니 추천합니다!

역할과 책임

손님은 카페인을 채우기 위해 커피를 주문할 책임을 수행하고, 캐시어는 손님의 주문을 받는 책임을 성실히 수행하고, 바리스타는 주문된 커피를 제조하는 책임을 수행한다.

커피 주문이라는 협력에 참여하는 모든 사람들은 커피가 정확하게 주문되고 주문된 커피가 손님에게 정확하게 전달될 수 있도록 맡은 받 역할책임을 다하고 있는 것이다. 사람 사는 곳이라면 어디서나 역할, 책임, 협력이 존재한다.

다형성

  • 역할은 대체 가능성을 의미한다. 손님 입장에서 캐시어는 다른 사람이 캐시어 역할을 할 수 있다면 다른 사람으로 대체할 수 있다는 것이다.
  • 책임을 수행하는 방법은 자율적으로 선택할 수 있다. 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력, 이것을 다형성이라 한다.
  • 또한 한 사람이 동시에 여러 역할을 수행할 수도 있다.

협력

객체는 충분히 ‘협력적’이어야 한다. 외부의 도움을 무시한 채 모든 것을 스스로 처리하려는 전지전능한 객체는 내부적인 복잡도에 의해 자멸하고 만다. 그리고 객체는 다른 객체의 명령에 복종하는 것이 아닌 요청에 응답할 뿐이다.

객체의 사적인 부분은 객체 스스로 관리하고 외부에서 일체 간섭할 수 없도록 차단해야 하며, 객체의 외부에서는 접근이 허락된 수단을 통해서만 객체와 의사소통해야 한다.

여기까지 정리하면

아키텍처를 설계할 때 도메인을 먼저 설계해야 하고, 각 도메인 객체가 책임을 가지고 있어야 한다. 우리가 작성하는 서비스 레이어에서는 각 도메인이 가지고 있는 책임과 역할들을 가져다가 사용하는 정도이다. 그리고, getter와 setter를 사용하지 않고 필요한 행동이 있다면 메서드를 따로 만들어서 역할을 수행하도록 만들어야 한다.(캡슐화) 우리는 그 역할을 가져다 사용할 뿐이다. 이렇게 설계함으로써 어떠한 라이브러리에도 의존하지 않는 순수 도메인을 만들 수 있다. 이러한 아키텍처가 바로 DDD(Domain Driven Design)이다.

클래스가 아닌 객체

훌륭한 객체지향 설계자가 되기 위해 거쳐야 할 첫 번째 도전은 코드를 담는 클래스의 관점에서 메시지를 주고받는 객체의 관점으로 사고의 중심을 전환하는 것이다. 중요한 것은 어떤 클래스가 필요한가가 아니라 어떤 객체들이 어떤 메시지들을 주고받으며 협력하는가이다. 클래스는 객체들의 협력관계를 코드로 옮기는 도구에 불과하다.

객체는 상태(state), 행동(behavior), 식별자(identity)를 지닌 실체로 보는 것이 가장 효과적이다. 실제 도메인 엔티티를 설계할 때 이렇게 설계할 것이다. (식별자=id, 상태=필드, 행동=메서드) 행동에 의해 상태값이 변경된다.

참고로 값 객체(Value Object)는 식별자를 가지지 않는 값을 가리키는 용어다. 그리고 값 객체는 불변이다.

기계로서의 객체

객체지향의 세계를 창조하는 개발자들의 주된 업무는 객체의 상태를 조회하고 객체의 상태를 변경하는 것이다. 일반적으로 객체의 상태를 조회하는 작업을 쿼리(Query)라고 하고 객체의 상태를 변경하는 명령(Command)라고 한다. 객체가 외부에 제공하는 행동의 대부분은 쿼리와 명령으로 구성된다. 그리고 이 쿼리와 명령의 책을 분리한 패턴을 바로 CQRS(Command And Query Segregation)라고 한다.

객체지향 설계는 애플리케이션에 필요한 협력을 생각하고 협력에 참여하는데 필요한 행동을 생각한 후 행동을 수행할 객체를 선택하는 방식으로 수행된다. 즉, 행동을 결정한 후에야 행동에 필요한 정보가 무엇인지를 고려하게 되며 이 과정에서 필요한 상태가 결정된다. 책임-주도 설계(Responsibility-Driven Design, RDD)는 협력이라는 문맥 안에서 객체의 행동을 생각하도록 도움으로써 응집도를 높이고, 재사용 가능한 객체를 만들 수 있다.

타입

타입 없는 무질서가 초래한 혼돈의 세상에 질려버린 사람들은 메모리 안의 데이터에 특정한 의미를 부여하기 시작했다. 타입 시스템의 목적은 메모리 안의 모든 데이터가 비트열로 보임으로써 야기되는 혼란을 방지하는 것이다. 타입 시스템은 메모리 안에 저장된 0과 1에 대해 수행 가능한 작업과 불가능한 작업을 구분함으로써 데이터가 잘못 사용되는 것을 방지한다. 즉, 타입 시스템은 데이터가 잘못 사용되지 않도록 제약사항을 부과하는 것이다. 객체는 데이터가 아니다. 객체가 이웃하는 객체와 협력하기 위해 어떤 행동을 해야 할지를 결정하는 것이다. 만약 서로 다른 객체가 같은 데이터를 가지고 있더라도 다른 행동을 한다면 그 객체들은 서로 다른 타입으로 분류되어야 한다. 즉, 객체의 타입을 결정하는 것은 객체의 행동뿐이다.

객체에 대한 선입견

  1. 시스템에 필요한 데이터를 저장하기 위해 객체가 존재한다는 선입견
  • 객체가 존재하는 이유는 행위를 수행하며 협력에 참여하기 위해서다.
  1. 객체지향이 클래스와 클래스 간의 관계를 표현하는 시스템의 정적인 측면에 중점을 둔다는 선입견
  • 협력에 참여하는 동적인 객체이며, 클래스는 단지 시스템에 필요한 객체를 표현하고 생성하기 위해 프로그래밍 언어가 제공하는 구현 메커니즘이다.

TDA

묻지 말고 시켜라.

메시지를 먼저 결정하고 객체가 메시지를 따르게 하는 설계 방식은 객체가 외부에 제공하는 인터페이스가 독특한 스타일을 따르게 한다. 이 스타일을 묻지 말고 시켜라(Tell, Don’t Ask) 스타일 또는 데메테르 법칙(Law of Demeter)이라고 한다. 어떤 객체가 필요한지를 생각하지 말고 어떤 메시지가 필요한지를 먼저 고민해라. 모든 객체는 자신의 상태를 기반으로 스스로 결정을 내려야 한다.

Comments