메시지와 인터페이스

  • 객체지향이 class의 집합으로 구성된다는 것은 반은 맞고 반은 틀리다.
  • 실제로 class로 이뤄지긴 하지만 class는 그냥 구현 도구다.
  • 더 정확하게는 객체를 지향해야 한다. 협력 안에서 객체가 수행하는 책임에 초점을 맞춰야 한다.
  • 협력이라는 컨텍스트 속 객체 간의 주고 받는 메시지가 중요하다. 애플리케이션은 메시지 교환으로 이뤄지고 정의된다.

Message

Client-Server

  • 협력 안에서 메시지 전송하고 이를 처리, 반환되는 서버로 이뤄진다.
  • 요점은 객체가 독립적으로 수행할 수 있는 것보다 더 큰 책임을 수행하기 위해서는 다른 객체와 협력해야 한다는 것이다.

Message, MessagSening

  • 메시지는 객체 들이 의사소통하는 유일한 수단이다.
  • 다른 객체에 도움을 요청하는 것을 메시지 전송, 메시지 패싱이라고 한다.
  • 메시지를 보내는 객체를 전송자 받는 객체를 수신자라고 한다.
  • 메시지는 오퍼레이션 이름과 인자로 구성된다.
  • 메시지 전송은 메시지 수신자를 추가한 것이다.
  • 메시지 수신 시 실행하는 함수 또는 프로시저를 메소드라고 한다.

PublicInterface, Operation

  • 객체가 의사소통을 위해서 외부에 공개하는 메시지 집합을 퍼블릭 인터페이스라고 부른다.
  • 퍼블릭 인터페이스에 포함된 메시지를 Operation이라고 부른다.
  • 오퍼레이션은 수행 가능한 어떤 행동에 대한 추상화다. 메시지 수신 시 실행되는 코드를 메소드라고 한다.

Signature

  • 오퍼레이션의 이름과 파라미터 목록을 합쳐서 시그니쳐라고 한다.
  • 오퍼레이션은 실행 코드 없이 시그니쳐만 지정한 것이다.

정의

  1. 메시지 : 객체가 다른 객체와 협력하기 위해서 사용하는 의사소통 메커니즘
  2. 오퍼레이션 : 객체가 다른 객체에게 제공하는 추상적인 서비스, 오펴레이션은 메시지를 수신하는 객체의 인터페이스를 강조
  3. 메소드 : 메시지에 응답하기 위해서 실행되는 코드 블록
  4. 퍼블릭 인터페이스 : 객체가 협력에 참여하기 위해서 외부에서 수신할 수 있는 메시지의 묶음
  5. 시그니쳐 : 시그니쳐는 오퍼레이션이나 메소드의 명세를 나타낸 것이다.

Interface

  • 좋은 인터페이스는 최소한의 인터페이스와 추상적인 인터페이스라는 조건을 만족하는 것이다.
  • 메시지를 먼저 선택함으로써 협력과는 무관한 오퍼레이션이 인터페이스에 스며드는 것을 방지한다.

디미터의 법칙 (Law of Demeter)

  • “오직 인접한 이웃과만 말해라”
  • 객체들의 협력 경로를 제한하면 결합도를 효과적으로 낮출 수 있다는 것을 발견
  • Shy Code를 작성하도록 유도한다.
  • 부끄럼 타는 코드란 필요한 어떤 것도 다른 객체에 보여주지 않으며, 다른 객체의 구현에 의존하지 않는 코드를 의미한다.
  • 캡슐화를 다른 관점에서 표현한 것이다.
    • MethodChaining이 디미터의 법칙을 위반한 예시가 된다. : 기차 충돌(TrainWreck)
  • 객체는 자기 자신을 책임지는 자율적이 존재가 되어야 한다는 점을 강조한다.

묻지 말고 시켜라

  • 내부 객체에 대해서 묻지 말고 원하는 것을 시켜라(Tell, Don’t Ask)
  • 객체 외부에서 해당 객체의 상태를 기반으로 결정을 내리는 것은 갭슐화 위반이다.
  • 객체 지향의 기본은 함께 변경될 확률이 높은 정보와 행동을 하나의 단위로 통합하는 것이다.
  • 행동을 객체의 외부가 아닌 내부에 위치시키기 때문에 자연스럽게 정보와 행동을 동일한 클래스 안에 두게 된다.
  • 내부 상태로 결정을 내리면 객체가 책임져야 하는 어떤 행동이 외부로 누수된 것과 같다.

