책임 할당을 위한 GRASP 패턴

GRASP 패턴

일반적인 책임 할당을 위한 소프트웨어 패턴.

객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙들.

영화 예매 시스템을 통해 GRASP 패턴을 익혀보자.

도메인 개념에서 출발하기

어떤 책임을 할당해야 할 지 고민해야 될 때 가장 먼저 고려해야 할 게 도메인이다.

영화에는 금액 할인 영화, 비용 할인 영화가 있다.

할인 조건에는 순번 조건, 기간 조건이 있다.

하나의 영화는 여러번 상영될 수 있고,
하나의 상영에는 여러 예매가 있을 수 있다.

그리고 한 영화에는 여러 할인 조건이 사용될 수 있다.

![](/Users/MUHN2-031/Documents/dev/ghblog/source/img/스크린샷 2021-11-29 오후 8.40.39.png)

도메인 설계는 완벽할 수 없다.
다만 앞으로의 설계와 구현에 출발점일 뿐이다.

정보 전문가에게 책임을 할당하라(information expert pattern)

애플리케이션이 제공해야 되는 기능을 애플리케이션의 책임으로 생각하라.

이 책임을 애플리케이션에 대해 전송된 메시지로 간주하고, 메시지를 책임질 첫번째 객체를 선택하는 것으로 시작하자.

영화 예매시스템은 영화 예매를 책임져야한다.

이제 이 책임을 수행하는 메시지를 결정해야 한다.

이때 메시지는 수신할 객체 중심이 아닌 발신한 객체의 의도를 반영해서 결정해야 한다.

메시지를 전송할 객체는 무엇을 원할까?

우리가 던져야 할 첫 질문이다.
누구와 협력할지는 몰라도 그 객체가 협력에서 원하는 건 분명해보인다.
영화 예매의 예시에선 영화를 예매하는 것을 원한다. 그렇다면 메시지는 예매하라가 된다.

메시지를 결정하고 나면 메시지에 적합한 객체를 선택해야 한다.

메시지를 수신할 적합한 객체는 누구인가?

이때 객체는 상태와 행동을 가진 캡슐화의 단위다.
객체는 자신의 상태를 스스로 처리하는 자율적인 존재여야 한다.
(무지성 getter, setter는 안된다!)

객체는 자신의 책임과 책임에 필요한 상태를 가져야 한다.
객체의 책임과 책임을 수행하는데 필요한 정보를 잘 아는 객체에게 책임을 할당한다.
GRASP는 이 패턴을 정보 전문가 패턴이라고 한다.

이때 정보를 알고 있다는 건 데이터를 저장하고 있다는 의미와는 살짝 다르다.
정보를 저장하지 않아도, 어떤 객체를 알고 있거나, 필요한 정보를 계산을 제공하는 방식 등 다양하다.

정보 전문가가 정보를 알고 있다는 것은 반드시 정보를 저장한다는 의미는 아니다!

영화 예매 시스템의 예시에서는 어떨까?

예매하라는 메시지를 잘 수행할 객체를 찾아보면 된다.

도메인 개념에서 아마 상영 객체가 잘 수행할 것 같다.
왜냐면 예매하는 책임에는 상영 시간, 상영 순번, 상영 영화 등 정보가 필요한데, 이런 정보는 상영 객체가 가지는게 자연스럽다.
따라서 영화 예매 정보 전문가는 상영이다.

정보 전문가가 메시지를 처리하는 흐름을 생각하라.

메시지와 메시지를 처리할 객체를 정했다면 이제 그 정보 전문가가 내부에서 어떻게 책임을 다할지 생각해보자.

이 흐름은 외부로 공개되지 않으며, 개략적인 수준에서 책임에 필요한 작업을 생각해보고, 혼자서 못하는 작업인지 분별할 정도로 생각한다.
혼자서 처리할 수 없는 작업은 또다시 외부로 보내는 메시지가 되고, 이 메시지에 맞는 정보 전문가를 찾는 과정을 반복한다.

영화 시스템의 예시에서는 상영 객체가 예매하라는 메시지를 처리할 때, 예매 가격을 처리하는 과정이 필요하다.
그러나 상영 객체는 각 영화의 가격이나, 할인 정책을 알지 못한다. 그래서 외부의 도움이 필요하다.

