pandaterry's 개발로그

[책 | 오브젝트] CH3. 역할/책임/협력 본문

개발/OOP

[책 | 오브젝트] CH3. 역할/책임/협력

pandaterry 2025. 11. 9. 16:55

 

 

옛날에도 읽었지만 업무를 하면서 체화가 안되었다고 판단해서 다시 읽어보면서 정리해보려 합니다. 여러번 읽어도 괜찮은 책이라 생각하기도 하고, 더 광범위한 사례가 필요할 때 다른 책도 구매해보려 합니다.

 

암튼 목표는! 데이터 중심 사고에서 어느정도 벗어나서 객체지향적인 사고를 해봐야겠습니다.

 

1. 협력

협력이란?

  • 다양한 객체들이 영화 예매라는 기능을 구현하기 위해 메시지를 주고 받으며 상호작용하는 것
  • 여기서 협력에 참여하기 위해 수행하는 로직이 책임
  • 협력 안에서 수행하는 책임들이 모여 역할을 구성

 

메시지 전송

객체 사이 협력을 위해 사용할 수 있는 유일한 커뮤니케이션 수단


  • 왜 유일하냐면, 내부 구현에 직접 접근할 수 없기 때문. 메시지 전송만을 통해 요청을 전달가능
  • 메시지 발신자가 메서드를 실행하여 메시지를 보내고, 수신자는 메서드를 실행하여 수신한다.(상태에 담기는 것도 사실상 메서드 실행)

 

협력 = 설계를 위한 문맥을 결정

이 말은 설계를 하려면 협력관계를 잘 정의해야한다는 말이다. 서로 독립적으로 작동하는 객체는 없고 협력간에 이뤄진다. 그래서 이 협력을 위한 적절한 행동파악에 유의해야한다.

 

Movie 객체는 어떤 행동을 수행해야할까?

  • 일반적으론, play 라는 행동을 수행할 것이라 생각한다.
  • 하지만, Movie 객체안에는 상영을 위한 어떠한 코드도 없다. 대부분 요금 계산을 위한 것일뿐.
  • 결국 Movie는 요금 계산의 책임을 지고 있다는 것이다.

 

 

협력 -> 행동 -> 상태

  1. 협력을 통해 객체의 행동을 결정한다.
  2. 객체의 행동을 통해 객체의 성태를 결정한다.

절대 반대로 되면 안된다. 추상화가 깨질 뿐이라 유연하지 못한 설계가 나올 확률이 높다.

 

이렇기 때문에 협력이 행동과 그에 따른 상태까지 결정하기 때문에 문맥을 제공한다는 것이다.

 

 

2. 책임

 

책임이란?

  • 행위의 집합
  • 객체가 유지해야하는 정보 + 수행할 수 있는 행동 -> 이거를 서술한 문장
  • 무엇을 알고 있는가(knowing)? + 무엇을 할 수 있는가(doing)?
  • 객체지향 설계의 핵심(객체에 책임을 능숙하게 할당하면 그 자체로 능통한 것). 왜냐면 책임을 할당하는 순간부터 맥락이 형성된다.

 

예시

 

Screening의 책임은?

  • 아는 것 : 상영할 영화를 알아야 한다.(Movie)
  • 하는 것 : 예매 정보를 생성해야한다.

 

Movie의 책임은?

  • 아는 것 : 영화 정보를 알아야한다.
  • 하는 것 : 예매 가격을 계산해야한다.

 

DiscountPolicy 의 책임은?

  • 아는 것 : 할인 정책을 알아야한다.
  • 하는 것 : 할인된 가격을 계산해야한다.

 

DiscountCondition 의 책임은?

  • 아는 것 : 할인 조건을 알고 있어야한다.
  • 하는 것 : 할인 여부를 판단해야한다.

 

책임과 메시지의 차이?

  • 책임은 추상적이다. 간략하게 서술하는 것이다. 추상적이다보니 개념적으로 범위가 넓다.
  • 메세지는 구체적이며 개념적으로 좁다.(ex. 예매하라, 가격을 계산하라)

 

