[Object] 3. 역할, 책임, 협력
in Programming on Java
객체지향패러다임의 관점에서의 핵심은 역할(role), 책임(responsibility), 협력(collaboration)이다.
객체지향의 본질은 협력하는 객체들의 공동체를 창조하는것 이다.
객체지향 설계의 핵심은 협력을 구성하기 위해 적절한 객체를 찾고 적절한 책임을 할당하는 과정에서 드러난다.
역할, 책임, 협력을 충분히 고민한 뒤에 이를 구현하는것이 클래스, 상속, 협동인것이다.
예) 영화예매기능을 구현하기 위한 객체들 사이의 상호작용
어플리케이션의 제어흐름은 어떤 하나의 객체에 의해 통제되는것이 아니고 다양한 객체들 사이에 균형있게 분배됨.
객체들은 요청의 흐름을 따라 자신에게 분배된 로직을 실행하면서 어플리케이션 전체 기능을 완성.
객체들이 어플리케이션의 기능 구현을 위해 수행하는 상호작용을 협력 이라고 한다.
객체가 협력에 참여하기 위해 수행하는 로직은 책임 이라고 한다. 객체들이 협력 안해서 수행하는 책임이 모여 객체가 수행하는 역할을 구성한다.
협력(collaboration)
객체지향시스템 : 자율적인 객체들의 공동체.
객체 : 고립된존재가 아니라, 시스템 기능이라는 목표를 달성하기 위해 다른 객체와 협력하는 사회적인 존재.
메세지전송(message sending) : 객체 사이의 협력을 위해 사용할 수 있는 유일한 커뮤니케이션 수단. 협력 : 어떤 객체가 다른 객체에게 무엇인가 요청하는것. 메서드 : 메세지를 수신한 객체가 응답하는 방법. 수신한 메세지를 어떻게 처리할지는 수신한 객체가 자율적으로 직접 결정.
예) screening은 메세지를 전송해 movie와 협력
만약 screening이 직접 fee를 계산한다면? screening이 movie의 변수에 접근해야하고 이로이인해 movie객체의 자율성이 훼손됨.(screening객체에 결합되어버림.)
따라서 movie는 자율적인 존재가 되기 위해 자신의 정보를 자신이 이용해 직접 요금계산을 해야함.(캡슐화)
screening은 메세지만 날려서 fee를 응답받으면됨.
==> 이러한 과정이 곧 협력(collaboration)
협력이 설계를 위한 문맥을 결정한다.
객체지향 : 객체를 중심에 놓는 프로그래밍 패러다임
객체 : 상태와 행동을 함께 캡슐화하는 실행단위
객체의 행동을 결정하는것은 객체가 참여하고있는 협력이다.
예) Movie객체는 영화를 예매하기 위한 협력에 참여하고있고, 요금을 계산하는 책임을 지고있다.
Movie의 행동을 결정하는것은 영화 예매를 위한 협력이다. (Movie객체의 행동이 Play가 아닌 Fee계산인 이유)
객체의 상태를 결정하는것은 행동이다
상태(객체의 인스턴스 변수 등)은 그 객체가 행동을 수행하는데 필요한 정보가 무엇인지에 따라 결정된다.
결과적으로 객체가 참여하는 협력이 객체를 구성하는 행동과 상태를 모두 결정한다
이렇게, 협력은 객체를 설계하는데 필요한 일종의 문맥(context)을 제공한다.
책임(responsibility)
책임이란 무엇인가
협력에 참여하기위해 객체가 수행하는 행동. 아는것/하는것으로 나뉨.
1) 객체가 무엇을 알고있는가 - 아는것(knowing)
2) 객체가 무엇을 할 수 있는가 - 하는것(doing)
예) Movie는 예매 가격을 계산할 책임을 진다(doing). 이를 위해 가격과 어떤 할인정책이 적용됐는지 알고있어야한다.(knowing)
객체에게 얼마나 적절한 책임을 할당하느냐가 설계의 전체적 품질을 결정.
책임할당
정보전문가패턴(INFORMATION EXPERT) : 책임을 수행하는데 필요한 정보를 가장 잘 알고있는 전문가에게 그 책임을 할당하는것.
1) 협력이라는 문맥을 정의한다
2) 사용자에게 제공하는 기능을 시스템이 담당할 하나의 책임으로 바라본다
3) 더 작은 책임을 찾아내고 이를 개체에 할당한다. 이를 반복한다.
예시) 영화예매시스템에서 정보전문가에게 책임을 어떻게 할당할까?
1) 책임을 할당하기위해, 메세지의 이름을 정한다.
예매하라
라는 이름의 메세지르 협력을 시작해보자.
2) 메세지를 처리할 적절한 객체를 선택하자. 예매 관련된 정보를 가장 많이 아는 정보전문가는 누구인가? Screening 객체이다. Screening 객체에 책임을 할당한다.
3) 문제가생겼다. Screening은 예매 가격에 대한 정보를 충분히 갖고있지 않다. 새로운 메세지, 가격을 계산하라
는 메세지가 필요하다.
4) 가격계산에 대한 정보를 가장 많이 아는 전문가는 누구인가? Movie 객체이다. Movie객체에게 책임을 할당한다.
이렇게 객체지향 설계는 협력에 필요한 메세지를 찾고 메세지에 적절한 객체를 선택.
이렇게 결정된 메세지가 public interface를 구성.
책임 주도 설계
책임주도설계(Responsibility-Driven Design, RDD)
- 시스템이 사용자에게 제공해야하는 시스템 책임을 파악
- 책임을 더 작은 책임으로 분할
- 분할된 책임을 수행할 전문가 찾아 책임 할당
- 객체가 책임 수행 중 다른 객체 도움이 필요하면 이를 책임질 적절한 객체를 찾음
- 해당 객체에게 역할 또는 책임 할당. 두 객체의 협력.
메시지가 객체를 결정한다.
위 과정에서 보았듯, 메시지를 먼저 식별하고 메시지를 처리할 객체를 나중에 선택.
이렇게 하는 이유는 2가지가 있다.
1) 객체가 최소한의 인터페이스를 가질수있게된다. 객체는 꼭 필요한 크기의 public interface를 가질 수 있다.
2) 충분히 추상적인 인터페이스(기능을 포괄하는 인터페이스)를 가질수있게된다.
행동이 상태를 결정한다.
초보자들의 가장 큰 실수. 객체에 필요한 상태가 뭔지 먼저 고민하고 그 후에 상태에 필요한 행동을 결정. 이러한 설계는 캡슐화를 저해함. (-> 데이터-주도설계 Data-Driven design)
캡슐화를 위반하지 않으려면 협력이라는 문맥 안에서 다른 객체에게 무엇을 제공하고 무엇을 얻어야하는지를 고민해야함.
그런 고민이 응집도가 높고 결합도가 낮은 객체를 창조.
중요한것은 상태가 아닌 행동이다.
역할
역할과 협력
역할 : 객체가 어떤 협럭 안에서 수행하는 책임의 집합.
실제 협력 모델링 시에는 객체가 아닌 역할에게 책임을 할당함.
예)예매하라
는 메세지 처리할 적절한 역할이 무엇일까?
-> Screening 인스턴스로 역할을 수행하자!
유연하고 재사용 가능한 협력
역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 만들어낼 수 있기 때문이다.
예) 할인요금 계산시, 두가지 종류의 할인 정책(금액할인, 퍼센트할인)이 존재하고 AmountDiscountPolicy와 PercentDiscountPlicy 인스턴스 모두 할인 요금을 계산하라
는 메세지에 응답 가능해야함. 그렇다면 두종류의 객체가 참여하는 협력을 아래와같이 개별적으로 만드는게 맞는가?
-> 이러한 코드중복은 모든 문제의 원인.
-> 객체라는 존재를 지우고 대신 객체를 바꿔끼울수있는 슬롯을 넣으면 두 협력을 하나로 통일 가능. 이 슬롯이 바로 역할.
-> 역할은 두 종류의 구체적인 객체를 포괄하는 추상화.
-> 이 역할은 DiscountPolicy로 추상화된다.
이렇듯, 역할을 이용하면 불필요한 중복코드를 제거할수있다.
협력은 더욱 유연해진다.
이렇게 책임과 역할을 중심으로 협력을 바라보는것이 변경과 확장이 용이한 유연한 설계로 나아가는 첫걸음이다.
역할의 구현
역할을 구현할때 가장 일반적인 방법이 바로 추상클래스(abstract)와 인터페이스(interface).
추상클래스는 책임의 일부를 구현한다.
인터페이스는 일체의 구현 없이 책임의 집합만을 나열한다.
객체 대 역할
역할은 객체가 참여할 수 있는 일종의 슬롯이다.
그렇다면 오직 한 종류의 객체만 협력에 참여하는 상황에서는?
그때는 역할이 아닌 객체로 간주하면 된다.
여러 종류의 객체들이 참여할수있을때 역할이라고 부르면 된다.
사실 이걸 정확히 구분하는건 별로 중요치 않고 책임과 협력을 정제하면서 필요한 순간에 객체로부터 역할을 분리해내는게 가장 좋은방법이다.
역할과 추상화
추상화를 이용한 설계는 두가지 장점을 가진다.
1) 중요한 정책을 상위수준에서 단순화 가능
2) 설계가 좀더 유연해짐
역할은 객체의 추상화로 볼수있음.
따라서 불필요한 세부사항을 생략하고 핵심개념을 강조할 수 있다.
설계를 유연하게 만들 수 있다.
예) 할인정책과 할인조건을 각각 DiscountPolicy, DiscountCondition으로 추상화하여
1) 상위수준에서 협력을 설명할 수 있다.
2) 다양한 종류의 할인정책과 할인조건에도 적용될 수 유연한 협력을 만들었다.
배우와 배역
연극 안에서 배역을 연기하는 배우라는 은유는 협력 안에서 역할을 수행하는 객체들의 특성을 잘 나타낸다.
- 배우가 연극에서 배역이라는 역할을 연기하는것처럼, 객체는 협력이라는 문맥 안에서 특정한 역할을 수행한다.
- 배우가 연극이 끝나면 배역을 잊고 자기자신으로 돌아오는것처럼, 객체는 협력이 끝나면 역할을 잊고 원래의 객체로 돌아온다.
- 하나의 배역을 여러 배우가 연기할 수 있는것처럼, 하나의 역할을 여러 객체가 수행할 수 있다.
- 배우가 여러 연극에 참여하며 여러 배역을 연기하듯, 객체도 여러 협력에 참여하며 다양한 역할을 할 수 있다.
- 배우가 하나의 연극에서는 하나의 배역을 연기하는것 처럼, 하나의 협력에서 객체는 하나의 역할만이 보여진다.
협력이라는 문맥 안에서 역할은 특정한 협력에 참여에서 책임을 수행하는 객체의 일부다.
역할은 객체가 협력에 참여하는동안 잠시동안 존재하는 일시적 개념이다.
역할은 객체의 페르소나(가면)다.