서적/Object

6장 메시지와 인터페이스 / 8장 의존성 관리하기

Mo_bi!e 2026. 2. 24. 22:57

6장 메시지와 인터페이스

  • 애플리케이션은 클래스로 구성되지만, 메시지를 통해 정의된다.

1. 협력과 메시지

클라이언트 - 서버 모델

  • 클라이언트 - 서버 모델은 두 객체 사이의 협력관계 설명하기 위해 사용하는 메타포이다.
  • 객체가 독립적으로 수행할 수있는 것보다 더 큰 책임을 수행하기위해서는 다른 객체와 협력해야한다
  • 메시지 전송자와 수신자는 서로에 대한 상세한 정보를 모른 채 단지 메시지라는 얇은 끊으로 연결된다.
  • 수신가능한 메시지가 객체의 퍼블릭 인터페이스와 오퍼레이션을 결정한다

2. 인터페이스 설계와 품질

  • 좋은 인터페이스는 최소한의 인터페이스(꼭 필요한 오퍼레이션만)와 추상적인 인터페이스(무엇을 하는지 표현 -> 메시지를 먼저 선택 / 메시지가 객체를 선택) 조건을 만족해야한다

1. 디미터 법칙

  • 객체의 내부 구조에 대한 결합으로 인해 발생하는 설계문제를 해결하기 위해서 탄생
  • 객체의 내부 구조에 강하게 결합되지 않도록 협력경로 제한하라
  • 이를 위해서는 클래스가 특정한 조건을 만족하는 대상에게만 메시지 전송하도록 해야한다 -> shy code 작성 가능
  • 디미터 법칙은 캡슐화의 다른 관점에서 표현한것이다
    • 디미터 법칙은 클래스를 캡슐화하기 위해 따라야하는 구체적인 지침 제공
      • 지침 : 캡슐화를 지키기 위해 접근해야하는 요소 제한
  • 기차 충돌(train wreck) : 여러대의 기차가 한줄로 늘어서 충돌한 것 처럼 보임.
    • 클래스의 코드 내부 구현이 외부로 나타났을 때 의미
    • 디미터 법칙 지키지 않으면 기차 충돌이라고 부른다

2. 묻지 말고 시켜라

  • 훌륭한 메시지는 객체의 상태에 관해 묻지 말고 원하는 것을 시켜야한다는 사실
  • 절차적인 코드는 정보를 얻은후에 결정하지만, 객체지향 코드는 객체에게 그것할 도록 시킨다
    • 결국 자연스럽게 정보 전문가에게 책임을 할당하게 되고, 높은 응집도를 가진 클래스를 얻을 수있다.
  • 위 두 원칙은 퍼블릭 인터페이스 품질을 향상시키는 좋은 습관이다.
    • 즉 How가 아닌 What 을 서술한다술한다

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

  • 켄트벡은 메서드 명명하는 2가지 방법 설명

    1. 메서드가 작업을 어떠게 수행하는지를 나타내도록 이름 짓기
      • 동일한 작업에 대해서 객체별 메서드명이 다르다면, 동일작업을 하는지 알아채기 어렵다
      • 클라이언트로 하여금 협력의 종류를 알도록 강요한다강요한다
    2. 어떻게가 아니라 무엇을 하는지 드러내기
    • 동일한 작업을 하는 메시드를 동일한 메서드명으로 하면 혼란스럽다 (강의에서 같은 경험 함), 이름을 통해 명확하게 표현이 가능하다
    • 방법은 인터페이스를 정의하고, 인터페이스에 오퍼레이션을 정의한다
  • 무엇을 하는지 드러니는 방식을 의도를 드러내는 선택자 라고 부른다

    • 마치 방적식을 푸는 방법을 제시하지 말고 공식으로 표현하라 / 문제를 내라, 하지만 문제를 푸는 방법을 표현해서는 안된다

