Skip to main content

6. 메시지와 인터페이스

  • 훌륭한 퍼블릭 인터페이스를 얻기 위해서는 책임 주도 설계 방법을 넘어서 더 나아가야합니다.
  • 아래에서는 도움이 되는 설계 원칙과 기법에 대해 이야기합니다.

01. 협력과 메시지#

클라이언트-서버 모델#

  • 협력 안에서 메시지를 전송하는 객체를 클라이언트, 메시지를 수신하는 객체를 서버라고 부릅니다.
  • 가장 전통적인 모델입니다.
  • 객체가 독립적으로 수행할 수 있는 것보다 더 큰 책임을 수행하기 위해서는 다른 객체와 협력해야 한다는 것입니다.

메시지와 메시지 전송#

  • 메시지(message) 는 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단입니다.
  • 한 객체가 다른 객체에게 도움을 요청하는 것을 메시지 전송(message sending) 또는 메시지 패싱(message passing) 이라고 부릅니다. 이때 메시지를 전송하는 객체를 메시지 전송사(message sender) 라고 부르고, 메시지를 수신하는 객체를 메시지 수신자(message receiver) 라고 부릅니다.
  • 메시지는 오퍼레이션명(operation name)인자(argument) 로 구성되며 메시지 전송은 여기에 메시지 수진자를 추가한 것입니다.

메시지와 메서드#

  • 메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저를 메서드라고 부릅니다.
  • 메시지와 메서드의 구분으로 메시지 전송자와 메시지 수신자가 느슨하게 결합되도록 합니다.
  • 메시지와 메서드를 바인딩하는 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만듭니다.

퍼블릭 인터페이스와 오퍼레이션#

  • 객체는 안과 밖을 구분하는 뚜렷한 경계를 가집니다.
  • 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 부릅니다.

메시지, 오퍼레이션, 메서드

시그니처#

  • 오퍼레이션(또는 메서드)의 이름과 파라미터 목록을 합쳐 시그니처(signature) 라고 부릅니다.

용어 정리

  • 메시지 : 객체가 다른 객체와 협력하기 위해 사용하는 의사소통 메커니즘
  • 오퍼레이션 : 객체가 다른 객체에게 제공하는 추상적인 서비스
  • 메서드 : 메시지에 응답하기 위해 실행되는 코드 블럭
  • 퍼블릭 인터페이스 : 객체가 협력에 참여하기 위해 외부에서 수신할 수 있는 메시지의 묶음
  • 시그니처 : 오퍼레이션이나 메서드의 명세

02. 인터페이스와 설계 품질#

  • 좋은 인터페스는 최소한의 인터페이스추상적인 인터페이스라는 조건을 만족해야 합니다.
  • 퍼블릭 인터페이스의 품질에 영향을 미치는 조건은 다음과 같습니다.
    • 디미터 법칙
    • 묻지 말고 시켜라
    • 의도를 드러내는 인터페이스
    • 명령-쿼리 분리

디미터 법칙#

  • 협력하는 객체의 내부 구조에 대한 결합으로 인해 발생하는 설계 문제를 해결하기 위해 제안된 원칙이 바로 디미터 법칙(Law of Demeter) 입니다.
  • 디미터 법칙을 간단하게 요약하면 객체의 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라는 의미입니다.
  • 디미터 법칙을 통해 부끄럼타는 코드(shy code, 필요한 어떤 것도 다른 객체에게 보여주지 않으며 다른 객체의 구현에 의존하지 않는 코드)를 의미합니다.

디미터 법칙과 캡슐화

  • 디미터 법칙은 캡슐화를 다른 관점에서 표현한 것입니다.
  • 디미터 법칙은 객체가 자기 자신을 책임지는 자율적인 존재여야 한다는 사실을 강조합니다.

