#14 일관성 있는 협력

조영호 저 "오브젝트: 코드로 이해하는 객체지향 설계, 2019"를 읽고 정리한 내용입니다.

일관성 있는 협력을 사용하면 코드가 이해하기 쉽고 직관적이며 유연해진다. 그리고 유사한 기능을 구현하기 위해 (기존 사용했던) 유사한 협력 패턴을 사용하라.

1 핸드폰 과금 시스템 변경하기

기본정책: 고정요금 / 시간대별 부가정책: 요일별 / 구간별

위의 조합 가능한 경우의 수가 무수히 많을 것이다.

고정요금에 추가할 기본정책 FixedFeeDiscountPolicy / TimeOfDayDiscountPolicy / DayOfWeekDiscountPolicy / DurationDiscountPolicy

수정전 UML

11장의 코드에서 추가 설명하니 11장 주요코드를 가져왔다.

new Phone(new FixedFeeDiscountPolicy(100원, 10초))

아래 코드는 추후 리팩토링해할 코드이다. (유사한기능인데 구현방식이 다 제각각. 이해 x)

고정요금 방식 구현하기

FixedFeePolicy은 기존 RegularPolicy를 이름만 변경.

시간대별 방식 구현하기

4개 중 이게 설명이 엄청 많다..

  • 기간을 관리 할 수 있는 DateTimeInterval 추가

  • 요금계산 로직 두가지 필요 (전체 통화시간 일자와 시간 기준으로 분할하여 계산할것임)

    • 통화기간을 일자별로 분리 + 일자별로 분리된 기간을 다시 시간대별 규칙에 따라 분리한 후 각 기간에 대해 요금을 계산한다.

  • DateTimeInterval에 책임 할당

    • 통화기간을 일자별로 분리

  • TimeOfDayDiscountPollicy에 책임 할당

    • 일자별로 분리된 기간을 다시 시간대별 규칙에 따라 분리한 후 각 기간에 대해 요금을 계산한다.

요금계산 로직 과정

요일별 방식 구현하기

코드 보면 흐름 알 수 있음

구간별 방식 구현하기

코드 보면 흐름 알 수 있음

총코드 링크

step1와 step2가 있는데, step1은 설계에 일관성이 없다는 문제가 있다. 예를 들어, 기본정책을 구현한다는 공통 목적이 있지만 구현 방식이 모두 다르다. 즉, 개념적 연관과 구현적 연관이 다르다.

나중에 코드 돌려보고 step01 step02 이해해야 겠다...

이럴 경우 두가지 상황에서 발목을 잡는다.

  • 새로운 구현 추가하는 상황

  • 기존 구현을 이해해야 하는 상황

기본 정책 설계의 초점은 여러개 규칙을 구성하는 방법을 잘 결정해야 한다.

문제들을 집어보자.

새로운 구현 추가하는 상황

  • DayOfWeekDiscountPolicy는 DayOfWeekDiscountRule 별도 클래스사용. 여러개 규칙 사용 공통점. 그러나 구현방식 다름.

  • FixedFeePolicy는 하나의 규칙으로만, 구성되는데 세가지 서로 다른 구현방식이 존재.

  • 새로운 기본 정책을 추가할수록 코드 사이의 일관성이 점점 어긋나게 된다.

기존 구현을 이해해야 하는 상황

요일별 방식의 구현을 이해한다고 시간대별 방식을 이해하기 쉽지 않다. 구조 분석도 어렵다.

결론

유사한 기능을 서로 다른 방식으로 구현해서는 안된다.

2 설계에 일관성 부여하기

조언1: 다양한 설계 경험을 적용하라. 조언2: 디자인 패턴 적용하기.

  • 기본 지침 - 아래서 설명한다.

    • 변하는 개념을 변하지 않는 개념으로부터 분리해라.

    • 변하는 개념을 캡슐화하라.

조건로직 대 객체탐색

4장 코드로 설명한다.