4. 함께 모으기

  • 디미터 법칙 / 묻지 말고 시켜라 / 의도를 드러내는 인터페이스 3가지를 조합해야한다
  • 디미터 법칙, 묻지 말고 시켜라
    • 이 2가지를 하게 되면 협력을 얻게된다. 그 결과 결합도와 응집도가 개선된다
    • 그런데, 인터페이스가 클라이언트의 의도를 반영했는지 확인이 필요하다
  • 의도 반영 관련
    • 3개의 객체에서 각 메서드가 모두 같은 이름을 가지고 있으면 클라이언트 개발자에게 혼란스럽다
    • 의도가 드러나도록 퍼블릭 인터페이스 개선 필요
    • 결국 협력이라는 문맥을 반영해야한다
  • 정리하면
    • 디미터 법칙 : 협력을 설계 시 캡슐화를 위반하는 메시지가 인터페이스에 포함 되지 않도록 한다
    • 묻지 말고 시켜라 원칙 : 디미터 법칙을 준수하는 협력을 만들기위한 스타일 제시
    • 의도를 드러내는 인터페이스 : 어떤 이름이 드러나야하는지에 대한 지침으로서 코드의 목적을 명확하게 커뮤니케이션을 할 수있게 해준다.

3. 원칙의 함정

  • 하지만 절대적인 법칙이 아니다.
    • 설계는 트레이드 오프의 산물이다. 이것이 숙련자와 초보자를 구분하는 기준이다.

1. 스트림

  • 스트림은 디미터 법칙 위반이 아니다
    • 디미터 법칙은 결합도 관련된것인데, 스트림은 캡슐이 그대로 유지된다.
    • 어떤 경우도 내부에 대해 묻지 않는다

2. 결합도와 응집도의 충돌

  • 맹목적으로 위임 메서드를 추가하면 상관없는 책임들도 떠안게 되어서 응집도가 낮아진다.
    • 예) 본질인 영화 예매의 책임을 넘어서, 조건 판단 까지 맡게되는 경우로 됨 (나도 이렇게 리팩터링 했다)
      • 이런경우에 차라리 캡슐화 향상보다 응집도를 높이고 결합도를 낮추는것이 전체적으로 더 바람하다

4. 명령-쿼리 분리 원칙

1. 프로시저와 함수 / command 와 query / manipulator 와 builder 의 차이?

구분 상태 변경 (Side Effect O) 값 반환 (Side Effect X)
1. 책의 용어 (루틴) 프로시저 (Procedure) 함수 (Function)
2. 인터페이스 용어 명령 (Command) 쿼리 (Query)
3. 엘레강트 오브젝트 조정자 (Manipulator) 빌더 (Builder)
특징 • 객체의 내부 상태를 변경함
값을 반환하지 않음 (void) • 객체의 정보를 반환함
상태를 변경하지 않음
비유 (기계) 네모 버튼 (누르면 기계가 작동함) 디스플레이 (상태를 보여주기만 함)
  • 이런 방식을 명령-쿼리 인터페이스 라고 부른다
  • 하나의 메서드가 명령-쿼리 두가지 역할을 동시에 수행하면 버그발생을 찾기가 어렵다.
    • 이점 : 어떤 메서드가 부수 효과를 가지는지를 확인하기 위해 코드를 일일이 다 분석하는 것 보다는 메서드가 반환값을 가지는지 여부만 확인하는것이 간단하다
    • 즉 코드가 예측가능하고, 이해하기 쉬우며, 디버깅이 용이하고, 유지보수가 수월해 진다.

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

  • 참조 투명성 : 어떤 표현식 e가 있을 때, e 의 값으로 e가 나타내는 모든 위치를 교체하더라도 결과가 달라지지 않는 특성
    • 수학은 이를 만족하는 대표적인 예다.
    • 즉 부수효과가 발생하지 않는다는 것은 불변성이라고 부른다
      • 불변은 참조 투명성을 만족시킨다
  • OOP는 상태변경이라는 부수효과 기반이므로 예외에 가깝다.
    • 하지만 명령-쿼리 분리원칙을 사용하면 균열을 줄일 수 있다.
  • 이와 마찬가지로 FP 도 부수효과가 존재하지 않는 수학적인 함수에 기반한다 (강의에서 FP가 왜 좋은지 더 이해할 수 있게 됨)

요지 : 예측가능한 협력

8장 : 객체 분해

  • 객체 지향 설계의 핵심은 협력을 위해 필요한 의존성을 유지하면서도 변경을 방해하는 의존성을 제거하는 것이다.
  • 객체 지향 설계란 의존성을 관리하는 것이고, 객체가 변화를 받아들일 수 있게 의존성을 정리하는 기술

