NLP Blog

3. 역할, 책임, 협력

|

3. 역할, 책임, 협력

  • 객체지향 패러다임의 관점에서 핵심은 역할, 책임, 협력
  • 클래스, 상속, 지연 바인딩은 구현 픅면에 치우쳐있어 객체지향 패러다임의 본질과는 거리가 있다.
  • 객체지향의 본질은 협력하는 객체들의 공동체를 창조 하는 것
  • 협력 구성 -> 적절한 객체 찾기 -> 적절한 책임 할당; 클래스와 상속은 객체들의 책임과 협력이 어느 정도 자리를 잡은 후에 구현하는 것
  • 이런 설계가 되기 전 구현을 해버리면 변경하기 어렵고 유연하지 못한 코드를 낳을 수 있다.

1. 협력

영화 예매 시스템 돌아보기

  • 그림 3.1 참고
  • 객체 지향 원칙을 따르는 애플리케이션의 제어 흐름은 어떤 하나의 객체에 의해 통제되지 않고 다양한 객체들 사이에 균현 있게 분배되는 것이 일반적
  • 중요한 것은 다양한 객체들이 영화 예매라는 기능을 구현하기 위해 메시지를 주고받으면서 상호작용한다는 것
  • 객체들의 상호작용을 협력, 객체가 협력에 참여하기 위해 수행하는 로직은 책임, 객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성

협력

  • 메세지 전송(message sending)은 객체 사이의 협력을 위해 사용할 수 있는 유일한 커뮤니케이션 수단
  • 객체는 다른 객체의 상세한 내부 구현에 직접 접근할 수 없기 때문에 (캡슐화) 오직 메시지 전송을 총해서만 자신의 요청은 전달
  • 메시지를 수신한 객체는 메서드를 실행해 요청에 응답
    • 객체가 메시지를 처리할 방법을 스스로 선택한다는 점이 중요 -> 객체는 자신의 일을 스스로 처리할 수 있는 자율적인 존재
  • 영화 예매 예제에서 ScreeningMovie 협력을 보면, ScreeningMovie에게 caculateMovieFee 메시지를 전송하여 요금 계산을 요청
    • 이렇게 위임을 하는 이유는 요금에 대해서 가장 잘 아는 객체가 Movie이기 때문이다
    • 만약 요금 계산을 Screening이 하려고 한다면, Movie 내부 구현에 직접 접근해야하고, 이는 변경하기 어려운 코드를 만든다
    • 또한 Movie의 자율성이 훼손됨
    • 결과적으로 객체를 자율적으로 만드는 가장 기본적인 방법은 내부 구현을 캡슐화하는 것 -> 변경에 대한 파급효과 제한 가능

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

  • 어떤 객체도 섬이 아니다
  • 애플리케이션 안에 어떤 객체가 필요하다면 그 이유는 그 객체가 어떤 협력에 참여하고 있기 떄문
  • 협력에 참여할 수 있는 이유는 협력에 필요한 적절한 행동을 보유하고 있기 떄문
  • 객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력
    • 협력이 바뀌면 객체가 제공해야 하는 행동 역시 바뀌어야 함
  • 예제의 Movieplay 메서드가 없는 이유가 뭘까? 우리의 예제는 예매를 위한 어플리케이션이기 때문이다


  • 객체의 상태를 결정하는 것은 행동
  • 객체의 상태는 그 객체가 행동을 수행하는 데 필요한 정보가 무엇인지로 결정됨
  • MoviefeediscountPolicy라는 인스턴스 변수를 상태의 일부로 포함하는 이유는 요금 계산이라는 행동을 수행하는데 필요하기 때문
  • 상태는 객체가 행동하는데 필요한 정보에 의해 결정
  • 행동은 협력 안에서 객체가 처리할 메시지로 결정
  • 결과적으로 객체가 참여하는 협력이 객체를 구헝하는 행동과 상태 모두를 결정
  • 협력은 문맥(context)을 제공

2. 책임

