[루비로 배우는 객체지향 디자인] 4. 유연한 인터페이스 만들기

4. 유연한 인터페이스 만들기

  • 애플리케이션은 클래스로 구성되지만 메시지를 통해 정의됨
  • 애플리케이션의 움직임을 반영하는 것은 메시지

객체가 무엇을 아는지(객체의 책임), 누구를 알고 있는지(객체의 의존성) 뿐만 아니라 서로 어떻게 소통하는지 알아야 함

객체 사이의 ㅍ소통은 인터페이스를 통해 이뤄짐 애플리케이션이 자라나고 변형될 수 있도록 해주는 유연한 인터페이스에 대해 알아보자

4.1 인터페이스 이해하기

img

첫번쨰 애플리케이션 객체는 재사용하기 어려움

  • 자기 자신을 너무 많이 드러냄
  • 주변 객체에 대해 너무 많이 알고 있음
  • 독립적으로 존재하는 객체 없음
  • 하나를 재사용하려면 전체를 사용해야함
  • 하나 수정시 전체 수정

위 문제의 핵심은 클래스가 무엇을 하는지 가 아닌 무엇을 드러내는지

  • 클래스의 모든 메서드는 다른 객체가 언제든 호출하기 쉬운 대상

두 번쨰 어플리케이션 객체는 어떤 메시지를 주고받을지에 대한 합의가 있음

  • 각 객체는 다른 객체가 사용해도 되는 메서드 묶음을 잘 정리함
  • 밖으로 노출된 메서드가 클래스의 public interface 를 구성

인터페이스란

여러가지 의미를 가짐

  1. 위 예제에서는 클래스 안에 있는 인터페이스 지칭

  2. 클래스는 메서드를 구현

  3. 하나의 클래스로부터 독립되어 있고 여러 클래스 사이를 돌아다니는 인터페이스

  4. 메시지의 묶음

  5. 메세지들 자체가 인터페이스를 정의

  6. 마치 인터페이스가 가상의 클래스를 정의하는 것과 같음

4.2 인터페이스 정의하기

레스토랑의 부엌을 예로 들자.

식당에는 손님이 사용할 수 있는 public interface, 즉 메뉴판 부엌안에서는 많은 일이 오가지만 private 하기에 손님에게 보이지 않음

퍼블릭과 프라이빗의 구분은 일을 가장 효율적으로 처리하기 위함

  • 손님이 직접 요리 과정을 감독하면, 식재료가 부족하여 다른 요리 추천해야 할 때마다 손님을 새로 교육 시켜야함
  • 메뉴판을 이용하기 때문에 손님은 부엌에서 어떻게 요리하는지 모르면서 주문 가능

클래스는 마치 이런 부엌과 같음

  • 1개의 책임을 제대로 수행하기위해 존재하며
  • 수많은 메서드를 구현

어떤 메서드는

  • 우리 클래스의 메뉴판과 같은 역할이므로 퍼블릭 메서드
  • 자잘한 내부 구현에 관여하기 때문에 프라이빗 메서드

4.2.1 퍼블릭 인터페이스

  • 클래스의 핵심 책임을 드러냄
  • 다른 객체에 의해 호출됨
  • 쉽게 변경 안됨
  • 다른 객체가 안정적 의존 가능
  • 테스트를 통해 문서화됨

4.2.2 프라이빗 인터페이스

  • 세부 구현 담당
  • 다른 객체에 의해 호출 안됨
  • 필요에 따라 언제든 변경 가능
  • 다른 객체가 의존하기에 위험
  • 테스트에서 다루지 않을 수도 있음

4.2.3 책임, 의존성 그리고 인터페이스

클래스의 퍼블릭 인터페이스는 클래스의 구체적인 책임을 표현한 문장과 상응(책임을 명시한 계약서)

4.3 퍼블릭 인터페이스 찾기

디자인의 목표는 당장의 요구사항을 처리하기에 충분하면서 나중에 수정할 여지 남기는 것

4.3.1 예시: 자전거 여행 회사

여행회사가 여행객에게 자전거 여행길 추천, 자전거는 회사꺼가 부족할 시 지역 점포들과 공유