객체에게 책임 할당하기(전문가를 찾자)

 

예매하라!

  1. 예매 시스템을 보면 '예매하라' 라는 책임을 추출하기는 쉽다.
  2. 하지만, 어떤 객체에게 할당할지가 가장 문제가 되는 부분이다.
  3. 이 때, 예매를 하기 위한 정보를 소유하고 있거나, 해당 정보의 소유자를 가장 잘 알고 있는 전문가를 찾는다.
  4. Screening 은 예매를 하기 위한 대상인 Movie를 잘 알고 있어서 여기 할당한다.

 

가격을 계산하라!

  1. 영화를 예매하기 위해선 가격을 계산해야한다.
  2. Screening은 예매 가격을 위한 정보를 충분히 알고 있지 않다.
  3. 그럼 외부 객체에, 가격을 잘 아는 객체에 요청해야한다.
  4. 이를 위해선 '가격을 계산하기' 위한 새로운 메세지를 만들고 이를 위한 적절한 객체를 찾아야한다.
  5. 가격에 대한 정보를 가장 잘 아는 객체는 Movie 이다.(영화 가격이 있으니) 그래서 가격계산의 책임은 Movie가 된다.

 

할인 정보를 계산하라!

  1. Movie는 할인 정보에는 문외한이다.(=전문가가 아니다.)
  2. 이 또한 '할인 정보를 계산하라' 라는 메세지를 만든다.
  3. ....

위와 같이 반복되는 설계를 통해 책임 할당이 이뤄진다.


 

메세지가 결국 객체를 결정한다.

위 과정에서 느낀 가장 중요한 포인트는 아래와 같다.

 

원칙

  1. 책임을 할당하는데 필요한 메시지를 먼저 식별한다.
  2. 메시지를 처리할 객체를 나중에 선택했다.

이렇게 하면 다음과 같은 이유가 있다.

 

이유

    1. 객체는 최소한의 인터페이스를 가질 수 있다.
      -> 메시지를 알아야만 추가하기 때문에 불필요한 내용이 추가되지 않는다.
    2. 객체는 충분히 추상적인 인터페이스를 가질 수 있게 된다.
      -> 객체는 어떻게 수행하는지를 노출하면 안된다. 무엇을 하는지만 메시지에 노출되기 때문에 추상적이게 되는 것이다.

 

행동이 상태를 결정한다.

 

객체를 추가하는 시점

- 객체는 협력에 참여하기 위해 존재한다.

- 반대로 말하면 협력이 필요없는 상태에선 추가 객체를 만들 필요가 없다.

- 그래서 객체가 객체다워지려면 객체의 상태가 아닌 객체가 다른 객체에게 제공하는 행동(협력)에 집중해야한다.

 

상태먼저? -> 데이터 주도 설계(O), 객체 주도 설계(X)

 

초보자들은 객체에 먼저 필요한 상태가 무엇인지 파악한다.

그리고 상태에 필요한 행동을 결정한다.

이렇게 되면 다음과 같은 단점이 존재한다.

 

객체의 내부 구현이 객체의 인터페이스에 노출되고마니 캡슐화를 저해한다.
객체 내부 구현 변경이 의존하는 객체에도 영향이 간다는 말이다

 

이런 개발 방법은 데이터 주도 설계이다.

 

상태는 신경쓰지 마라.

상태는 단순히 협력을 위해 만들어진 부산물이라 생각해라.

 

 

3. 역할

객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합이다. 역할에게 책임을 할당한다고 보면 된다. 그리고 객체와 역할이 1:1 대응이 될수도 혹은 더 추상적인 개념이 될수도 있다.

 

역할을 찾기

 

영화를 예매하라!

  1. 영화를 예매할 수 있는 적절한 역할은 무엇일까?
  2. 역할을 수행할 객체로 익명의 역할은 상영이다.
  3. 상영에 어울리는 객체는 Screening이니 이 객체를 선택한다.

 

