5. 책임 할당하기
- 데이터 중심 설계로 인해 발생하는 문제점을 해결할 수 있는 가장 기본적인 방법은 데이터가 아닌 책임에 초점을 맞추는 것입니다.
- GRASP 패턴은 책임 할당의 어려움을 해결하는 방법입니다.
#
01. 책임 주도 설계를 향해- 데이터 중심의 설계에서 책임 중심의 설계로 전환하기 위해서는 다음의 두 가지 원칙을 따라야 합니다.
- 데이터보다 행동을 먼저 결정하라
- 협력이라는 문맥 안에서 책임을 결정하라
#
데이터보다 행동을 먼저 결정하라- 책임을 결정한 후에 객체의 상태를 결정해야합니다.
#
협력이라는 문맥 안에서 책임을 결정하라- 협력에 적합한 책임을 수확하기 위해서는 객체를 결정한 후에 메시지를 선택하는 것이 아니라 메시지를 결정한 후에 객체를 선택해야 합니다.
- 객체에게 적절한 책임을 할당하기 위해서는 협력이라는 문맥을 고려해야 합니다.
#
책임 주도 설계- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악합니다.
- 시스템 책임을 더 작은 책임으로 분할합니다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당합니다.
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾습니다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 합니다.
#
02. 책임 할당을 위한 GRASP 패턴- GRASP은 "General Responsibility Assignment Software Pattern(일반적인 책임 할당을 위한 소프트웨어 패턴)"의 약자로 객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙들의 집합입니다.
#
도메인 개념에서 출발하기- 어떤 책임을 할당해야 할 때 가장 먼저 고민해야 하는 유력한 후보는 도메인 개념입니다.
- 중요한 것은 설계를 시작하는 것이며 도메인 개념을 완벽하게 정리하는 것이 아닙니다.
올바른 도메인 모델이란 존재하지 않는다.
#
정보 전문가에게 책임을 할당하라- 책임 주도 설계 방식의 첫 단계는 애플리케이션이 제공해야 하는 기능을 애플리케이션의 책임으로 생각하는 것입니다.
- 다음의 질문 순으로 진행됩니다.
- 첫째, 메시지를 전송할 객체는 무엇을 원하는가?
- 둘째, 메시지를 수신할 적합한 객체는 누구인가?
- 객체에게 책임을 할당하는 첫 번째 원칙은 책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것이며, 이를 INFORMATION EXPERT(정보 전문가) 라고 합니다.
#
높은 응집도와 낮은 결합도- 설계는 트레이드 오프입니다.
- 높은 응집도와 낮은 결합도를 얻을 수 있는 설계가 있다면 그 설계를 택해야하며 이를 LOW COUPLING(낮은 결합도) 패턴과 HIGH COHESION(높은 응집도) 패턴이라고 부릅니다.
- LOW COUPLING 패턴에서는 설계의 전체적인 결합도가 낮게 유지되도록 책임을 할당해야 합니다.
- HIGH COUPLING 패턴에서는 높은 응집도를 유지할 수 있게 책임을 할당해야 합니다.
#
창조자에게 객체 생성 책임을 할당하라- CREATOR(창조자) 패턴은 이 같은 경우에 사용할 수 있는 책임 할당 패턴으로서 객체를 생성할 책임을 어떤 객체에게 할당할지에 대한 지침을 제공합니다.
- 어떤 방식으로든 생성되는 객체와 연결되거나 관련된 필요가 있는 객체에 해당 객체를 생성할 책임을 맡기는 것입니다.
#
03. 구현을 통한 검증코드 생략
#
DiscountCondition 개선하기- 위의 코드에서 가장 큰 문제점은 변경에 취약한 클래스를 포함하고 있다는 것입니다.
- 새로운 할인 조건 추가
- 순번 조건을 판단하는 로직 변경
- 기간 조건을 판단하는 로직이 변경되는 경우
- 낮은 응집의 문제를 해결하기 위해서는 변경의 이유에 따라 클래스를 분리해야 합니다.
- 코드를 통해 변경의 이유를 파악할 수 잇는 방법은 다음과 같습니다.
- 인스턴스 변수가 초기화되는 시점을 살핍니다. -> 함께 초기화되는 속성을 기준으로 코드를 분리합니다.
- 메서드들이 인스턴스 변수를 사용하는 방식을 살펴보는 것입니다. -> 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리해야 합니다.
- 다음의 문제가 있다면 클래스의 응집도가 낮은 것입니다.
- 클래스가 하나 이상의 이유로 변경돼야 한다면 응집도가 낮은 것입니다.
- 클래스의 인스턴스를 초기화하는 시점에 경우에 따라 서로 다른 속성들을 초기화하고 있다면 응집도가 낮은 것입니다. 초기화되는 속성의 그룹을 기준으로 클래스를 분리합니다.
- 메서드 그룹이 속성 그룹을 사용하는지 여부로 나뉜다면 응집도가 낮은 것입니다.
#
타입 분리하기- 때로는 응집도가 높아졌으나, 변경과 캡슐화의 관점에서 보면 전체적 설계가 떨어질 때도 있습니다.
#
다형성을 통해 분리하기- 이때 중요한 개념 중 하나는 역할입니다.
- 객체의 타입에 따라 변하는 행동이 있다면 타입을 분리하고 변화하는 행동을 각 타입의 책임으로 할당하는 것이며 이를 POLYMOPHISM(다형성) 패턴이라고 합니다.
#
변경으로부터 보호하기- 변경을 캡슐화하도록 책임을 할당하는 것을
PROTECTED VARIATIONS(변경 보호)
패턴이라고 부릅니다. - 하나의 클래스가 여러 타입의 행동을 구현하고 있는 것처럼 보인다면 클래스를 분해하고
POLYMORPHSIM
패턴에 따라 책임을 분산시킵니다. - 예측 가능한 변경으로 인해 여러 클래스들이 불안정해진다면
PROTECTED VARIATIONS
패턴에 따라 안정적인 인터페이스 뒤로 변경을 캡슐화 합니다.
#
Movie 클래스 개선하기POLYMOPHISM
패턴을 사용하여 서로 다른 행동을 타입별로 분리하면 다형성의 혜택을 누릴 수 있습니다.- 객체지향 설계의 기본은 책임과 협력에 초점을 맞추는 것입니다.
도메인의 구조가 코드의 구조를 이끕니다.
#
변경과 유연성- 설계를 주도하는 것은 변경입니다.
- 이를 대비하는 방법은 두가지가 있습니다.
- 하나는 코드를 이해하고 수정하기 쉽도록 최대한 단순하게 설계하는 것입니다.
- 다른 하나는 코드를 수정하지 않고도 변경을 수용할 수 있도록 코드를 더 유연하게 만드는 것입니다.
- 상속 대신에 합성을 쓰는 것이 좋습니다.
코드의 구조가 도메인의 구조에 대한 새로운 통찰력을 제공합니다.
#
04. 책임 주도 설계의 대안- 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것을 리팩터링(Refactoring) 이라고 합니다.
#
메서드 응집도- 긴 메서드는 다양한 측면에서 코드의 유지보수에 부정적인 영향을 미칩니다.
- 어떤 일을 수행하는지 한눈에 파악하기 어렵기 때문에 코드를 전체적으로 이해하는 데 너무 많은 시간이 걸립니다.
- 하나의 메서드 안에서 너무 많은 작업을 처리하기 때문에 변경이 필요할 때 수정해야 할 부분을 찾기 어렵습니다.
- 메서드 내부의 일부 로직만 수정하더라도 메서드의 나머지 부분에서 버그가 발생할 확률이 높습니다.
- 로직의 일부만 재사용하는 것이 불가능합니다.
- 코드를 재사용하는 유일한 방법은 원하는 코드를 복사해서 붙여넣는 것뿐이므로 코드 중복을 초래하기 쉽습니다.
#
객체를 자율적으로 만들자- 책임 주도 설계 방법에 익숙하지 않다면 일단 데이터 중심으로 구현ㅇ한 후 이를 리팩터링하더라도 유사한 결과를 얻을 수 있습니다.