GRASP : General Responsibility Assignment Software Patterns?

해당 내용은 상호작용하는 클래스 혹은 객체에 책임을 할당하는데 도움이 되는 개념과 방법 즉 패턴들로 이루어져 있습니다. 이러한 내용들을 준수함으로써 좋은 객체지향 디자인 패턴을 만들어낼 수 있습니다.

 

 

 

Responsibility?

책임은 SOLID나 지금 다룰 GRASP 등 Oriented-Object-Design 패턴과 원칙에서 핵심이 되는 개념입니다. 이는 클래스와 객체가 어떤 메시지(요청)에 대해 처리할 수 있거나, 적절한 행동을 해야 하는 의무가 있는 경우 해당 객체가 이러한 책임을 가진다라고 이야기할 수 있습니다.

 

책임은 메서드를 통해 구현되게 됩니다.

 

 

Example.

  • String은 문자열을 표현하고 처리하는 책임을 가집니다.
  • File은 파일에 대한 정보를 알려주고 처리하는 책임을 가집니다.
  • DTO는 데이터를 다른 Layer로 운반하고 특별한 경우 비즈니스 로직에 따라 데이터 값을 반영시키는 책임을 가질 수 있습니다.

이런 것이 모두 책임 입니다.

 

책임의 분류는 행위 관점과 상태 관점으로 나눌 수 있습니다.

 

행위 관점.

  • 객체를 생성하는 행위, 결과를 계산하는 행위 - 객체 스스로 하는 것.
  • 다른 객체의 행동을 지시하는 것 - Controller
  • 다른 객체의 활동을 제어하고 조절하는 것 - Chain of Responsibility, Proxy

상태 관점.

  • private로 접근 제어된 데이터에 관하여 아는 것
  • 관련된 객체에 대해 아는 것
  • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

 

 

 

Creator

새로운 객체를 생성하는 책임을 말합니다.

 

이러한 책임을 가지기 위해서는 하나 이상의 조건을 만족하여야 합니다.

  • 생성하는 객체를 포함합니다.
  • 생성하는 객체에 대한 초기화 정보를 가집니다.
  • 생성하는 객체를 사용합니다.
  • 생성하는 객체에 대한 정보를 기록합니다.

이와 관련된 것은 Creation Pattern 중 Abstract Factory, Factory Method가 있습니다.

 

 

 

Information Expert

새로운 기능이나 방법을 추가하는 것은 그에 필요한 정보를 가지는 클래스, 객체를 대상으로 하여야 합니다. 그러한 클래스, 객체는 최소한의 변경으로 기능을 구현할 수 있기 때문입니다.

 

결국 객체가 가지는 데이터에 대한 기능을 직접 처리하게끔 하라는 것입니다.

 

하지만 모든 필드에 대한 무분별한 Getter, Setter는 캡슐화를 지키지 못합니다.

해당 행위를 주관하는 객체(Client, Service)에서 데이터 흐름을 만들어내는 객체(DTO)의 데이터를 꺼내오는 것보다는 다음 행위를 하는 객체에게 해당 객체를 전달하는 것이 캡슐화를 지키는 방법입니다.

 

그리고 상태에 대한 질의 후 분기해야 하는 로직의 경우 Tell Don't Ask 원칙을 준수하는 것이 좋습니다.

Tell Don't Ask : https://martinfowler.com/bliki/TellDontAsk.html

 

 

 

Controller

요청에 대한 비즈니스 로직과 요청을 전달, 지시하는 로직은 분리되어야 합니다. 그리고 우리는 일반적으로 MVC Pattern을 통해 이러한 요구사항을 만족합니다.

 

Controller는 요청을 받고 적절한 행위를 하는 객체에게 지시하는 행위 관점의 패턴입니다.

 

이러한 책임을 가지는 객체는 내부적으로 별도의 비즈니스 로직을 가져서는 안 되며, 요청을 전달, 위임하는 것에 중점을 두어야 합니다.

 

 

 

Low Coupling

낮은 결합도를 준수하는 것은 시스템 설계의 전체적인 결합도를 낮게 유지되도록 구현하고 책임을 할당하라는 의미입니다. 객체 간의 결합이 없을 수는 없기에 우리는 최대한 영향을 주지 않도록 노력하여야 합니다.

 

