오브젝트 6장

오브젝트 6장

제 6장

메시지와 인터페이스

협력과 메시지

  • 클라이언트 - 서버 모델
    • 두 객체 사이의 협력 관계를 설명하기 위한 전통적인 메타포
    • 클라이언트가 서버의 서비스를 요청하는 단방향 상호작용
    • 객체는 클라이언트와 서버의 역할을 동시에 수행하는 것이 일반적
    • 두 객체 사이의 협력을 가능하게 해주는 매개체는 메시지

메시지와 메시지 전송

  • 메시지란?
    • 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단
    • 한 객체가 다른 객체에게 도움을 요청하는 것을 메시지 전송(message sending) or 메시지 패싱(message passing)
    • 메시지를 전송하는 객체를 메시지 전송자(message sender)
    • 메시지는 오퍼레이션명인자로 구성
    • 메시지 전송은 여기에 메시지 수신자를 추가한 것
    • 예를 들어, buyWater(money)는 메시지
    • 메시지 수신자인 market을 추가한 market.buyWater(money)이 메시지 전송

메시지와 메서드

  • 메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저를 메서드라고 부른다.
  • 객체 사이의 메시지 전송은 전통적인 방식의 함수 호출이나 프로시저 호출과는 다르다.
  • 전통적인 방식의 함수 호출은 컴파일 타임과 런타임에 실행되는 함수가 동일함
  • 그러나 객체는 메시지와 메서드라는 두가지 서로 다른 개념을 런타임에 연결해야 하기 때문에 컴파일 시점과 런타임의 의미가 달라질 수 있다. (동적 바인딩)
  • 런타임에 메시지와 메서드를 바인딩하는 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만든다.

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

  • 객체는 안과 밖의 경계가 뚜렷하다.
  • 외부에서 볼 때 객체의 안쪽은 볼 수 없다.
  • 외부 객체는 객체가 공개하는 메시지를 통해서만 객체와 상호작용할 수 있다.
  • 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 부른다.
  • 퍼블릭 인터페이스에 포함된 메시지를 오퍼레이션이라고 부른다.
  • 시그니처는 오퍼레이션의 이름과 파라미터 목록을 합쳐서 부르는 용어다.
  • 오퍼레이션은 실행 코드 없이 시그니처만을 정의한 것이다.
  • 오퍼레이션 관점에서 다형성이란 동일한 오퍼레이션 호출에 대해 서로 다른 메서드들이 실행되는 것이라 정의할 수 있다.

인터페이스와 설계 품질

좋은 인터페이스는 최소한의 인터페이스추상적인 인터페이스라는 조건을 만족해야 한다.

디미터 법칙 (Law Of Demeter)

  • 객체의 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라는 법칙
  • 낯선 자에게 말하지 말라
  • 인접한 이웃하고만 말하라
  • 자바같이 도트(.)를 이용해 메시지 전송을 표현하는 언어라면 오직 하나의 도트만 사용하라
  • 클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하라
    • this 객체
    • 메서드의 매개변수
    • this 속성
    • this의 속성인 컬렉션의 요소
    • 메서드 내에서 생성된 지역 객체
  • 디미터의 법칙을 따르면 부끄럼타는 코드(shy code)를 작성할 수 있다.
    • 부끄럼타는 코드란 불필요한 어떤 것도 다른 객체에게 보여주지 않으며 다른 객체의 구현에 의존하지 않는 코드
  • 디미터의 법칙은 캡슐화를 다른 관점에서 표현한 것

묻지 말고 시켜라

  • 디미터 법칙은 훌륭한 메시지는 객체의 상태에 관해 묻지 말고 원하는 것을 시켜야 한다는 사실을 강조
  • Tell. Don’t Ask는 이러한 스타일의 메시지 작성을 장려하는 원칙을 가르키는 용어
  • 묻지 말고 시켜라 원칙에 따르도록 메시지를 결정하다 보면 자연스럽게 정보 전문가에게 책임을 할당하고 높은 응집도를 가진 클래스를 얻을 확률이 높아진다.

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

오퍼레이션의 이름은 협력이라는 문맥을 반영해야 한다.

클라이언트가 객체에게 무엇을 원하는지 명확하게 표현해야 한다.

디미터 법칙은 객체 간 협력을 설계할 때 캡슐화를 위반하는 메시지가 인터페이스에 포함되지 않도록 제한한다.

묻지 말고 시켜라 원칙은 디미터의 법칙을 준수하는 협력을 만들기 위한 스타일을 제시한다.

의도를 드러내는 인터페이스 원칙은 객체의 퍼블릭 인터페이스에 어떤 이름이 드러나야 하는지에 대한 지침을 제공함으로써 코드의 목적을 명확하게 커뮤니케이션할 수 있게 해준다.

원칙의 함정

디미터의 법칙과 묻지 말고 시켜라 스타일은 객체의 퍼블릭 인터페이스를 깔끔하고 유연하게 만들 수 있는 훌륭한 설계 원칙이지만 절대적인 법칙은 아니다.

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

디미터의 법칙에 대한 오해를 풀고 시작하자.

아래의 Stream은 디미터의 법칙을 위반하였을까?

IntStream.of(1,15,20,3,9).filter(x -> x>10).distinct().count();

정답은 위 Stream은 디미터의 법칙을 위반하지 않았다.

어째서일까?

of, filter, distinct 메서드는 모두 IntStream이라는 동일한 클래스의 인스턴스를 반환한다.

즉, 이들은 IntStream의 인스턴스를 또 다른 IntStream의 인스턴스로 변환한다.

IntStream이 다른 IntStream으로 변환할 뿐 객체를 둘러싸고있는 캡슐을 그대로 유지한다.

만약 위와같은 상황의 코드가 디미터의 법칙을 위반한지 궁금하다면 다음과 같은 질문을 스스로에게 하라.

과연 여러개의 도트를 사용한 코드가 객체의 내부 구조를 노출하고 있는가?

명령 - 쿼리 분리 원칙

명령 - 쿼리 분리 원칙은 퍼블릭 인터페이스에 오퍼레이션을 정의할 때 참고할 수 있는 지침을 제공해준다.

어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈을 루틴(Routine)이라 부른다.

루틴은 다시 프로시져(Procedure)와 함수(Function)으로 구분할 수 있다.

프로시져와 함수를 혼용해서 사용하는 경우가 많은데 명확하게 구분하자.

프로시져는 정해진 절차에 따라 내부의 상태를 변경하는 루틴의 한 종류이다.

함수는 어떤 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 한 종류이다.

다시 말하면 프로시져는 부수효과를 발생시킬 수 있지만 값을 반환할 수 없다.

함수는 값을 반환할 수 있지만 부수효과를 발생시킬 수 없다.

명령(Command)와 쿼리(Query)는 객체의 인터페이스 측면에서 프로시저와 함수를 부르는 또 다른 이름이다.

명령 - 쿼리 분리 원칙의 요지는 오퍼레이션은 부수효과를 발생시키는 명령이거나 부수효과를 발생시키지 않느 쿼리 중 하나여야 한다는 것이다.

  • 객체의 상태를 변경하는 명령은 반환값을 가질 수 없다.
  • 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다.

명령 - 쿼리 분리 원칙 한문장 요약

질문이 답변을 수정해서는 안된다.

Reference

오브젝트 - 조영호



© 2022. by minkuk

Powered by minkuk