오브젝트 04_ 설계 품질과 트레이드오프에 언급된 데이터 중심 설계 문제점의 해결 방법인 책임 할당하기 내용으로
이전 시간에 언급된 주요 개념들이 자주 등장한다. 중간에 구현으로 검증하는 과정이 있지만 이전에 설명한 내용으로 생략한다.
자세한 내용이 궁금하면 오브젝트를 펼쳐보는 걸 추천드립니다.📖
책임 주도 설계를 향해
데이터보다 행동을 먼저 결정하라
객체는 외부에 제공하는 행동이 중요하다. 협력안에서 수행하는 책임이 객체의 존재가치를 증명한다. 데이터는 객체의 행동을 수행할 재료일 뿐이다. 재료가 행동보다 우선시 되면 캡슐화를 위반하게 되고 변경에 취약하게 된다.
즉, 책임을 먼저 결정한 후에 객체의 상태를 결정해야 한다.
협력이라는 문맥 안에서 책임을 결정하라
책임의 품질은 협력에 적합도로 결정된다. 협력에 어울리지 않는다면 책임이 잘못된거다. 객체가 어색해도 책임이 적합하면 괜찮다.
즉, 책임은 객체의 입장이 아닌 협력의 관점에서 적합해야한다.
협력에 적합한 책임을 결정하는 것은 객체를 결정하고 메시지를 선택하는 게 아니라 메시지를 결정한 후에 객체를 선택해야 한다.
클래스 기반 설계에서 메시지 기반 설계로의 자리바꿈은 우리가 해오던 설계 활동의 전환점이다. 메시지 기반의 설계 관점은 클래스 기반의 설계 관점보다 훨씬 유연한 애플리케이션을 만들 수 있게 해 준다.
"이 클래스가 필요하다는 점은 알겠는데 이 클래스는 무엇을 해야 하지?"라고 질문하지 않고
"메시지를 전송해야 하는데 누구에게 전송해야 하지?"라고 질문하는 것. 설계의 핵심 질문을 이렇게 바꾸는 것이 메시지 기반 설계로 향하는 첫걸음이다.
객체를 가지고 있기 때문에 메시지를 보내는 것이 아니다. 메시지를 전송하기 때문에 객체를 갖게 된 것이다. [Metz12]
결론적으로 책임 주도 설계에서는 협력이라는 문맥 안에서 객체가 수행할 책임에 초점을 맞춘다.
책임 주도 설계의 흐름
- 사용자에게 제공해야 할 시스템 책임을 파악
- 시스템 책임을 더 작은 책임으로 분할
- 작아진 책임을 객체 또는 역할을 찾아 책임을 할당
- 객체가 책임을 수행할 때 외부 도움이 필요하면 적절한 객체 또는 역할을 찾고 책임을 할당
- 두 객체가 협력
책임 할당을 위한 GRASP 패턴
GRASP(General Responsibility Assignment Software Pattern)는 일반적인 책임 할당을 위한 소프트웨어 패턴으로 객체에 책임을 할당할 때 지침으로 삼는 원칙들의 집합이다.
도메인 개념에서 출발하기
설계 시작 전에 도메인의 개략적인 모습을 그려보는 게 좋다. 이 도메인 개념들을 사용하면 코드에 도메인의 모습을 투영하기 쉬워진다.
정보 전문가(Information expert)에게 책임을 할당하기
객체에게 책임을 할당하는 첫 번째 원칙은 책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것이다. 여기서 정보는 데이터와 다르다. 책임을 수행하는 객체가 정보를 '알고'있다고 해서 그 정보를 '저장'할 필요는 없다.
만약 객체 스스로 작업을 처리할 수 없다면 외부의 도움을 요청해야 한다. (협력하는 것이다)
높은 응집도(High cohesion)와 낮은 결합도(Low coupling)
객체에 책임을 할당할 때 한 가지 대안이 아닌 여러 가지 대안이 나올 수도 있는데, 이때 신경 써야 할 기본 원리가 높은 응집도와 낮은 결합도다. 높은 응집도와 낮은 결합도를 가진 설계가 가장 좋은 선택이다.
* 설계는 트레이드오프 활동이라는 것을 기억하라
창조자(Creator)에게 객체 생성 책임을 할당하기
창조자 패턴은 책임 할당 패턴으로 객체를 생성할 책임을 어떤 객체에게 할당한 것인지 가이드를 제공한다.
다음은 창조자에 대한 조건으로 최대한 해당되는 객체에게 생성 책임을 할당하면 된다.
- B가 A 객체를 포함하거나 참조
- B가 A 객체를 기록
- B가 A 객체를 긴밀하게 사용
- B가 A 객체를 초기화하는데 필요한 정보를 소유(B는 A에 대한 정보 전문가)
한마디로 설명하자면 B가 A를 잘 알고 있는 것으로 두 객체는 결합된 상태다. 이미 결합된 객체에게 생성 책임 할당은 설계의 전체적인 결합도에 영향을 미치지 않는다. 즉, 낮은 결합도를 유지한다.
낮은 응집도 파악하기
첫째, 인스턴스 변수가 초기화되는 시점을 보는 것이다.
높은 응집도를 가진 객체라면 인스턴스를 생성할 때 모든 속성도 함께 초기화된다. 하지만 낮은 응집도를 가진 객체는 일부분만 초기화되고 일부분은 남겨진다. 이런 경우 함께 초기화되는 속성을 기준으로 코드를 분리해야 한다.
둘째, 메서드들이 인스턴스 변수를 사용하는 방식을 보는 것이다.
모든 메서드가 객체의 모든 속성을 사용한다면 높은 응집도를 가진다 볼 수 있다. 하지만 속성에 따라 메서드들이 그룹이 나눠진다면 낮은 응집도로 볼 수 있다. 이런 경우 응집도를 높이기 위해서 속성과 메서드의 그룹을 기준으로 코드를 분리해야 한다.
변경에 대비
변경에 대비하는 두 가지 방법이 있다.
첫째, 코드를 이해하고 수정하기 쉽게 최대한 단순하게 설계
둘째, 코드를 수정하지 않고도 변경이 수용되도록 유연하게 설계
대부분 전자가 좋지만, 유사한 변경이 자주 발생한다면 후자가 더 좋다.
유연성
유연성은 의존성 관리의 문제로 의존성이 유연성을 결정하게 된다. (설계가 트레이드오프 산물인걸 기억하라)
유연성의 정도에 따라 결합도를 조절하는 능력은 객체지향 개발자가 지녀야 할 중요한 기술 중 하나다.
책임 주도 설계의 대안
책임 주도 설계에는 많은 시간과 경험이 필요하다. 그러나 숙련된 설계자도 책임 할당에 어려움을 느끼기도 한다. 그럴 경우 사용하는 방법이 실행되는 코드를 작성하는 것이다. 객체에 책임 할당이 어렵다면 우선 구현을 해보고 책임을 올바른 위치로 조정하는 것이다. 설계의 실마리가 풀리지 않을 때 사용하면 생각보다 좋은 설계를 얻게 되는 경우가 종종 존재한다.
주의할 점은 외부에 보이는 동작은 변함없이 코드를 수정해야 한다는 것이다. 이를 리팩터링이라 부른다.
메서드 응집도
긴 메서드는 다양한 측면에서 부정적인 영향을 끼친다.
- 코드를 이해하는데 오래 걸린다.
- 변경이 생길 경우 수정할 부분을 찾기 힘들다.
- 일부만 수정해도 나머지 로직에 문제가 생길 확률이 높다.
- 일부만 재사용하는 것이 불가능하다.
- 재사용하려고 일부 코드를 복붙 하는 경우가 생겨 코드 중복을 초래한다.
해결 방법은 로직을 작은 단위의 메서드로 쪼개는 것뿐이다. 작은 메서드들로 조합된 메서드는 주석들을 나열한 것처럼 보여 이해하기 쉬워지고 재사용이 쉬워지며, 코드 중복 문제가 해결된다.
'서적 > 오브젝트: 코드로 이해하는 객체지향 설계' 카테고리의 다른 글
오브젝트 07_ 객체 분해 (0) | 2021.06.09 |
---|---|
오브젝트 06_ 메시지와 인터페이스 (0) | 2021.06.09 |
오브젝트 04_ 설계 품질과 트레이드 오프 (0) | 2021.06.08 |
오브젝트 03_ 역할, 책임, 협력 (0) | 2021.06.08 |
오브젝트 02_ 객체지향 프로그래밍 (0) | 2021.06.08 |