juuuuuuun 2024. 4. 22. 16:20

GRASP(General Responsibility Assignment Software Patterns)

 - 일반적인 책임 할당 소프트웨어 패턴. 기본 객체 설계 학습을 위한 방법론

 - 객체 지향 설계의 관점에서 책임은 클래스의 계약 혹은 의무로, 클래스와 객체가 어떤 메세지에 대해 처리해야 할 의무가 있을 때 해당 객체가 이러한 책임을 가진다라고 이야기 할 수 있다.

 

 - Object-Oriented 설계의 핵심은 각 객체에 책임을 부여하는 것

 - 책임을 부여하는 원칙들을 말하고 있는 패턴

 - OOP에서 책임은 기본적으로 메서드를 통해서 구현된다.

 

책임의 예

 - String 클래스는 문자열을 표현하고 처리하는 책임을 가진다.

 - File 클래스는 파일에 대한 정보를 알려주고 처리하는 책임을 가진다.

 - DTO 클래스는 데이터를 다른 Layer로 운반하고 특별한 경우 비즈니스 로직에 따라 데이터 값을 반영하는 책임을 가진다.

 

책임을 이해한다는 것은 좋은 객체지향 설계의 핵심이다.

 

1. Information Expert

 

가장 핵심적인 클래스에 책임을 부여하는 패턴

 

문제 : 객체에 책임을 할당하기 위한 일반적인 원리는 무엇일까?

 

해법 : 책임을 수행하기 위해 필요한 정보를 가장 많이 가지고 있는 객체에게 책임을 할당

 

역할을 수행할 수 있는 정보, 계산된 필드 등의 책임을 위임할 위치를 결정하는 데 사용되는 패턴이다.

그리고 그것을 이행하는데 필요한 가장 많은 정보를 가지고 있는 클래스에 책임을 부여한다.

 

클래스는 데이터와 처리로직이 함께 묶여 있고, 자신의 데이터를 감추려하면 오직 자신의 처리 로직에서만 데이터를 처리하고 외부에는 기능만 제공한다.

 

2. Creater

새로운 객체를 생성하는 책임을 부여하는 패턴

 

상황 : 어떤 클래스의 새로운 인스턴스를 생성하는 책임은?

 

해법 : 다음과 같은 상황인 경우 클래스 B는 A 클래스의 인스턴스를 생성하는 책임이 있다.

 - B가 A를 포함할 때

 - B가 A 인스턴스를 기록할 때

 - B가 A를 많이 사용할 때

 - B가 A 객체를 생성하는데 필요한 정보를 가지고 있다

 

3. High cohesion

 

객체를 적절하게 집중시키며 관리할 수 있고 이해가능하게 만드는 패턴

 

상황 : 너무 복잡하기 않게 관리를 하려면 어떻게 해야될까?

 

해법 : 응집도가 높도록 책임을 할당하라

 

높은 응집력은 일반적으로 낮은 결합을 지원하는 데 사용된다.

응집력이 높다는 것은 주어진 요소의 책임이 밀접하게 관련되어 있고 집중되어 있음을 의미한다.

 

반면 응집력이 낮은 요소들은 이해하기 어렵고 유지보수도 힘들고 재사용을 어렵게 하는 원인이 된다.

 

프로그램 모듈을 역할과 책임 기반으로 패키지와 서브 패키지로 나누는 것은 응집력을 높이기 위한 것이다.

 

4. Low coupling

 

클래스 간의 종속성을 낮춰 변화에 효율적으로 대응하기 위한 패턴.

 

상황 : 클래스 간의 낮은 의존도, 변경에 유연한, 재사용성을 높이려면 어떻게 할까?

 

해법 : 결합도를 낮게 책임을 할당하라

 

커플링 예시

 - X가 Y 인스턴스를 참조하는 변수를 갖고 있을 때

 - X가 Y 객체의 서비스를 요청할 때

 - X가 Y의 직접, 간접적 서브 클래스일 때

 - X가 Y 인터페이스를 구현했을 때

 

