본문 바로가기

IT 도서 정리/오브젝트

[오브젝트] CHAPTER 5. 책임 할당하기

4장에서 데이터 주도 설계의 문제점을 알아봤으니, 이제 책임 주도 설계를 해보자.

 

 

책임에 초점을 맞춰서 설계할 때 어떤 것이 가장 어려울까?

=> 어떤 객체에게 어떤 책임을 할당할지를 결정하는 것이 가장 큰 어려움이라고 한다.

 

 

GRASP 패턴은 책임 할당의 어려움을 해결하기 위한 답을 제시해준다.

GRASP 패턴: General Responsibility Assignment Software Pattern의 약자, 객체에 책임을 할당할 때 지침으로 삼을 수 있는 원칙들의 집합

 

책임 할당을 위한 GRASP 패턴

1. 도메인(DOMAIN) 개념에서 출발하라.

설계를 시작히기 전 참고할 수 있는 개념들의 모음이 있는 것만으로도 큰 도움이 된다.

 

하지만, 도메인 개념을 정리하는 데 너무 많은 시간을 들이지 말고 빠르게 설계와 구현을 진행하라.

2. 정보 전문가(INFORMATION EXPERT)에게 책임을 할당하라.

정보 전문가(INFORMATION EXPERT) 패턴: 책임을 수행하는 데 필요한 정보를 가지고 있는 객체엑 책임을 할당하라.

 

하지만, 정보 전문가가 데이터를 반드시 저장하고 있을 필요는 없다.

3. 낮은 결합도(LOW COUPLING) 패턴 + 높은 응집도(HIGH COHESION) 패턴.

낮은 결합도 패턴: 설계의 전체적인 결합도가 낮게 유지되도록 책임을 할당한다.

높은 응집도 패턴: 높은 응집성을 유지할 수 있게 책임을 할당하라.

4. 창조자(CREATOR)에게 객체 생성 책임을 할당하라.

창조자(CREATOR) 패턴: 객체 A를 생성해야 할 때 어떤 객체에게 객체 생성 책임을 할당해야 할지를 결정해주는 패턴으로 아래 조건을 최대한 많이 만족하는 B에게 객체 생성 책임을 할당하라.

 

1. B가 A 객체를 포함하거나 참조한다.
2. B가 A 객체를 기록한다.
3. B가 A 객체를 긴밀하게 사용한다.
4. B가 A 객체를 초기화하는 데 필요한 데이터를 가지고 있다.(정보 전문가)

 

 

구현을 통한 검증

코드는 적지 않겠지만 핵심 부분만 간추려서 설명해볼까 한다.

1. 캡슐화

프로그램에서 처음 시작은 Screening이라는 클래스이며 그 다음은 Movie 클래스이다.

 

이때, Screening이 Movie에 전송하는 메시지의 시그니처는

calculateMovieFee(Screening screening)으로,

 

Movie의 내부 구현에 대한 어떤 지식도 없이 전송할 메시지를 결정한다.

이를 통해 캡슐화가 충실히 구현된다.

 

2. 응집도

클래스 응집도를 판단할 수 있는 세 가지 방법

1. 클래스가 하나 이상의 이유로 변경돼야 한다면? => 낮은 응집도
2. 클래스의 인스턴스를 초기화하는 시점에 경우에 따라 서로 다른 속성들을 초기화하고 있다면? => 낮은 응집도
3. 메서드 그룹이 속성 그룹을 사용하는지 여부로 나뉜다면? => 낮은 응집도

 

1번. 하나 이상의 변경 이유

해결 방법: 변경의 이유에 따라 클래스를 분리하라.

 

2번. 인스턴스 변수가 초기화되는 시점이 다를 때

해결 방법: 함께 초기화 되는 속성을 기준으로 코드를 분리하라.

 

3. 메서드들이 인스턴스 변수를 사용하는 방식이 다를 때

해결 방법: 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리하라.

 

3. 다형성을 통한 타입의 분리

분리하는 과정에서 역할의 개념이 등장할 수 있는데, 이 경우 인터페이스를 통해 구현하면 된다.

 

다형성(POLYMORPHISM) 패턴: 객체의 타입에 따라 변하는 행동이 있다면 타입을 분리하고 변화하는 행동을 각 타입의 책임으로 할당하라.


=> if ~ else or switch ~ case 대신 다형성을 활용하라!

 

변경 보호(PROTECTED VARIATIONS) 패턴: 변경을 캡슐화하도록 책임을 할당하라.

 

 

클래스를 변경에 따라 분리하고 인터페이스를 이용해 변경을 캡슐화하는 것은 결합도와 응집도를 향상시키는 훌륭한 방법이다.

하나의 클래스가 여러 타입의 행동을 구현한다면?
=> 클래스를 분리하고, 다형성 패턴에 따라 책임을 분산시켜라.
예측 가능한 변경으로 인해 여러 클래스들이 불안정하다면?
=> 변경 보호 패턴에 따라 안정적인 인터페이스 뒤로 변경을 캡슐화하라.

 

4. 변경과 유연성

개발자로서 변경에 대비할 수 있는 방법은 두 가지이다.

1. 코드를 이해하고 수정하기 쉽도록 최대한 단순하게 설계
2. 코드를 수정하지 않고도 변경을 수용할 수 있도록 코드를 더 유연하게 만드는 것.

대부분 1번의 방법이 좋으나,

유사한 변경의 반복이 계속된다면 2번 방법을 사용하라.

 

그 예시로 상속을 합성으로 변경하는 게 있다.

 

책임 주도 설계의 대안

처음부터 책임 주도 설계를 하는 것은 어려운 일이다.

 

그러므로, 처음엔 기능을 수행하는 코드를 최대한 빠르게 구현한 뒤 리팩터링을 시도하라!

리팩터링 - 이해하기 쉽고 수정하기 쉬운 소프트웨어로 개선하기 위해 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것

리팩터링의 구현 과정

1. 몬스터 메서드를 작은 메서드로 변경하라!

몬스터 메서드
긴 메서드로, 응집도가 낮아 이해도 어렵고, 재사용도 어렵고, 변경도 어려운 메서드.
작은 메서드
1. 주석들의 나열처럼 보여 이해하기가 쉽다.
2.메서드가 어떤 일을 하는 지를 한눈에 알아볼 수 있다.
3. 변경도 쉽다.

 

 

2. 메서드를 다른 클래스로 이동하라

이때 이동은 인자에 정의된 클래스 중 하나로 이동하는 것이 일반적이다.