이를 위해서는

  • 클래스 간의 종속성을 낮추어야 합니다.
  • 한 클래스의 변경이 다른 클래스에 주는 영향이 적어야 합니다.
  • 객체는 더 높은 재사용 가능성을 가져야 합니다.

이는 Interface 등을 이용한 상호 작용이나 Facade 패턴 등을 사용하여 이루어낼 수 있습니다.

 

반대로 높은 결합도는 각각의 객체가 서로의 구현 세부사항을 인식할 때(접근 가능할 때, 사용할 때) 발생되게 되는 것입니다. 이런 경우 한 객체의 변경이 다른 객체의 변경을 야기하게 됩니다. extend의 경우 이런 변경에 대한 파급효과가 강해지게 됩니다.

 

 

 

High Cohesion

높은 응집력을 준수하라는 것은 객체 책임에 의해 변경될 수 있는 요소들을 한 곳에 모아서 관리하라는 것입니다.

이것을 통해 요구사항으로 인해 책임의 변경이 이루어진다면, 그러한 영향이 하나의 객체에 대해서만 이루어질 수 있습니다.

 

이것과 관련된 원칙은 SOLID의 Single responsibility principle가 있습니다. 이 원칙은 객체를 변경할 수 있는 책임이 하나만 존재해야 한다는 원칙입니다.

 

 

 

Indirection

간접이란 두 객체 간의 직접적인 결합을 분리하고 간접적인 결합을 지원하게끔 설계하라는 것입니다. 이는 별도의 중개자 객체를 통해 Dependency Injection을 진행하거나, 요청에 대한 위임, 지시하는 컨트롤러와 같은 요소를 도입하는 것을 말합니다.

 

Observer, Facade, Bridge, Mediator, Adapter는 간접 접근 방식을 활용하는 패턴입니다.

 

 

 

Polymorphism

하나의 타입을 가지는 서로 다른 객체들이 동일한 책임, 메시지에 대해서 서로 다르게 반응하는 것을 말합니다. 즉 책임을 공유하는 상태입니다.

 

이러한 원칙은 객체들 사이의 대체 가능성을 의미하며 그를 통해 설계를 유연하고 재사용 가능하게 만들 수 있습니다.

 

각각의 요청에 대하여 Reflection API 등의 동적 로딩을 통하여 Runtime Dependency를 구현하거나 Chain of Responsibility와 같은 행위 패턴을 구현하고 분기를 위한 상태 변수를 전달함으로써 컴파일 시점에서 필요한 객체를 Dependency Injection 할 수 있습니다.

 

 

 

Protected Variations

요구사항에 대한 책임, 동작 변화는 객체의 내부에서만 이루어져야 하며, 공용 인터페이스를 동일하게 유지함으로써 Client 역할을 하는 객체에게는 변경 사실을 숨겨야 합니다.

 

변경될 여지가 있는 로직에 대해서는 객체 간의 캡슐화를 지켜야 한다는 것입니다.

 

이와 관련된 원칙으로는 SOLID의 Open/closed principle이 있습니다. 이 원칙은 객체의 책임의 확장과 변경이 다른 객체의 행위에 영향을 주지 않아야 된다는 것을 말합니다.

 

 

 

Pure Fabrication

객체가 광범위한 책임을 가지지 않도록 (God Class) 분리하라는 것을 말합니다. 이는 기능에 대한 공통적인, 비즈니스 로직을 하나의 객체를 만들어 담당하게끔 하는 것을 뜻합니다.

 

Layer Architecture에서 이러한 책임을 담당하는 객체를 Service라고 합니다.

 

 

 

참고 자료

'Programming' 카테고리의 다른 글

Live Study_Week 14. Generic  (0) 2021.02.28
JPA 학습 정리 Persistence Context, Entity, Flush?  (0) 2021.02.27
Java의 Reflection API와 성능 이슈?  (0) 2021.02.02
G1 GC  (0) 2021.01.26
Concurrency, Parallelism, Parallel and Concurrency  (0) 2021.01.23

+ Recent posts