본문 바로가기
스터디/오브젝트

6장 - 메시지와 인터페이스

by 검은도자기 2023. 4. 17.

개요

이번장에서는 유연하고 재사용 가능한 퍼블릭 인터페이스를 만드는 데 도움이 되는 설계 원칙과 기법을 익히기 위한 개념들을 살펴봅니다.

 

 

메시지(message)

  • 객체가 다른 객체와 협력하기 위해 사용하는 의사소통 메커니즘을 메시지(message), 일반적으로 객체의 오퍼레이션이 실행되도록 요청하는 것을 메시지 전송(message sending)이라고 부릅니다.
  • 메시지는 협력에 참여하는 전송자와 수신자 양쪽 모두를 포함하는 개념입니다.
  • 메시지는 오퍼레이션명과 인자로 구성되며 메시지 전송은 여기에 메시지 수신자를 추가한 것입니다.
  • 따라서 메시지 전송은 메시지 수신자, 오퍼레이션명, 인자의 조합입니다.

 

퍼블릭 인터페이스

  • 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 부릅니다.
  • 클래스의 퍼블릭 메서드들의 집합이나 메시지의 집합을 가리키는 데 사용됩니다.

 

오퍼레이션(operation)

  • 객체가 다른 객체에게 제공하는 추상적인 서비스(인터페이스)를 오퍼레이션이라고 부릅니다.
  • 오퍼레이션은 메시지 전송자는 고려하지 않은 채 메시지 수신자의 관점만을 다룹니다.(객체의 인터페이스를 강조)
  • 따라서 메시지 수신이란 메시지에 대응되는 객체의 오퍼레이션을 호출하는 것을 의미합니다.

 

메서드

  • 메시지에 응답하기 위해 실행되는 코드 블록을 메서드라고 부릅니다.
  • 메서드는 오퍼레이션의 구현입니다.
  • 메시지를 수신했을 때 실제로 어떤 코드가 실행되는지는 메시지 수신자의 실제 타입이 무엇인가에 달려 있습니다.
  • 실행 시점에 메시지와 메서드를 바인딩하는 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만들 수 있게 합니다.
  • 오퍼레이션과 메서드의 구분은 다형성의 개념과 연결이 됩니다.

 

시그니처(signature)

  • 오퍼레이션(또는 메서드)의 이름과 파라미터 목록을 합쳐 시그니처라고 부릅니다.
  • 오퍼레이션은 실행 코드 없이 시그니처만을 정의한 것이며 메서드는 이 시그니처에 구현을 더한 것입니다.
  • 일반적으로 메시지를 수신하면 오퍼레이션의 시그니처와 동일한 메서드가 실행됩니다.
  • 오퍼레이션의 관점에서 다형성이란 동일한 오퍼레이션 호출에 대해 서로 다른 메서드들이 실행되는 것이라고 정의합니다.

 

 

좋은 인터페이스 설계란 무엇일까?

  • 좋은 인터페이스는 최소한의 인터페이스와 추상적인 인터페이스라는 조건을 만족해야 합니다.(3장 참고)
  • 최소주의를 따르면서도 추상적인 인터페이스를 설계할 수 있는 가장 좋은 방법은 책임 주도 설계 방법을 따르는 것입니다.
  • 책임 주도 설계 방법은 메시지를 먼저 선택함으로써 협력과는 무관한 오퍼레이션이 인터페이스에 스며드는 것을 방지합니다.
  • 따라서 최소의 오퍼레이션만 포함하게 됩니다.
  • 또한 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 함으로써 클라이언트의 의도를 메시지에 표현할 수 있게 합니다.
  • 퍼블릭 인터페이스의 품질에 영향을 미치는 다음과 같은 원칙과 기법들이 존재합니다.

 

디미터 법칙(Law of Demeter)

  • 협력하는 객체의 내부 구조에 대한 결합으로 인해 발생하는 설계 문제를 해결하기 위해 제안된 원칙입니다.

 

디미터 법칙을 어떻게 적용해야 할까?

  • 클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 프로그래밍해야 합니다.
    • this 객체
    • 메서드의 매개변수
    • this의 속성
    • this의 속성인 컬렉션의 요소
    • 메서드 내에서 생성된 지역 객체
  • 디미터 법칙을 따르면 부끄럼 타는 코드를 작성할 수 있습니다.
  • 디미터 법칙을 따르는 코드는 메시지 수신자의 내부 구조가 전송자에게 노출되지 않으며, 메시지 전송자는 수신자의 내부 구현에 결합되지 않습니다.
  • 따라서 클라이언트와 서버 사이에 낮은 결합도를 유지할 수 있습니다.

 

부끄럼 타는 코드란?

  •  불필요한 어떤 것도 다른 객체에게 보여주지 않으며, 다른 객체의 구현에 의존하지 않는 코드를 말합니다.

 

