책임 할당의 기준
정보 전문가 패턴
- 메시지를 전송할 객체(클라이언트 코드)는 무엇을 원하는가?
- 메시지를 수신할 적합한 객체는 누구인가?
우리는 객체가 상태와 행동을 통합한 캡슐화의 단위라는 사실에 주목해야 한다.
객체는 자율적인 존재여야 한다.
따라서 객체에게 책임을 할당하는 첫번째 원칙은 책인을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것이다.
이를 통해 높은 응집도가 가능해진다.
여기서 이야기하는 정보는 데이터와 다르다.
책임을 수행하는 객체가 정보를 알고 있다고 해서 그 정보를 저장하고 있을 필요는 없다.
객체는 해당 정보를 제공할 수 있는 다른 객체를 알고 있거나, 필요한 정보를 계산해서 제공할 수도 있다.
여기서 정보는 도메인 모델상 떠올리는 개념의 추상적인 속성에 가깝다.
설계중에 너무 세세한 부분까지 고민할 필요는 없다.
객체가 책임을 수행하는 데 필요한 작업을 구상해보고 스스로 처리할 수 없는 작업이 무엇인지를 가릴 정도면 된다.
만약 스스로 처리할 수 없는 작업이 있다면 외부에 도움을 요청(메시지)해야 한다.
이같은 연쇄적인 메시지 전송과 수신을 통해 협력 공동체가 구성되는 것이다.
낮은 결합도 패턴
어떻게 하면 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시킬 수 있을까?
설계의 전체적인 결합도가 낮게 유지되도록 책임을 할당하라.
위 UML에서, Screening - Discount Condition, Screening - Movie 개별 관계만 봤을 때엔 결합도에 크게 문제가 있어보이진 않는다.
오히려 Screening이 예매 역할을 맡았기 때문에 영화보다는 할인 조건과 더 친하게 느껴지기도 한다.
그러나 Movie와 Discount Condition은 이미 결합되어 있다.
설계 전체의 결합도를 생각했을 때, Movie는 가격 계산 책임을 맡고 있고 결국 할인 여부를 알아야 하기 때문에, Movie - Discount Condition 의존성만 맺어준다면 설계 전체적으로 결합도를 줄이면서도 협력을 완성할 수 있다.
높은 응집도 패턴
어떻게 복잡성을 관리할 수 있는 수준으로 유지할 것인가?
높은 응집도를 유지할 수 있게 책임을 할당하라.
Screening의 가장 중요한 책임은 예매를 생성하는 것이다.
만약 Discount Condition과 협력해야 한다면, Screening은 영화 요금 계산과 관련된 책임 일부를 떠안아야 한다.
즉, 예매 요금 계산 방식이 변경될 경우 Screening도 함께 변경되어야 한다.
서로 다른 이유의 변경을 짊어지게 되므로 응집도가 낮아질 수밖에 없다. SRP를 위반한다.
반면 Movie의 주된 책임은 영화 요금을 계산하는것이므로 Discount Condition과 협력하는 것은 응집도에 아무런 해도 가하지 않는다.
창조자 패턴
객체 생성 책임을 어떻게 할당해야 할까?
아래 조건을 가장 많이 만족하는 객체(A)에게 객체(B) 생성 책임을 할당하라.
- A는 B 객체를 포함하거나 참조한다.
- A는 B 객체를 기록한다.
- A는 B 객체를 긴밀하게 사용한다.
- A는 B 객체를 초기화하는 데 필요한 데이터를 갖고 있다. (A는 B 객체에 대한 [[정보 전문가 패턴]])
이미 결합된 객체에게 생성 책임을 할당하는 것은 설계의 전체적인 결합도에 영향을 미치지 않는다.
결과적으로 창조자 패턴은 이미 존재하는 객체 사이의 관계를 이용하기 때문에 전체적인 설계가 낮은 결합도를 유지할 수 있게 해준다.
미래에 일어날 변경을 상상하자
클래스 분리의 기준
응집도가 낮다는 것은 서로 연관성 없는 기능이나 데이터가 하나의 클래스 안에 뭉쳐져있다는 것을 의미한다. 이 문제를 해결하기 위해서는 변경의 이유에 따라 클래스를 분리해야 한다.
일반적으로 클래스 분리는 변경의 이유가 하나 이상인 클래스를 찾는 것으로부터 시작하는 것이 좋다.
이를 쉽게 찾도록 돕는 위험 징후들이 존재한다.
첫번째 방법: 인스턴스 변수가 초기화되는 시점을 살펴보는 것.
응집도가 높은 클래스는 인스턴스를 생성할 때 모든 속성을 함께 초기화한다.
두번째 방법: 메서드들이 인스턴스 변수를 사용하는 방식을 살펴보는 것.
모든 메서드가 객체의 모든 속성을 사용한다면 클래스의 응집도는 높다고 볼 수 있다.
클래스의 응집도를 높이기 위해선 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리해야 한다.
다형성 패턴
위 UML은 할인 조건에 해당하는 두 타입(Period, Sequence)의 객체에 대해 Movie가 의존하는 형태다.
두 객체는 같은 책임을 가졌지만, 가진 속성과 내부 구현이 다른 상태이다.
여기서 첫번째 문제점은, Movie 클래스가 양쪽 모두에게 결합된다는 것이다. DiscountCondition 객체 하나에게 결합되었을 때보다 Movie의 응집도가 낮아진다.
두번째 문제점은 새로운 할인 조건을 추가하기가 어려워졌다는 것이다.
이 문제를 다형성으로 해결할 수 있다. 역할을 생각해보자.
두 조건이 동일한 책임을 수행한다는 것은 하나의 역할에 대한 대체 가능한 클래스임을 의미한다.
-> DiscountCondition 인터페이스의 구현체로써 만든다.
=> Movie는 구체적인 클래스는 알지 못한 채로 오직 역할에 대해서만 결합된다. 그럼으로써 할인 조건에 대한 요구사항이 변경되더라도 Movie에는 변경의 영향이 미치지 않는다.
GRASP에서는 이를 다형성 패턴이라고 부른다.
객체의 암시적인 타입에 따라 행동을 분기해야 한다면 그 타입을 명시적인 클래스로써 정의하고 행동을 나눔으로써 응집도 문제를 해결하자.
타입에 따라 변하는 행동이 있다면, 타입을 분리하고 행동을 각 타입의 책임으로 할당하라는 것이다.
프로그램을 조건 논리(if else, switch case)를 사용해 설계한다면 새로운 변경이 일어날 때 조건 논리를 수정해야 한다. 이는 프로그램을 수정하기 어렵고 변경에 취약하게 만든다.
변경 보호 패턴
변화가 예상되는 불안정한 지점들을 식별하고 그 주위에 안정된 인터페이스를 형성하도록 책임을 할당하라.
= "설계에서 변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화하라"
메서드 응집도
몬스터 메서드. 긴 메서드는 응집도가 낮기 때문에 이해하기도 어렵고 재사용하기도 어려우며 변경하기도 어렵다.
응집도가 낮은 메서드는 로직의 흐름을 이해하기 위해 주석이 필요한 경우가 대부분이다.
주석을 쓰는 대신 메서드를 작게 분해해서 각 메서드의 응집도를 높여라.
응집도가 높은 메서드는 변경 이유가 단 하나여야 한다. (SRP)
'CS > OOP' 카테고리의 다른 글
[오브젝트] 7장 : 기능 분해의 측면에서 본 OOP (1) | 2024.01.23 |
---|---|
[오브젝트] 6장 - 메시지와 인터페이스 (0) | 2024.01.22 |
[오브젝트] 4장 - 데이터 중심 설계의 문제점과 캡슐화 (0) | 2024.01.15 |
[오브젝트] 3장 - 역할, 책임, 협력 (0) | 2024.01.11 |
[오브젝트 스터디] 2장 - 상속과 다형성 (0) | 2024.01.10 |