책임이란 무엇인가

  • 협력이 갖춰진 이후에는 협력에 필요한 행동을 수행할 수 있는 적절한 객체를 찾아야 한다
  • 이때 협력에 참여하기 위해 객체가 수행하는 행동을 책임이라고 부른다
  • 책임이란 객체에 의해 정의되는 응집도 있는 행위의 집합
    • 객체가 유지해야 하는 정보와 수행할 수 있는 행동에 대해 개략적으로 서술한 문장, 이는 하는 것아는것으로 나뉜다


  • 하는 것
    • 객체를 생성하거나 계산을 수행하는 등의 스스로 하는 것
    • 다른 객체의 행동을 시작시키는 것
    • 다른 객체의 활동을 제어하고 조절하는 것
  • 아는 것
    • 사적인 정보에 관해 아는 것
    • 관련된 객체에 관해 아는 것
    • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것


  • 그림 3.3, 객체의 책임을 CRC카드로 표현
  • 협력 안에서 객체에게 할당한 책임이 외부의 인터페이스와 내부의 속성을 결정
  • 책임의 관점에서 ‘아는 것’과 ‘하는 것’이 밀접하게 연관돼 있다.
    • 객체는 자신이 맡은 책임을 수행하는 데 필요한 정보를 알고 있을 책임이 있음
    • 자신이 할 수 없는 작업을 도와줄 객체를 알고 있을 책임이 있다
  • 책임은 객체지향 설계의 핵심
  • 적절한 협력이 적절한 책임을 제공 -> 적절한 책임을 적할한 객체에게 할당 == 단순하고 유연할 설계
  • 다시 한 번 강조 : 겍체지향 설계에서 가장 중요한 것은 책임, 구현 방법은 그 뒤의 일

책임 할당

  • 자율적인 객체를 만드는 가장 기본적인 방법은 책임을 수행하는 데 필요한 정보를 가장 잘 알고 있는 전문가에게 그 책임을 할당 -> INFORMATION EXPERT pattern
  • 객체에게 책임을 할당하기 위해서는 먼저 협력이라는 문맥을 정의해야 함
  • 협력 설계의 출발점 : 시스템이 사용자에게 제공하는 기능을 시스템이 담당할 하나의 책임으로 바라보는 것
    • 객체지향 설계는 시스템의 책임을 완료하는 데 필요한 더 작은 책임을 찾아내고, 이를 객체들에게 할당하는 반복적인 과정
  • 영화 예매 예제
1: 예매하라 -> :Screening -> 2: 가격을 계산하라 -> :Movie
  • 예매 전문가 : Screening, 가격 전문가 : Movie, 할인 전문가 : DiscountPolicy, …
  • 객체 지향 설계는 협력에 필요한 메시지를 찾고 메시지에 적절한 객체를 선택하는 반복적인 과정을 통해 이뤄진다.
  • 이렇게 결정된 메시지가 객체의 퍼블릭 인터페이스를 구성
  • 협력을 설계하면서 객체의 책임을 식별해 나가는 과정에서 최종적으로 얻게 되는 결과물은 시스템을 구성하는 객체들의 인터페이스와 오퍼레이션의 목록

책임 주도 설계

  • 책임 주도 설계 (Resposibility-Driven Design, RDD) : 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법
    • 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
    • 시스템 책임을 더 작은 책임으로 분할
    • 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당
    • 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다
    • 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 함

메시지가 객체를 결정한다

  • 객체에게 책임을 할당하는 데 필요한 메시지를 먼저 식별하고 메시지를 처리할 객체를 나중에 선택
  • 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 함
  • 메시지가 객체를 선택하게 해야하는 두 가지 중요한 이유
    • 객체가 최소한의 인터페이스(minimal interface)를 가질 수 있음
      • 필요한 메시지가 식별될 때까지 객체의 퍼블릭 인터페이스에 어떤 것도 추가하지 않기 때문에, 객체는 애플리케이션에 꼭 필요한 크기의 퍼블릭 인터페이스를 가질 수 있음
    • 객체는 충분히 추상적인 인터페이스(abstract interface)를 가질 수 있게 됨
      • 객체의 인터페이스는 무엇what을 하는지는 표현해야 하지만 어떻게how 수행하는지를 노출해서는 안된다
      • 메시지는 외부의 객체가 요청하는 무언가를 의미하기 때문에 메시지를 먼저 식별하면 무성ㅅ을 수행할지에 초점을 맞추는 인터페이스를 얻을 수 있다
  • 결과적으로 협력을 구성하는 객체들의 인터페이스는 충분히 추상적인 동시에 최소한의 크기를 유지할 수 있음

