상세 컨텐츠

본문 제목

[BOOK: 오브젝트 6장] 메시지와 인터페이스

Other

by 조킴 2022. 11. 7. 00:29

본문

반응형

6장 메시지와 인터페이스

 

오늘은 이전, 5장에 이어 6장을 정리해보고자 한다. 

 

6장에서는 이전에 나왔던 메시지에 대한 내용과 인터페이스 관련된 주제로서 이야기함을 알 수 있었다. 

 

한번 하나씩 살펴보도록해보자! 

 

협력과 메시지 

클라이언트 - 서버모델 

두 객체 사이의 협력 관계를 설명하기 위해 사용하는 전통적인 메타포이다. 

 

클라이언트 

  • 협력안에서 메시지를 전송하는 객체

서버

  • 메시지를 수신하는 객체

협력은 클라이언트가 서버의 서비스를 요청하는 단방향 상호작용이다. 

메시지를 통해 협력하는 클라이언트와 서버

위 예제를 통해 협력관계를 파악해보도록 하자 

 

Screening 객체는 클라이언트 , Movie는 서버로 볼수있다. 

 

Screening 객체는 Movie에게 가격을 계산하라는 요청을 보내고 Movie는 가격 계산 서비스를 제공해줌으로써 이 메시지에 응답하게 된다. 

 

해당 부분을 통해 알 수 있듯, 메시지는 두 객체 사이의 협력을 가능하게 해주는 매개체로 볼 수 있다. 

 

메시지와 메시지 전송 

메시지 

  • 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단
  • 한 객체가 다른 객체에게 도움을 요청하는걸 메시지 전송 (Message sending) 혹은 메시지 패싱(Message passing)
  • 메시지를 전송하는 객체를 메시지 전송자(Message sender)라고 한다.
  • 메시지를 수신하는 객체를 메시지 수신자(Message receiver)라고 한다.

클라이언트 - 서버 모델 관점에서 전송자는 클라이언트, 수신자는 서버라고 부르기도 한다.

 

메시지는 오퍼레이션명 (operation name)인자(argument)로 구성되며 메시지 전송은 여기에 메시지 수신자를 추가한 것이다.

 

다음은 각 언어별 메시지 전송 표기법을 나타낸 그림이다. 

언어별 메시지 전송 표기법

메시지와 메서드 

메시지를 수신했을 때 실제로 어떤 코드가 실행되는지는 메시지 수신자의 실제 타입이 무엇인가에 달려있다.

 

메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저를 메서드라고 한다. 

여기서 중요한건 코드 상에서 동일한 이름이 변수에게 동일한 메시지를 전송하더라도 객체의 타입에 따라 실행되는 메서드가 달리질 수 있다는 것이다.

 

전통적인 방식의 함수 호출은 컴파일과 런타임에 실행되는 함수가 동일하지만, 객체는 메시지와 메서드 두 가지의 서로 다른 개념을 런타임에 연결해야 하기에 컴파일 시점과 런타임의 의미가 달라질 수 있다. 

 

실행 시점에 메시지와 메서드 ( 런타임 시점 )를 바인딩하는 메커니즘 ( 동적 바인딩 ) 은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만든다.

 

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

객체는 안과 밖을 구분하는 뚜렷한 경계를 가진다. 

 

외부에서 볼 때 객체의 안쪽은 검은 장막으로 가려진 미지의 영역 즉, 외부에서는 안을 확인할 수 없다. 

 

외부의 객체는 오직 객체가 공개하는 메시지를 통해서만 객체와 상호작용할 수 있다. 

 

객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 한다.

 

퍼블릭 인터페이스에 포함된 메시지를 오퍼레이션이라고 한다. 

 

시그니처는 오퍼레이션의 이름과 파라미터 목록을 합쳐 부르는 용어

 

메시지, 오퍼레이션 메서드 사이의 관계

위 그림은 각 사이의 관계에 대해 이야기를 하고 있다. 

 

프로그래밍 언어의 관점에서 객체가 다른 객체에게 메시지를 전송하면 런타임 시스템은 메시지 전송을 오퍼레이션 호출로 해석하고 메시지를 수신한 객체의 실제 타입을 기반으로 적절한 메서드를 찾아 실행한다. 

 

퍼블릭 인터페이스와 메시지의 관점에서 보면 '메서드 호출' 보다는 '오퍼레이션 호출'이라는 단어 선택이 더 적절하다. 

 

시그니처 

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

오퍼레이션은 실행 코드 없이 시그니처만을 정의한 것이고 메서드는 이 시그니처에 구현을 더한 것이다. 

일반적으로 메시지를 수신하면 오퍼레이션의 시그니처와 동일한 메서드가 실행된다.

 