의도를 드러내는 인터페이스

  • 메소드는 어떻게가 아니라 무엇을 하는지를 나타내도록 드러내라.
  • 어떻게를 드러내면 캡슐화를 위반한다.
  • 무엇을 하는지를 드러내면 협력 안에서 수행해야하는 책임에 대해서 고민하게 한다.
  • 이러한 패턴을 의도를 드러내는 선택자(Intention Revealing Selector)라고 한다.

함께 모으기

  • 디미터 법칙을 위반하는 설계는 인터페이스와 구현의 분리 원칙도 자연스레 위반하게 된다.
  • 디미터 법칙을 위반한 코드를 수정하는 일반적인 방법은 내부 구조를 묻는 대신 직접 자신의 책임을 수행하도록 시키는 것이다.
  • 추가로 책임, 그러니까 오퍼레이션의 이름은 협력이라는 문맥을 고려해야 한다.

원칙?

  • 물론 위 원칙들이 절대적인 것은 아니다. 항상 설계는 트레이드 오프의 산물이다.
  • 심지어 원칙이 충돌하는 경우도 있다.
  • 원칙이 현재 상황에 부적합하다면 무시할 줄 아는 판단력이 필요하다.
  • 개발자는 원칙을 알고 이해하고 현재 상황에 적합한 방향으로 적용할 줄 아는 판단력을 기르는 것이 중요하다.

명령-쿼리 원칙

  • 필요에 따라 물어야 할 수도 있다.
  • Command-Query가 퍼블릭 인터페이스에 오퍼레이션을 정의할 때 지침이 될 수 있다.
    • 절차를 묶어 호출이 가능하도록 이름을 부여한 기능을 routine이라고 한다.
    • routine은 procedure와 function로 구분할 수 있다.
      • procedure: 정해진 절차에 따라서 내부의 상태를 변경하는 루틴의 한 종류
      • 어떤 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 한 종류
  • 명령-쿼리는 프로시저와 함수를 부르는 또 다른 이름이다. 객체의 상태를 수정하는 오퍼레이션을 명령, 객체와 관련된 정보를 반환하는 오퍼레이션을 쿼리
    1. 객체의 상태를 변경하면 명령
    2. 객체의 정보를 반환하면 쿼리
  • 명령-쿼리 분리 원칙에 따라서 작성된 객체 인터페이스를 명령-쿼리 인터페이스라고 부른다

명령-쿼리 분리와 참조 투명성

  • 명령과 쿼리를 엄격하게 분류하면 객체의 부수효과를 제어하기 수월해진다.
  • 쿼리는 객체의 상태를 변경하지 않기 때문에 몇 번이고 반복적으로 호출하더라도 상관이 없다.(멱등성)
  • 명령과 쿼리를 분리함으로써 명령형 언어의 틀 안에서 참조 투명성(referential transparency)의 장점을 누릴 수 있다.

    참조 투명성 : 어떤 표현식 e가 있을 때 e가 나타나는 모든 위치를 교체해도 결과가 달라지지 않는 특성 이런 값이 변하지 않는 성질을 불변성이라고 한다.

정리

책임에 초점을 맞추자

  1. 디미터 법칙: 협력이라는 컨텍스트 안에서 객체보다 메시지를 먼저 결정하면 두 객체 사이의 구조적인 결합도를 낮출 수 있다.
  2. 묻지 말고 시켜라 : 메시지를 먼저 선택하면 묻지말고 시켜서 스타일에 따라 협력을 구조화하게 된다.
  3. 의도를 드러내는 인터페이스 : 메시지를 먼저 선택하는 것은 메시지를 전송하는 클라이언트의 관점에서 메시지의 이름을 정한다.
  4. 명령-쿼리 분리 : 메시지를 먼저 선택한다는 것은 협력이라는 문맥 안에서 객체의 인터페이스에 관해 고민한다는 것을 의미한다.

그러나?!

  • 오퍼레이션의 시그니처는 단지 오퍼레니션 이름, 인자, 반환 값 타입만 맞으면 명시할 수 있다.
  • 이는 시그니처에 어떤 조건이 만족돼야만 오퍼레이션을 호출할 수 있는지, 어떤 경우에 결과를 반환받을 수 없는지를 표현할 수는 없다.

    계약에 의한 설계(Design By Contract)

    • 계약에 의한 설계는 협력을 위해서 클라이언트와 서버가 준수해야 하는 제약을 코드 상에 명시적으로 표현하고 강제할 수 있는 방법이다.