행동이 상태를 결정한다

  • 객체가 존재하는 이유는 협력에 참여하기 위해서
  • 객체는 협렵에 필요한 행동을 제공해야 함
  • 객체를 객체답게 만드는 것은 객체의 상태가 아니라 객체가 다른 객체에게 제공하는 행동
  • 객체가 협력에 접합한지를 결정하는 것은 그 객체의 상태가 아니라 행동이다
  • 얼마나 적절한 객체를 창조했느냐는 얼마나 적절한 책임을 할당했느냐에 달려있고, 책임이 얼마나 적절한지는 협력에 얼마나 적절한가에 달려있음
  • 초보자들의 흔한 실수 : 객체의 상태를 결정 한 이후 상태에 필요한 행동을 결정
    • 이런 방식은 객체의 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만드므로 캡슐화를 저해
    • 객체의 내부 구현을 변경하면 퍼블릭 인터페이스도 함께 변경되고, 결국 객체에 의존하는 클라이언트로 변경의 영향이 전파
  • 캡슐화를 위반하지 않도록 구현에 대한 결정을 뒤로 미루면서 객체의 해우이를 고려하기 위해서는 항상 협력이라는 문맥 안에서 객체를 생각해야 함
  • 개별 객체의 상태와 행동이 아닌 시스템의 기능을 구현하기 위한 협력에 초점을 맞춰야만 응집도가 높고 결합도가 낮은 객체들을 창조 할 수 있다.
  • 상태는 단지 객체가 행동을 정상저으로 수행하기 위해 필요한 재료일 뿐
  • 행동이 중요!

3. 역할

역할과 협력

  • 객체의 목적은 협력 안에서 객체가 맡게 되는 책임의 집합 -> 역할
  • 영화 예매 협력에서 예매하라라는 메시지 처리의 과정
    • 영화를 예매할 수 있는 적절한 역할이 무엇인지 찾는 것
    • 역할을 수행할 객체로 Screening 인스턴스를 선택
  • 사실은 역할을 찾고 객체를 선택

유연하고 재사용 가능한 협력

  • 역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있기 때문
  • 할인 요금을 계산 할 때 가격 할인 정책 마다 협력을 개별적으로 만들어야 하는가? -> 아니다, 코드 중복이 일어남
  • 객체가 아닌 책임에 초점을 맞추어야 함. 가격 할인 정책들은 할인 요금 계산이라는 동일한 책임을 수행한다
  • 객체라는 존재를 지우고 할인 요금을 계산하라라는 메시지에 응답할 수 있는 대표자를 생각한다면 두 협력을 하나로 통합할 수 있음, 이러한 객체의 슬록을 역할이라고 생각할 수 있다

역할은 다른 것으로 교체할 수 있는 책임의 집합이다[Wirfs-Brock03]

  • 여기서의 역할이 두 종류의 구체적인 객체를 포괄하는 추상화라는 점에 주목
  • 요점은 동일한 책임을 수행하는 역할을 기반으로 두 개의 협력을 하나로 통합할 수 있다는 것
  • 따라서 역할을 이용하면 불필요한 중복 코드를 제거할 수 있음
  • 책임과 역할을 중심으로 협력을 바라보는 것이 바로 변경과 확장이 용이한 유연한 설계로 나아가는 첫걸음

역할의 구현

  • 역할을 구현하는 가장 일반적인 방법은 추상 클래스인터페이스를 사용하는 것
  • 추상 클래스와 인터페이스는 구체 클래스들이 따라야 하는 책임의 집합을 서술한 것
  • 추상 클래스는 책임의 일부를 구현해 놓은 것, 인터페이스는 일체의 구현 없이 책임의 집합만을 나열해 놓은 것
  • 역할이 다양한 종류의 객체를 숭할 수 있는 일종의 슬록이자 구체적인 객체들의 타입을 캡슐화하는 추상화
  • 일단 협력 안에서의 역할이 어떤 책임을 수행해야 하는지를 결정하는 것이 중요
  • 역할을 구현하는 방법을 그 다음 문제
  • 객체에게 중요한 것은 행동이며, 역할은 객체를 추상화해서 객체 자체가 아닌 협력에 초점을 맞출 수 있게 함