묻지 말고 시켜라(Tell, Don't Ask)

  • 디미터 법칙은 훌륭한 메시지는 객체의 상태에 관해 묻지 말고 원하는 것을 시켜야 한다는 사실을 강조합니다.
  • 묻지 말고 시켜라는 이런 스타일의 메시지 작성을 장려하는 원칙을 가리키는 용어입니다.
  • 객체의 외부에서 해당 객체의 상태를 기반으로 결정을 내리는 것은 캡슐화를 위반합니다.
  • 묻지 말고 시켜라 원칙을 따르면 객체의 정보를 이용하는 행동을 객체의 외부가 아닌 내부에 위치시키기 때문에 자연스럽게 정보와 행동을 동일한 클래스 안에 두게 됩니다.
  • 내부의 상태를 묻는 오퍼레이션을 인터페이스에 포함시키고 있다면 더 나은 방법은 없는지 아래 질문을 던지며 고민해 봐야 합니다.
    • 내부의 상태를 이용해 어떤 결정을 내리는 로직이 객체 외부에 존재하는가?
  • 만약 중요 로직이 객체 외부에 존재하다면 해당 객체가 책임져야 하는 어떤 행동이 객체 외부로 누수된 것입니다.
  • 상태를 묻는 오퍼레이션을 행동을 요청하는 오퍼레이션으로 대체함으로써 인터페이스를 향상해야 합니다.
  • 훌륭한 인터페이스를 만들기 위해서는 객체가 어떻게 하는지가 아니라 무엇을 하는지를 서술해야 합니다.

 

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

  • 구현과 관련된 모든 정보를 캡슐화하고 객체의 퍼블릭 인터페이스에는 협력과 관련된 의도만을 표현한 것을 의미합니다.

 

어떻게 의도를 드러내는 인터페이스를 만들어야 할까?

  • 어떻게가 아니라 무엇을 하는지 드러내는 것입니다.
  • 협력을 설계하기 시작하는 이른 시기부터 클래스 내부 구현에 관해 고민할 수밖에 없습니다.
  • 무엇을 하는지 드러내도록 메서드의 이름을 짓기 위해서는 객체가 협력 안에서 수행해야 하는 책임에 관해 고민해야 합니다.
  • 이것은 외부의 객체가 메시지를 전송하는 목적을 먼저 생각하도록 만들며, 결과적으로 협력하는 클라이언트의 의도에 부합하도록 메서드의 이름을 짓게 됩니다.
  • 어떻게 하느냐가 아니라 무엇을 하느냐에 따라 메서드의 이름을 짓는 패턴을 의도를 드러내는 선택자라고 부릅니다.
  • 객체에게 묻지 말고 시키되 구현 방법이 아닌 클라이언트의 의도를 드러내야 합니다.
  • 이것이 이해하기 쉽고 유연한 동시에 협력적인 객체를 만드는 가장 기본적인 요구사항입니다.

 

유연한 인터페이스를 만들 때 주의해야 할 점은 무엇일까?

  • 잊지 말아야 하는 사실은 설계가 트레이드오프의 산물이라는 것입니다.
  • 원칙이 현재 상황에 부적합하다고 판단되면 과감하게 원칙을 무시해야 합니다.
  • 원칙을 하는 것보다 더 중요한 것은 언제 원칙이 유용한지 판단할 수 있는 능력을 기르는 것입니다.
  • 가끔씩은 필요에 따라 물어야 합니다.
  • 소프트웨어 설계에 존재하는 몇 안 되는 법칙 중 하나는 "경우에 따라 다르다"라는 사실을 명심해야 합니다.

 

 

명령-쿼리 분리 원칙

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

 

명령과 쿼리를 분리해서 얻게 되는 장점은 무엇일까?

  • 명령이 개입하지 않는 한 쿼리의 값은 변경되지 않기 때문에 쿼리의 결과를 예측하기 쉬우며, 이는 디버깅이 용이한 동시에 유지보수성이 좋아집니다.
  • 명령과 쿼리를 분리함으로써 명령형 언어의 틀 안에서 참조 투명성(referential transparency)의 장점을 제한적이나마 누릴 수 있게 됩니다.

 

참조 투명성(Referential transparency)이란?

  • 동일한 입력에 대해 항상 동일한 값을 반환하는 특성을 의미합니다.

 

참조 투명성을 만족하는 식의 장점은 무엇일까?

  • 모든 함수를 이미 알고 있는 하나의 결괏값으로 대체할 수 있기 때문에 식을 쉽게 계산할 수 있습니다.
  • 모든 곳에서 함수의 결괏값이 동일하기 때문에 식의 순서를 변경하더라도 각 식의 결과는 달라지지 않습니다.

 

 

마무리

오늘은 오브젝트 책 6장을 스터디하면서 중요하다고 생각한 부분을 정리해 보았습니다.

이번 장에서는 어떤 기준으로 인터페이스를 설계하면 좋을지 이해하게 되었고 또 여러 설계 기법들에 대해 알게 되어서 좋았다고 생각이 듭니다.

하지만 설계 기법들을 사용할 때 어떤 부분들을 고려해야할지에 대한 내용이 부족한 거 같아서 조금 아쉽다고 생각이 드네요.

이번 포스팅은 마무리하면서 다음 포스팅에서 뵙겠습니다.

 

 

참고

http://www.yes24.com/Product/Goods/74219491

 

오브젝트 - YES24

역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음은 객체를

www.yes24.com

 

 

'스터디 > 오브젝트' 카테고리의 다른 글

8장 - 의존성 관리하기  (0) 2023.05.22
7장 - 객체 분해  (0) 2023.04.30
5장 - 책임 할당하기  (0) 2023.04.10
4장 - 설계 품질과 트레이드오프  (0) 2023.03.28
3장 - 협력, 책임, 역할  (2) 2023.03.12