커플링이란

 - 한 요소가 다른 요소에 얼마나 강력하게 연결되어 있거나 다른 요소에 대해 알고 있거나 의존하는지에 대한 것을 나타내는 척도

 - 객체지향 시스템은 각 객체들 간의 상호작용을 전제로 하기 때문에 커플링이 없을 수는 없다.

 

낮은 결합도는 다음을 의미한다.

 - 클래스 간의 종속성이 낮다

 - 높은 재사용 가능성

 - 한 클래스의 변화가 다른 클래스의 변화에 미치는 영향이 낮다.

 

5. Controller

 

시스템 이벤트를 처리하는 책임을 전체 시스템 또는 유스케이스 시나리오를 나타내는 non-UI 클래스에 할당하는 패턴

 

상황 : UI계층에서 사용자의 입력을 받아 처리하는 것은 누구의 책임인가?

 

해법 : 컨트롤러를 만들어서, 컨트롤러가 사용자의 요청을 받아 로직을 분기해서 처리한다.

 

일반적으로 컨트롤러 개체는 시스템 이벤트를 받거나 처리하는 non-UI 개체이다. 컨트롤러는 모든 시스템 이벤트를 처리하는 데 사용되어야 하며 둘 이상의 use case에서도 사용 가능하다.

 

예를 들어 사용사 생성 삭제는 두 개의 개별 컨트롤러가 아닌 UserController라는 단일 클래스로 구성가능

 

6. Polymorphism

 

상황 : 대체 가능한 소프트웨어 구성 요소들을 어떻게 생산하는가?

 

해법 : 다형성을 이용해서 코딩하라.

 

객체의 Type으로 분기를 처리하는 영역에서는 명시적인 분기 대신 다형성의 연산을 사용하는 것이 좋다.

객체지향의 특징인 상속과 다형성을 적절히 활용해야 한다.

 

7. Pure fabrication

 

상황 : Information Expert에 근거하여 정보가 많은 객체에 책임을 부여했는데 높은 응집성과 낮은 결합도에 위반될 때 어떻게 해야 할까?

 

해법 : 인위적으로 클래스를 만들어서, 문제가 되는 책임만 모아서 높은 응집도를 갖는 클래스를 만든다.

 

예)

 - DB 연동 시 많이 사용하는 DAO(Data Access Object) 객체를 위의 예로 볼 수 있다.

   예를 들어 회원 정보를 DB에 저장할 때 Information Exper에 따르면 Member 객체가 DB에 직접 접근하는 것이 맞지만 DB 접근에 대한 책임이 여러 곳에 분산될 수 있기 때문에 DAO 클래스에 DB 접근 책임을 할당한다.

 - MVC 웹이나 Rest Controller, DDD에서 Service Layer를 사용하는 것도 Pure Fabrication에 해당한다.

 

8. Indirection

 

상황 : 두 개의 객체 간의 직접적인 커플링을 피하기 위한 책임을 어디에 할당하는가? Low Coupling, High Cohesion이 만족하도록 커플링을 피하려면?

 

해법 : 두 객체 사이에 중간 객체를 만든다.

 

두 객체 사이의 중간 요소에 책임을 할당하여 두 요소 간의 낮은 결합 및 재사용 가능성을 지원하도록 한다.

 

예)

 - MVC 패턴에서 모델과 뷰 사이의 중개를 위한 컨트롤러를 도입해서 낮은 결합상태를 유지하도록 할 경우 Controller도 Indirection 패턴에 해당한다.

 

9. Protected variations

 

상황 : 한 요소에서의 변화가 확장이 다른 요소에 영향을 미치지 않게 하려면 어떻게 해야 하는가?

 

해법 : 요소에서의 변화가 영향을 미치는 곳을 인터페이스를 이용하여 감싼다.

 

Protected variations 패턴은 변경될 여지가 있는 곳에 안정된 인터페이스를 정의하여 사용하는 것.

인터페이스로 감싸고 다형성을 이용하여 다양한 구현을 생성함으로써 다른 요소(객체, 시스템, 서브시스템)의 변화로부터 요소를 보호하도록 한다.