애플리케이션의 명사, 정보, 행동 을 기반으로 클래스를 떠올려보면

  • 여행객
  • 여행
  • 여행길
  • 자전거
  • 정비공

이를 도메인 객체 라고 부름

도메인 객체에만 집착하면 행동들을 객체 속에 넣어버림 도메인 객체들이 주고 받는 메시지 에 주목해야함 이 메세지들은 새로운 겍체를 찾도록 도움

4.3.3 시퀀스 다이어그램 사용하기

UML은 그 중 하나임

  • 객체들의 배치
  • 메시지 전송 전략 에 대해 검토 가능

wayne은 Trip에게 suitable_trips 메세지를 전송하여 결과만 돌려받음

img

위 이미지에서 질문을 던져보자.

  • 여행에 적당한 자전거가 준비되어 있는지 파악하는 것이 Trip의 책임일까?

    • 이 수신자가 이 메시지에 반응하 책임을 가지고 있는가?

위와 같이 다이어그램을 사용하면 “이 클래스가 필요한건 알겠는데 이 클래스는 무엇을 해야하지? ” 가 아닌 ”메시지를 전송해야 하는데 누구에게 전송하지? 라고 질문할 수 있음, 즉 메시지 기반 디자인 가능

여튼 위와 같은 질문에 대해

  • 여행에 어울리는 자전거를 파악하는 것은 Trip이 아닌 Bicycle일듯

    • Trip은 sutiabletrips에, Bicycle은 suitablebicycle을 처리해야할 책임 있음

정리하자면 wayne은

  • 여행지 목록을 얻고자함
  • suitable_trips 메세지를 구현하고 있는 객체를 가짐

개선해보자

(2)

  • wayne은 여행지 목록을 얻고자함
  • suitable_trips 메세지를 구현하고 있는 객체가 있음
  • suitable trips를 찾기 위해 suitable_bicycles도 골라야 함
  • suitable_bicycles 메시지를 구현하고 있는 또 다른 객체가 있음

이번에는 Trip에서 추가적인 책임을 걷어냈지만

  • 이 책임을 단지 customer로 옮겨놓았을 뿐임
  • 자신이 무엇을 원하는지 알아야하고
  • 다른 객체들이 어떻게 협업해야 하는지도 알아야함
  • 즉, Customer 클래스는 자신의 책임이 아닌 거까지 직접 관리하고 있음(손님이 메뉴판을 보고 음식을 주문하는게 아닌 직접 부엌에 들어가 요리하는 것)

Summary

객체 사이의 (메시지)소통은 인터페이스를 통해 이뤄짐

인터페이스란 메시지의 묶음

예를 들어보자. 식당에는 손님이 사용할 수 있는 public interface인 메뉴판이 있음 부엌에서는 많은 일이 오가지만 private 하기에 손님에게 보이지 않음 손님은 메뉴판으로 원하는 것을 주문 만 하면됨. public interface의 메서드에 메뉴만 인자로 전달하면 원하는 것을 제공하기 위해 음식을 만듬

퍼블릭 인터페이스

  • 클래스의 핵심 책임 을 드러냄
  • 다른 객체에 의해 호출됨
  • 쉽게 변경 ㄴㄴ
  • 다른 객체가 안정적 의존 가능
  • 테스트를 통해 문서화됨

프라이빗 인터페이스

  • 세부 구현 담당
  • 다른 객체에 의해 호출 ㄴㄴ
  • 필요에 따라 언제든 변경 가능
  • 다른 객체가 의존하면 위험

설계할 때 주의할점

  • 도메인 객체에만 집착하면 행동들을 객체 속에 넣는 실수하게됨
  • 도메인 객체들이 주고 받는 메시지 에 주목해야함

    • 이 메세지들은 새로운 객체를 찾도록 도움

UML을 통해

  • 객체를 배치하고
  • 메시지 전송을 그려서 전략 작성하면 도움됨

    • 이 클래스는 무엇을 해야하지? 보다는 메시지를 전송하면 누구한테 전송하지? 라는 올바른 질문 가능