Ch.5 책임 할당하기

Previously...

데이터 중심의 접근법의 문제점을 알아보았다.

  • 행동보다 설계를 먼저 결정하고, 협력이라는 문맥을 벗어나 고립된 객체의 상태에 초점을 맞춘다.

  • 그래서:

    • 캡슐화를 위반하기 쉽고,

    • 요소들 사이의 결합도가 높아지고,

    • 코드를 변경하기 어려워진다.

즉, 변경하기 어려운 설계가 되기 쉽다.

About

이를 해결하기 위한 가장 기본적인 방법은 데이터가 아닌 책임에 초점을 맞추는 것이다.

그런데 "어떤 객체에게 어떤 책임을 할당할지"가 관건이고 가장 어려운 문제이다.

이번 장에서는 그런 어려움을 해결해 줄 GRASP 패턴이 나온다.

책임 주도 설계

데이터 중심의 설계에서 책임 중심의 설계로 전환하기 위해 따라야 하는 원칙

  • 데이터보다 행동을 먼저 결정하라.

    • 객체에게 중요한 것은 데이터가 아닌 외부에 제공하는 행동이다.

    • "이 객체가 포함해야 하는 데이터가 무엇인가"가 아니라 "이 객체가 수행해야 하는 책임은 무엇인가"를 결정한 후에 필요한 데이터를 결정하자.

    • 즉, 객체의 행동, 즉 책임을 먼저 결정한 후에 객체의 상태를 결정하자.

  • 협력이라는 문맥 안에서 책임을 결정하라.

    • 책임은 객체의 입장이 아니라 객체가 참여하는 협력에 적합해야 한다.

    • 협력을 시작하는 주체는 메시지 전송자이므로, 메시지를 전송하는 클라이언트의 의도에 적합한 책임을 할당해야 한다.

      • ...그러니까 객체/클래스가 어떤 게 있을지 먼저 생각하지 말고 어떤 작업(행동)이 있을지 생각하는 게 먼저라는 뜻?

    • 메시지를 결정하고 객체를 선택하자!

두 원칙의 핵심은 데이터가 아닌 책임과 협력에 초점을 맞추라는 것.

단순히 메시지를 먼저 결정하는 것만으로 메시지 송신자는 메시지 수신자에 대한 어떠한 가정도 할 수 없으므로 메시지 전송자 관점에서 메시지 수신자가 캡슐화할 수 된다!

GRASP 패턴

  • grasp: 꽉 잡다, 이해하다

  • GRASP - General Responsibility Assignment Software Pattern (일반적인 책임 할당을 위한 소프트웨어 패턴)

    • 객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙들의 집합을 패턴 형식으로 정리.

도메인 개념에서 출발하기

먼저 도메인에 대한 개략적인 모습을 그려보자. (완벽할 필요는 없다.)

INFORMATION EXPERT 패턴

정보를 가장 잘 알고 있는 전문가에게 책임을 할당해라

가장 먼저 "애플리케이션이 제공해야 하는 기능""애플리케이션의 책임"으로 생각하는 것으로 시작한다.

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

  • 메시지를 전송할 객체는 무엇을 원하는가?

  • 메시지를 수신할 적합한 객체는 무엇인가?

이 질문들에 답하고 객체에게 책임을 할당하는 것은 책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것이다. GRASP에서는 이를 INFORMATION EXPERT(정보 전문가) 패턴이라고 부른다.

그 다음으론 작업의 흐름을 생각해보며 스스로 책임일 수 없는 작업이 있다면 외부에 요청하는 메시지를 생각해보면 이 메시지가 바로 새로운 객체의 책임으로 할당된다.

INFORMATION EXPERT 패턴을 따르는 것만으로도 자율성이 높은 객체들로 구성된 협력 공동체를 구축할 가능성이 높아진다.

LOW COUPLING 패턴 / HIGH COHESION 패턴

낮은 결합도와 높은 응집도를 갖는 설계를 선택해라

여러 설계에 대한 선택지, 또는 책임을 할당할 수 있는 다양한 대안이 있다면 낮은 결합도와 높은 응집도를 갖는 설계를 선택해라. 매 순간마다!

이렇게 책임을 할당하고 코드를 작성하는 매 순간마다 전체적인 설계 품질을 검토하면 단순하고 재사용 가능하고 유연한 설계를 얻을 수 있을 것이다.

CREATOR 패턴

객체 A를 생성할 책임을 가질 객체는 다음과 같은 기준을 최대한 많이 만족하는 B에게 책임을 할당해라.

  • B가 A를 포함하거나 참조한다.

  • B가 A를 기록한다.

  • B가 A를 긴밀하게 사용한다.

  • B가 A를 초기화하는 데 필요한 데이터를 갖고 있다. (이 경우 B는 A에 대한 정보 전문가다.)

어쨌든 생성되는 객체와 연결되거나 관련될 필요가 있는 객체에 해당 객체를 생성할 책임을 맡기자.

설계 개선하기

  • 변경의 이유에 따라 클래스를 분리하자.(변경의 이유에 따라 클래스를 묶자.)

    • 변경의 이유가 하나 이상이라면 분리해야 응집도를 높일 수 있다.

  • 먼저 인스턴스 변수가 초기화되는 시점을 보

    • 응집도가 높은 클래스는 인스턴스 생성 시 모든 속성을 함께 초기화한다.

  • 메서드들이 인스턴스 변수를 사용하는 방식을 보자.

    • 모든 메서드가 객체의 모든 속성을 사용한다면 클래스의 응집도가 높은 것이다.

    • 즉, 각 메서드가 어떤 필드를 사용하는지를 보고 묶자.

POLYMORPHISM PATTERN

GitBook is obviously going nuts...^^

Java offers interfaces and abstract classes for implementing "responsibilities".

Well, if classes for a responsibility need to have a shared implementation, you can go for abstract classes.

If no implementations are to be shared but their responsibility is to be defined, you can go for interfaces.

Why the term polymorphism? If there's an action that changes according to the type of an object, separate into multiple types and assign responsibility for different actions to each type.

PROTECTED VARIATIONS PATTERN

Assign responsiblities so that changes can be encapsulated. It's called PROTECTED VARIATIONS PATTERN, in the world of GRASP.

If something is bound to change? Encapsulate it.

Combine polymorphism and protected variations patterns and use them at the right time at the right moment. Then you can achieve an architecture that can handle changes and extensions.

Changes and Flexibility

  1. KISS - Keep it simple, stupid!

  2. Use GRASP to create an architecture that is robust to changes.

Refactoring

  • One way of creating a breakthrough to the writer's block is... you could code first and refactor them later.

    • Refactoring DOES NOT change how the code looks from outside.

  • Split methods into small parts for higher cohesion, instead of wrting a comment.

Make objects "autonomous"

  • Make objects handle their own data.

Conclusion

Well, I was tryna be easy today, but GitBook isn't just letting me :)

Last updated