객체 대 역할

  • 역할은 객체가 참여할 수 있는 일종의 슬록
  • 만약 오직 한 종류의 객체만 협력에 참여하는 상황에서 역할이라는 개념을 고려하는 것이 유용할까?
    • 레베카 워프스브록의 말에 따르면 협력에 참여하는 후보가 여러 종류의 객체에 의해 수행될 필요가 있다 -> 역할, 단지 한 종류의 객체만이 협력에 참여할 필요가 있다 -> 객체
  • 설계 초반에는 어떤 것이 역할이고 어떤 것이 객체인지가 또렷하게 드러나지는 않을 것
  • 설계 초반에는 적절한 책임과 협력의 큰 그림을 탐색하는 것이 가장 중요한 목표여야 하고 역할과 객체를 명확하게 구분하는 것은 그렇게 중요하지는 않다
  • 애매하다면 단순하게 객체로 시작하고, 반복적으로 책임과 협력을 정제해가면서 필요한 순각에 객체로부터 역할을 분리해내는 것이 가장 좋은 방법

  • 처음에 특정 시나리오에 대한 협력을 구상할 때는 아마도 도메인 모델에 있는 개념들을 후보로 선택해 직접 책임을 할당할 것
  • 다양한 시나리오를 설계로 옮기면서 협력을 지속적으로 정제하다 보면 두 협력이 거의 유사한 구조를 보인다는 것을 발견하게 됨
  • 이 경우 두 협력을 하나로 합치면서 두 객체를 포괄할 수 있는 역할을 고려해서 객체를 역할로 대체 가능

역할과 추상화

  • 역할은 공통의 책임을 바탕으로 객체의 종류를 숨기기 때문에 이런 관점에서 역할을 객체의 추상화로 볼 수 있음
  • 추상화의 장점
    • 세부 사항에 억눌리지 않고도 상위 수준의 정책을 쉽고 간단하게 표현할 수 있음
      • 추상화를 적절하게 사용하면 불필요한 세부 사항을 생략하고 핵심적인 개념을 강조할 수 있다
      • 93 page 예시 참조
    • 설계를 유연하게 만들 수 있음
      • 협력 안에서 동일한 책임을 수행하는 객체들은 동일한 역할을 수행하기 떄문에 서로 대체 가능
      • 프레임워크나 디자인 패턴과 같이 재사용 가능한 코드나 설계 아이디어를 구성하는 핵심적인 요소가 바로 역할

배우와 배역

  • 연극의 배역과 배우 간의 관계에 다음과 같은 특성이 존재
    • 배역은 연극 배우가 특정 연극에서 연기하는 역할
    • 배역은 연극이 상영되는 동안에만 존재하는 일시적인 개념
    • 연극이 끝나면 연극 배우는 배역이라는 역할을 벗어 버리고 원래의 연극 배우로 돌아옴
    • 서로 다른 배우들이 동일한 배여기을 연기할 수 있음
    • 하나의 배우가 다양한 연극 안에서 서로 다른 배역을 연기할 수 있음\
  • 연극 안에서 배역을 연기하는 배우라는 은유는 협력 안에서 역할을 수행하는 객체
    • 하나의 배역을 여러 배우가 연기할 수 있는 것처럼 동일한 역할을 수행하는 하나 이상의 객체들이 존재 가능 함
    • 배우가 여러 연극에 참여하면서 여러 배역을 연기할 수 있는 것처럼 객체 역시 여러 협력에 참여하면서 다양한 역할을 수행할 수 있음
      • 객체는 여러 역할을 가질 수 있지만 특정한 협력 안에서는 일시적으로 오직 하나의 역할만이 보여진다
  • 역할은 특정한 객체의 종류를 캡슐화하기 때문에 동일한 역할을 수행하고 계약을 준수하는 대체 가능한 객체들은 다형적이다.

Comments