묻지 말고 시켜라#

  • 묻지 말고 시켜라(Tell, Don't Ask) 는 객체의 상태에 관해 묻지 말고 원하는 것을 시켜주는 스타일의 메시지 작성을 장령하는 원칙을 가리키는 용어입니다.
  • 묻지 말고 시켜라 원칙에 따르도록 메시지를 결정하다 보면 자연스럽게 정보 전문가에게 책임을 할당하게 되고 높은 응집도를 가진 클래스를 얻을 확률이 높아집니다.

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

  • 무엇을 하는지를 드러내는 이름은 코드를 읽고 이해하기 쉽게 만들뿐만 아니라 유연한 코드를 낳는 지름길입니다.
  • 어떻게 수행하는지를 드러내는 이름이란 메서드의 내부 구현을 설명하는 이름입니다.
  • 어떻게 하느냐가 아니라 무엇을 하느냐에 따라 메서드의 이름을 짓는 패턴을 의도를 드러내는 선택자(Intention Revealing Selector) 라고 합니다.
  • 의도를 드러내는 선택자를 인터페이스 레벨로 확장한 의도를 드러내는 인터페이스도 있습니다. 이는 구현과 관련된 모든 정보를 캡슐화하고 객체의 퍼블릭 인터페이스에는 협력과 관련된 의도만을 표현해야 한다는 것입니다.

함께 모으기#

  • 근본적으로 디미터 법칙을 위반하는 설계는 인터페이스와 구현의 분리 원칙을 위반합니다.
  • 결합도가 낮으면서도 의도를 명확히 드러내는 간결한 협력이 필요합니다. 디미터 법칙과 묻지 말고 시켜라 스타일, 의도를 드러내는 인터페이스가 우리를 돕습니다.

03. 원칙의 함정#

  • 현재 상황에 부적합하다고 판단된다면 과감하게 원칙을 무시합니다.

디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다#

  • 여러 개의 도트를 사용한 코드가 객체의 내부 구조를 노출하는지를 고민해야 합니다.

결합도와 응집도의 충돌#

  • 묻지 말고 시켜라와 디미터 법칙을 준수하는 것이 항상 긍정적인 결과로만 구겨되는 것은 아닙니다.
  • 객체에게 시키는 것이 항상 가능한 것이 아니며, 가끔씩은 물어야 합니다.

04. 명령-쿼리 분리 원칙#

  • 명령-쿼리 분리(Command-Query Separation) 원칙은 퍼블릭 인터페이스에 오퍼레이션을 정의할 때 참고할 수 있는 지침을 제공합니다.
  • 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈을 루틴(routine) 이라고 부르며, 루틴은 다시 프로시저(procedure)함수(function) 으로 구분할 수 있습니다.
    • 프로시저는 부수효과를 발생시킬 수 있지만 값을 반환할 수 없습니다.
    • 함수는 값을 반환할 수 있지만 부수효과를 발생시킬 수 없습니다.
  • 명령(Command)쿼리(Query) 는 객체의 인터페이스 측면에서 프로시저와함수를 부르는 또 다른 이름입니다.
  • 명령과 쿼리를 분리하기 위해서는 다음의 두 가지 규칙을 준수해야 합니다.
    • 객체의 상태를 변경하는 명령은 반환값을 가질 수 없습니다.
    • 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없습니다.
  • 명령-쿼리 분리 원칙에 따라 작성된 객체의 인터페이스를 명령-쿼리 인터페이스(Command-Query Interface) 라고 부릅니다.

반복 일정의 명령과 쿼리 분리하기#

  • 명령과 쿼리를 뒤섞으면 실행 결과를 예측하기가 어려워집니다.
  • 반환 값을 돌려주는 메서드는 쿼리이므로 부수 효과에 대한 부담이 없습니다.

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

  • 명령과 쿼리를 분리함으로써 명령형 언어의 틀 안에서 참조 투명성(referential transparency) 의 장점을 제한적이나마 누릴 수 있게 됩니다.
  • 참조 투명성은 어떤 표현식 e가 있을 때 e의 값으로 나타나는 모든 위치를 교체하더라도 결과가 달라지지 않는 특성을 의미합니다.
  • 참조 투명성을 만족하는 식은 우리에게 두 가지 장점을 제공합니다.
    • 모든 함수를 이미 알고 잇는 하나의 결괏값으로 대체할 수 있기 때문에 식을 쉽게 계산할 수 있습니다.
    • 모든 곳에서 함수의 결괏값이 동일하기 때문에 식의 순서를 변경하더라도 각 식의 결과는 달라지지 않습니다.

책임에 초점을 맞춰라#

  • 아래의 특징은 다음의 특징을 가집니다.
    • 디미터 법칙
      • 협력이라는 컨텍스트 안에서 객체보다 메시지를 먼저 결정하면 두 객체 사이의 구조적인 결합도를 낮출 수 있습니다.
      • 수신할 객체를 알지 못한 상태에서 메시지를 먼저 선택하기 때문에 객체의 내부 구조에 대해 고민할 필요가 없어집니다.
    • 묻지 말고 시켜라
      • 메시지를 먼저 선택하면 묻지 말고 시켜라 스타일에 따라 협력을 구조화하게 됩니다.
    • 의도를 드러내는 인터페이스
      • 메시지를 먼저 선택한다는 것은 메시지를 전송하는 클라이언트의 관점에서 메시지의 이름을 정한다는 것입니다.
    • 명령-쿼리 분리 원칙
      • 메시지를 먼저 선택한다는 것은 협력이라는 문맥 안에서 객체의 인터페이스에 관해 고민한다는 것을 의미합니다.
  • 훌륭한 메시지를 얻기 위한 출발점은 책임 주도 설계 원칙을 따르는 것입니다.
  • 계약에 의한 설계(Design By Contract) 는 협력을 위해 클라이언트와 서버가 준수해야 하는 제약을 코드 상에 명시적으로 표현하고 강제할 수 있는 방법입니다.
Last updated on