이제, 각 용어들에 대한 정리를 다시 한번 해보자 

  • 메시지
    • 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다. 
    • 일반적으로 객체의 오퍼레이션이 실행되도록 요청하는 것을 "메시지 전송"이라고 부른다.
    • 메시지는 협력에 참여하는 전송자와 수신자 양쪽 모두를 포함하는 개념이다. 
  • 오퍼레이션 
    • 객체가 다른 객체에게 제공하는 추상적인 서비스이다. 
    • 메시지가 전송자와 수신자 사이의 협력 관계를 강조하는 데 비해 오퍼레이션은 메시지를 수신하는 객체의 인터페이스를 강조한다.
  • 메서드 
    • 메시지에 응답하기 위해 실행되는 코드 블록을 메서드라고 부른다.
    • 메서드는 오퍼레이션의 구현이다. 
    • 동일한 오퍼레이션이라고 해도 메서드는 다를 수 있다.
    • 오퍼레이션과 메서드의 구분은 다형성의 개념과 연결된다.
  • 퍼블릭 인터페이스
    • 객체가 협력에 참여하기 위해 외부에서 수신할 수 있는 메시지의 묶음 클래스의 퍼블릭 메서드들의 집합이나 메시지의 집합을 가리키는 데 사용한다. 
  • 시그니처
    • 시그니처는 오퍼레이션이나 메서드의 명세를 나타낸 것으로 이름과 인자의 목록을 포함한다. 
    • 대부분의 언어는 시그니처의 일부로 반환 타입을 포함하지 않지만 반환 타입을 시그니처의 일부로 포함하는 언어도 존재한다. 

객체의 퍼블릭 인터페이스가 객체의 품질을 결정하기 때문에 결국 메시지가 객체의 품질을 결정한다고 할 수 있다. 

 

인터페이스와 설계 품질 

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

 

디미터 법칙 ( Law Of Demeter )

  • 객체의 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라는 법칙을 의미한다. 

낯선 자에게 이야기하지 마라 + 인접한 이웃하고만 말하라 

 

무슨 이야기일까?

 

해당 객체의 내부 그 안에 있는 내부의 객체와 이야기하는 것이 아닌 단순 A, B 객체와 소통하라 즉, getter를 남용하지 마라 라는 의미로 나는 들렸다. 

 

해당 부분은 다음과 같은 내용을 통해 정답이 아닐까라는 생각이 들었다. 

 

만약, 자바같이 도트 (.)를 이용해 메시지 전송을 표현하는 언어라면 오직 하나의 도트만을 사용하라 

 

TDA ( Tell Don't Ask ) 기법은 디미터 법칙을 준수하는 협력을 만들기 위한 스타일을 제시한 기법임을 다시 한번 확인이 가능했다. 

 

묻지 말고 시켜라의 원칙은 이전 포스팅에서도 많이 다루었기에 스킵! 

 

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

해당 인터페이스가 어떠한 내용인지 의도를 명확하게 들어내야 한다.

 

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

 

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

 

원칙의 함정 

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

 

즉, 위 내용이 전부 정답은 아니다 라는걸 이야기하는 의미라고 나는 생각이 들었다. 

 

축약한다면 여러 개의 도트를 사용한다고 하더라도 객체의 내부 구조를 노출하고 있지 않는다면? 이는 원칙을 어긴 것이 아니다. 즉, 내부 객체 구조를 노출하지 않는 것이 나는 핵심이라는 생각이 들었다. 

 

명령 - 쿼리 분리 원칙 

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

 

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

 

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

 

프러시저와 함수를 혼용해서 사용하는 경우가 많은데 명확하게 구분해야 한다. 

 

프러시저

  • 정해진 절차에 따라 내부의 상태를 변경하는 루틴의 한 종류

함수

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

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

 

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

 

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

 

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

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

 

마치며

이전, 내용에 나는 실무가 이론보다 우선이다라는 말이 기억이 났다. 

 

이전 중고등학교 당시 때처럼 정해진 답들이 있고 이 답을 해나가는 것이 아닌 학문 자체는 정답이 따로 존재하지 않다고 생각이 들었다.

 

즉, 실무를 통해 해당 내용에 대해 이해하고 책을 접할 때와 모르는 상태에서 책을 접하였을 때 어느 정도의 거를 내용과 좋은 내용에 대한 부분이 명확하게 갈리는 경험을 할 것이다라는 이야기를 접한 적이 있었다.

 

맞는 말이라고 나는 생각이 들었다. 

 

해당 내용 중 정답은 아니다는 이야기가 있는 것처럼 모든 이론들이 다 어쩌면 맞는 이야기가 아닐 가능성도 있다. 

 

공부를 하더라도 이러한 부분들에 대해 깨어진 눈을 가진 사람이 될 수 있도록 더욱 노력해야겠다는 생각이 들었다.

 

반응형

관련글 더보기