1. 의존성 이해하기

  • 의존성은 실행시점에 따라 다른의미를 가진다.

    • 런타임의존성 / 컴파일 타임 의존성
  • 의존성 : 함께 변경될 수 있는 가능성

  • 의존성 전이 : 직접의존성과 달리 직접 존재하지는 않지만, 영향이 전파되는 간접 의존성을 의미한다.

  • 런타임의존성과 컴파일 타임 의존성은 별개의 독립성을 갖는다

    • 생태계의 동적인 성질을 식물과 동일과 같은 정적분류 구조로 이해하는것과 같다
  • 컨텍스트 독립성 의미 : 특정한 문맥에 최소의 가정만 이루어지면, 다른 문맥에서 재사용하기 수월해진다

    • 유연한 설계를 위해서는 구체적인 클래스를 알아서는 안된다. 그러면 특정한 문맥에 강하게 결합된다
    • 즉 컨텍스트 정보가 적을수록 다양한 컨텍스트에서 재사용 가능하다
  • 의존성 해결 : 컴파일 타일 의존성을 런타임 의존서을 교체하는것을 의미한다.

    • 방법 : 생성자, setter, 메서드 실행

2. 유연한 설계

  • 의존성이 과하면 문제 될 수있다.

    • 문제는 의존성 존재가 아니라 의존성 정도이다.
      • 바람직한 의존성을 만드는게 중요하다
    • 바람직한 의존성이란 재사용성과 관련되어있다. 즉 컨텍스트 독립적인 의존성이 필요하다.
  • 바람직한 의존성에 관한 용어가 바로 결합도이다.

    • 바람직할 때는 tight, strong coupling / 바람직하지 못할 때 loose, weak coupling
  • 서로에 대한 지식의 양이 결합도를 결정한다

  • 명시적인 의존성

    • 인터페이스 인스턴스 변수에 대해서 생성자 내부에서 인자로 안받고, 구체 클래스를 new 해서 넣으면 의존하게된다. (로또 과제때 나도 이렇게 함)
      • 이런 '생성자의 인자로 전달받는 방법(명시적인 의존성)', '생성자 안에서 직접 생성하는 방법(숨겨진 의존성)'의 차이점은 퍼블릭 인터페이스를 통해 할인 정책 설정 방법 제공 여부
    • 이런 경우 변경시 내부구조 직접변경한다. 코드 수정은 결국 버그 발생가능성을 내포한다
  • 경계할 것은 의존성 자체가 아니라, 의존성을 감추는 것이다

  • new 를 잘못사용하면 결합도가 극단적으로 높아진다.

    1. 구체이름을 기술해야하고, 추상화가 아닌 구체클래스에 의존해서 결합도가 높아진다.
    2. 클라이언트가 어떤 생성자 호출해야하는지 알아야해서, 지식의 양이 늘어나 결합도가 높아진다.
    • 해결방법은 인스턴스를 생성하는 로직과 사용하는 로직을 분리한다
      • 즉 외부 주입 방식(DI)이다
  • 예외적인 경우로 생성자 체이닝방식을 이용한다 (주생성자-부생성자 방식)

    • 다양한 컨텍스트에서 유연하게 사용될 수있는 여지를 준다.
  • 컨텍스트 확장하기

    • 생성자 체이닝으로 null 을 하여서 할인혜택 제공하지 않는 방법은 결국 코드 내부 수정이다
      • 차라리 할인정책 종류로 하는것이 간단하다.
    • 또한 할인 중복 적용이다.
      • 마찬가지로 종류를 추가하면된다.
  • 어떤 객체와 협력하느냐에 따라 객체의 행동이 달라지는것은 유연하고 재사용 가능한 설계가 가진 특징이다.

    • what으로 표현하는 클래스로 구성한다

요지 : 훌륭한 설계는 객체가 어떻게 하는지 표현하는게 아니라, 객체들의 조합을 선언적으로 표현함으로써 객체들이 무엇을 하는지를 표현하는 설계이다

'서적 > Object' 카테고리의 다른 글

10장 상속과 코드 재사용  (3) 2026.03.08
9장 유연한 설계  (6) 2026.03.04
5장 책임 할당하기  (3) 2026.02.08
4장 설계품질과 트레이드 오프  (3) 2026.02.03
2장 객체지향 프로그래밍 / 3장 역할, 책임, 협력  (4) 2026.01.29