분기를 사용한 절차지향 방식 if문을 위그림과 같이 객체지향방식으로 분리해내기 위한 기준은 무엇일까? 변경에 초점. 클래스는 명확히 하나의 이유에 의해서만 변경되고 안의 모든 코드는 함께 변경된다. 즉, 단일 책임원칙을 따도록 분리해야한다.

기본 지침1: Amount, Percent 같은 변하는 개념을 타입계층으로 분리하고 이 서브타입을 클라이언트로 캡슐화 하였다. 기본 지침2: Movie가 아는 사실은 협력 객체가 단지 DiscountPolicy라는 것 뿐이다.

6장부터 요약

6장에서 인터페이스 설계 원칙은 캡슐화하는 코드를 알려줬고, 8-9장의 의존성 관리 기법은 캡슐화하기 위해 낮은 결합도 유지 방법을 알려주었다. 타입을 캡슐화하기 위해서가 아니라 코드 재사용을 위해 상속을 사용하고 있다면 10장을 다시보자. 11장 합성도 캡슐화를 보장하는 훌륭한 기법이다. 13장에서는 LSP를 준수하는 타입계층을 구현하는데 상속을 이용하였다.

캡슐화 다시 살펴보직

캡슐화는 데이터 은닉 대상이다. GOF의 조언은 캡슐화란 단순히 데이터를 감추는게 아니다. 변할 수 있는 모든 "개념"을 감춘다고 얘기하는데, 그냥 변하는 얻너 것이든 감추는 것을 이해하자.

캡슐화 대표적 예 - 객체 퍼블릭 인터페이스와 구현 분리

캡슐화 종류 - 데이터 / 메서드 / 객체 / 서브타입

변경을 캡슐화하는 방법 중에 서브타입 캡슐화와 객체 캡슐화를 조합하는 것이 가장 일반적이다. 다른 방법은 디자인 패턴을 보자.

  • 적용방법

    • 변하는 부분을 분리해서 타입계층 만든다. Amount, Percent.

    • 변하지 않는 부분의 일부로 타입계층을 합성한다. DiscountPolicy.

3 일관성 있는 기본정책 구현하기

이제 Phone 코드를 리팩토링 해보자.

변경 분리하기

시간대별, 요일별, 구간별 패턴이 (기본정책 구성 방식) 비슷하다. 다음과 같다.

  • 기본 정책은 한개 이상의 규칙으로 구성된다

  • 하나의 규칙은 적용조건과 단위요금의 조합이다.

단위요금과 다르게 적용조건의 형식은 다르다. = 변하는 부분 = 분리해야 되는 부분.

변경 캡슐화하기

변하는 부분 적용조건에 대해 FeeCondition으로 캡슐화하였다.

협력 패턴 설계하기

추상화 수준에서 협력 패턴 구현하기

구체적인 협력 구현하기

FeeCondition 인터페이스를 실체화하는 클래스에 따라 기본정책 종류가 달라진다. 시간대별, 요일별, 구간별.

유사한 기능에 대해 유사한 협력 패턴을 적용하는 것은 객체지향 시스템에서 개념적 무결성을 유지할 수 있는 효과적인 방법이다.

협력 패턴에 맞추기

FixedFeeCondition 고정 요금 정책은 규칙이라는 개념이 필요하지 않고 단위요금 정보만 잇으면 충분하다. 다시말해 기존 협력 방식에서 벗어날 수 밖에 없다.

그럼 또 다른 협력 패턴을 사용해야 하는가? 그렇지 않다. 모양이 이상하더라도, 기존의 협력 패턴에 맞추는 것이 가장 좋다. = 개념적 무결성을 깨트리는 것보다 약간의 부조화가 낫다.

수정후 UML

패턴을 찾아라

협력의 구조를 찾는 것은 긴 여정이다. 따라서 협력을 일관성 있게 만든다는 것은 유사한 변경을 수용할 수 있는 협력 패턴을 발견하는 것과 동일하다. 다음장에서 이와 관련된 패턴과 프레임워크를 살펴보자.

Last updated