루비로 배우는 객체지향 디자인 - 3장
<aside>
💡 이번 편은 코드는 없고 UML 만 예제 도표로 있다보니 가능하면 내용 정리 뿐 아니라 직접 본문을 읽어보는 것을 추천
</aside>
유연한 인터페이스 만들기
- 애플리케이션은 클래스로 구성되어있지만 메세지를 통해 정의된다.
- 소스 코드 저장소에 무엇이 들어갈지를 결정하는 것은 클래스지만, 애플리케이션의 움직임을 반영하는 것은 메세지이며 이 메세지가 애플리케이션을 살아 움직이게 한다.
- 따라서 디자인을 할 때 객체 사이를 가로지르는 메세지에 관심을 두어야 한다.
- 객체 사이의 소통은 인터페이스를 통해 이루저인다.
- 그림 4.1
- 왼쪽 애플리케이션의 객체는 재사용하기 어렵다. 자기 자신을 너무 많이 드러내고 주변 객체에 대해 너무 많이 알고 있다.
- 오른쪽은 조립 가능하게 구성되어 있다. 각 객체는 자기 자신을 최소한으로 드러내며 다른 객체에 대해 가능한 한 조금 알고 있다.
- 핵심 문제는 각 클래스가 무엇을 하는지에 있는 것이 아니라 무엇을 드러내는지(퍼블릭 인터페이스)에 있다.
- 레스토랑에서 손님이 메뉴를 보고 음식을 주문할 때 겉으로는 "음식을 시킨다
음식이 나온다" 로 정의할 수 있지만, 그 안에 음식을 조리하는 부엌에서는 엄청나게 많은 일들이 일어난다. 하지만 손님에게는 보이지 않는다.
- 식당에는 손님이 사용할 수 있는 퍼블릭 인터페이스, 즉 메뉴판이 있다.
- 부엌 안에서는 수 많은 메세지가 오가고 있지만 이 메세지는 private 하기 때문에 손님에게 보이지 않는다. 음식 시켰다고 손님이 직접 부엌에 들어가진 않으니까.
- 퍼블릭 인터페이스
- 클래스의 핵심 책임을 드러낸다.
- 다른 객체에 의해 호출될 수 있다.
- 쉽게 변경되지 않는다.
- 다른 객체가 안정적으로 의존할 수 있다.
- 테스트를 통해 꼼꼼하게 문서화되어 있다.
- 즉, 클래스의 책임을 명시해주는 계약서이다.
- 프라이빗 인터페이스
- 세부적인 구현을 담당한다.
- 다른 객체에 의해 호출되지 않는다.
- 필요에 따라 언제든 변경할 수 있다.
- 다른 객체가 의존하기에는 위험하다.
- 테스트에서 다루지 않을 수도 있다.
- 새로운 예시: 자전거 여행 회사
- 자전거 여행을 주선하며 로드바이크 여행과 마운틴 바이크 여행을 다룬다. 각 여행길은 여행객이 방문할 수 있는 횟수가 제한되어 있고, 정해진 수의 가이드 겸 정비공이 필요하다. 등등
- 명세를 정리하면
- 여행객은
- 여행길을 선택하기 위해서
- 정해진 날짜에
- 자신에게 맞는 난이도의
- 자전거를 빌릴 수 있는
- 여행길 목록을 보고싶어 한다.
- 의도를 구성하기
- 테스트를 먼저 작성한다? 이게 가능한 사람은 디자인 경험이 풍부하기 떄문이다. 이런 수준의 고수들이라면 객체들이 무엇을 할 수 있는지, 객체들이 전체 애플리케이션 속에서 어떻게 소통해야 하는지에 대한 이미지를 이미 떠올리고 있다. 어떤 "의도" 를 가지고 애플리케이션을 만들지 구상해놓고 있기 때문에 첫 번째 테스트를 짤 수 있는 것이다.
- Customer, Trip, Route, Bike, Mechanic 등의 클래스명이 떠올랐다. 이 클래스를 떠올릴 수 있던 이유는 애플리케이션 속의 명사들, 즉 정보(data)와 행동(behavior)을 가지고 있는 명사를 표현하기 떄문이다.
이를 도메인 객체라 부르자.
- 도메인 객체들은 매우 명시적이다. 지속적이고 큼지막하고 눈에 보이는 현실 세계의 실재들을 지칭하고, 결국 데이터베이스에서도 표현되기 떄문이다.
- 디자인 전문가들은 도메인 객체에 집중하지 않으면서 이 객체들을 인지한다. 이들은 도메인 객체가 아니라 도메인 객체들이 주고받는 메세지에 주목한다.
- 시퀀스 다이어그램 사용
- 객체들의 배치와 메세지 전송 전략에 대해 검토해 볼 수 있는 간단한 방법 제공
- 규칙에 얽매이지 말고, 편한대로 다이어그램을 화이트보드에 그리고, 필요한 만큼 수정하고, 원하는 것을 얻었다면 지워버리자.
- (이후는 시퀀스 다이어그램을 끼고 설명하는 것들이 많아서 많이 생략하고 핵심적인 내용만 발췌)
- 시퀀스 다이어그램을 그리면서 메세지를 중심으로 디자인을 이야기할 수 있게 되었다. 클래스를 결정하고 그 클래스의 책임을 찾아 나서는 대신 메세지를 결정하고 이 메세지를 누구에게 전송할 지 찾아보게 되었다.
- 송신자가 원하는 것을 요구하는 메세지와 수신자가 어떻게 행동해야 하는지 알려주는 메세지의 차이를 구분하는 것은 매우 중요하다. 둘 사이의 차이를 이해하는 것이 잘 정의된 퍼블릭 인터페이스를 가지고 재사용이 가능한 클래스를 만들기 위한 핵심 포인트이다.
- 그림 4.5 에서 그림 4.6 으로 가면서 Mechanic 의 세세한 동작을 다 알고 있던 Trip 이 그 책임을 Mechanic 에게 넘겨주었다.
- 덕분에 Mechanic 에 포함된 퍼블릭 인터페이스의 양이 눈에 띄게 줄었다. 변화에 영향을 받는 객체들도 줄어들었다는 이야기가 된다.
- 객체에게 주어진 맥락에서 독립시키기
- 객체에게 필요한 맥락은 객체의 재사용성에 바로 영향을 미친다. 단순한 맥락일 수록 재사용하기 쉽고 테스트하기도 쉽다.
- 복잡한 맥락 속에 위치한 객체는 사용하기도 어렵고 테스트하기도 어렵다. 이 객체들이 무엇을 하려고 하던지 복잡한 설정을 먼저 처리해야 한다.
- 가능한 최고의 상황은 객체가 자신의 맥락로부터 완전히 독립되어 있는 것이다. 다른 객체의 존재를 전혀 모른채로 협업할 수 있는 객체는 기대하지 않았던 방식으로 참신하게 재사용될 수 있다.
- 더 나은 인터페이스를 만들기 위해서는 일단 인터페이스가 있어야 한다. 잘 정의된 인터페이스가 있따는 것이 중요한 것이지, 인터페이스가 완벽해야 한다는 것이 중요한 것이 아니다.
- 인터페이스에 대해 고민해 보자. 명확한 의도를 가지고 구현해 보자. 우리의 애플리케이션의 특징을 드러내고 그 미래를 결정하는 것은 바로 인터페이스이다. 테스트보다, 다른 그 어떤 코드보다도 인터페이스가 중요하다.
- 퍼블릭 인터페이스의 메서드는 다음과 같아야 한다.
- 일반적인 의미, 엄밀하고 명시적으로 규정되어 있어야 한다.
- '어떻게' 보다는 '어떤' 것에 대해 말해야 한다.
- 예측할 수 있는 한도에서나마 바뀌지 않을 이름을 지어야 한다.
- 추가적인 인자는 순서와 관계없이 받을 수 있도록 하라. (루비라면 해시, JS라면 객체 리터럴)