가격을 계산하라!

  1. 가격을 계산할 수 있는 적절한 역할은 무엇일까?
  2. 역할을 수행할 객체로 익명의 역할로 영화이다.
  3. 영화에 어울리는 객체는 Movie 이니 이 객체를 선택한다.

 

'유연한 & 재사용가능한' 협력이란?

우선 역할이란게 없으면 유연하지 못하고 재사용이 불가능한 협력이 만들어질 수 있다.

 

할인가격을 계산하라!(잘못된 예시)

  1. Movie는 단순 가격 계산이 아닌 할인가격을 계산하기 위해 아까 DiscountPolicy에게 책임을 할당한 걸 보았을 것이다.
  2. 하지만!, 할인정책은 비율할인, 금액 할인 2가지가 있다.
  3. 역할이 없이 이 정책에 따라 바로 메시지에 따른 책임을 할당하려면 AmountDiscountPolicy, PercentDiscountPolicy 인스턴스 2개가 만들어져야한다.
  4. 이러면 코드중복이 발생해서 유연하지 못하고, 재사용이 불가능해진다.

 

할인가격을 계산하라!(잘된 예시)

  1. AmountDiscountPolicy, PercentDiscountPolicy 는 모두 할인 요금 계산이라는 동일한 책임을 갖고 있다.
  2. 할인 요금을 계산하는 역할을 만들면 된다.
  3. 그게 바로 DiscountPolicy 이다. 객체를 포괄하는 추상화된 개념이 역할이다.

 

객체 VS 역할

 

역할

객체가 참여할 수 있는 일종의 슬롯이다.

 

오직 하나의 객체만 협력에 참여한다면?

여러 객체가 협력한다면 객체는 역할과 1:1이 반드시 된다고 말할 수 없지만, 그게 아니라 하나의 객체만 협력에 참여한다면 객체:역할 = 1:1에 대응한다고 볼 수 있다.


 

역할, 추상화

 

추상화를 이용한 설계의 장점

  1. 추상화 계층만을 사용하면 중요한 정책을 상위 수준에서 단순화 가능하다.
  2. 설계가 좀 더 유연해진다.

 

역할 -> 추상화

역할도 추상화의 일종이기 때문에 위 2가지 장점을 모두 가져갈 수 있다.

    1. 세부사항에 억눌리지 않고 상위 수준의 정책을 쉽고 간단하게 표현가능
      -> 할인 가격 계산도 사실상 금액할인 정책, 비율 할인정책을 순번조건, 기간 조건과 조합해서 다양한 요금 계산 규칙을 설정할 수 있다. 하지만 이는 큰 그림을 파악하는데 방해만 할 뿐이다. 이걸 DiscountPolicy -> DiscountCondition으로 표현하면 바로 이해가 된다.
    2. 설계를 유연하게 만들 수 있다.
      -> 협력 안에서 동일한 책임을 수행하는 객체들은 동일한 역할을 하기에 서로 대체 가능해진다. 역할은 환경에 따라 다양한 객체들을 수용하기 때문에 협력이 유연해진다. DiscountPolicy, DiscountCondition 이라는 역할을 수행하는 어떤 객체라도 요금 계산 협력에 참여가 가능하다.

 

비유 : 배우 & 배역

대응 관계

  • 배우 : 객체
  • 배역 : 역할

 

예시

  • 배우는 하나의 영화에서 여러 배역을 가질 수 있다.(1인 2역)
    • 객체도 여러 역할을 가질 수 있다.
  • 서로 다른 배우가 동일한 배역을 연기할 수 있다.
    • 여러 객체가 동일한 역할을 가질 수 있다.
  • 배역은 영화가 상영되는 동안만 존재하는 일시적인 개념이다.
    • 역할은 특정 상황에만 존재하는 객체에 대한 일시적인 개념이다.