이제 가격을 계산하라는 새로운 메시지가 생기고, 이 메시지를 책임질 새로운 객체를 선정하면 된다.

낮은 결합도를 추구해라(low coupling pattern)

설계의 전체적인 결합도가 낮도록 책임을 할당하라.

위에서 만든 도메인 모델을 보면 연결되있는 개념들이 있다.
영화 예매 시스템에서, 영화 객체와 영화 할인 기준 개념은 서로 연관있다.

반면 상영과 영화 할인 기준 개념은 연결되어 있지않다.
만약 이 둘을 연결하려면, 새로운 결합도가 생기는 거고 추천하지 않는다.

차라리 이미 연관된 개념인 영화 객체와 영화 할인 기준 개념을 서로 연결하는게 맞다.

높은 응집도를 추구해라(high cohesion pattern)

영화 예매 시스템에서 상영이 영화 할인과 결합되면 응집도가 낮아지는 문제가 생긴다.

상영은 이제 영화 할인 계산을 직접할 수 있지만,
근데 만약에 영화 할인 관련 객체를 교환하는 경우를 생각해보자.
그러면 상영 객체도 같이 변경되어야 하는 거다.(서로 다른 이유로 변경되는 책임을 지게된다.)

상영 정보가 바뀌면 할인 객체도 변경되어야 하고,
할인 객체가 바뀌면 상영 객체도 변경되어야 한다.

이 둘은 그런 수고를 감수할 정도로 같이 결합해야할 객체인지 의심해보아야 한다.
한 클래스는 한 가지의 변경사항만 책임지도록 하자.
변경되는 이유가 여러가지면 이 클래스가 변경되는 시점도 제각각이고, 연관성도 떨어진다.

차라리 영화 객체와 할인 객체가 연결되는게 맞고, 상영은 영화 객체와만 결합하는게 낫다.
그러면 상영은 할인 관련 책임은 신경을 전혀 쓰지 않아도 된다.

변경의 이유가 지나치게 많은 클래스의 징후

  1. 인스턴스 변수가 초기화되는 시점을 살펴보자
    1. 인스턴스 변수가 초기화 될 때 일부만 초기화되면 응집도가 낮다고 봐야한다.
    2. 그렇지 않다면 함께 초기화 되는 기준으로 코드를 분리하라.
  2. 메서드들이 인스턴스 변수를 사용하는 방식을 살펴보자
    1. 모든 메서드가 모든 변수를 사용하면 응집도가 높은 것.
    2. 일부 메서드들은 특정 변수만 사용하는 경우, 코드를 분리하라.

창조자에게 객체 생성 책임을 할당하라(creator pattern)

객체를 생성할 책임을 어떤 객체에게 할당할 것인가?

Creator pattern은 다음 조건을 가장 만족시키는 객체가 생성해야 한다고 주장한다.
객체 A를 생성해야 한다면…

  1. A 객체를 포함하거나 참조하는 객체
  2. A 객체를 기록하는 객체
  3. A 객체를 긴밀하게 사용하는 객체
  4. A 객체를 초기화 하는데 필요한 데이터를 가진 객체

즉 조건들을 보면, A객체와 결합되있는 객체를 찾고 있는 걸 알 수 있다.
즉 이미 결합된 관계에서 객체를 만드는게 결합도를 낮추는데 도움이 된다.

인터페이스로 변화에 대응하라(polymorphism, protected variations)

코드를 작성하다보면, 특정 객체 타입에 따라 다르게 반응해야 하는 경우가 있다.

예를 들면 할인 기준에 따라 할인 적용을 다르게 해야 하면 if, else if로 객체의 타입을 검사해서 할인을 적용해줘야 한다.
하지만, 이때 기준이 추가되면, 매번 else if를 추가해줘야 된다.

그래서 인터페이스를 도입해서 원하는 기준으로 초기화해주면, 검사하지 않고 할인 적용 메시지를 보내주면 된다.

인터페이스를 도입해서 캡슐화하면, 구현 클래스들의 세세한 변경을 외부에서는 신경 안써도 된다.

Share