바뀌는 부분은 캡슐화 한다.


코드에 새로운 요구사항이 있을 때마다 바뀌는 부분이 있다면, 바뀌는 부분은 바뀌지 않는 다른 부분으로부터 골라내서 분리해야한다. 그래야 바뀌지 않는 부분에 영향을 미치지 않은 채로 그 부분만 고치거나 확장 할 수 있기 때문이다.


예시

 간단한 예시로, 날아다니는 기능과 꽉꽉 소리를 내는 기능을, 그리고 수영하는 기능을 가진 오리(Duck) 클래스를 구현하는 경우를 들어 보겠다.

오리에도 여러 종류가 있는데, 청둥오리(MallardDuck), 붉은머리오리(RedheadDuck), 고무오리(RubberDuck), 나무오리(DecoyDuck)가 그들이다.

각각의 오리는 꽉꽉 소리를 내는 방법도,  날아다니는 방법도 서로 다르다.


여기까지만 생각하면 상속을 이용해서 다음과 같이 디자인 할 수 있었을 것이다.


이 구조로 구현하면 MallardDuc, RedheadDuck, RubberDuck 클래스들이 제각각 quack(), fly(), swim() 메소드를 알아서 구현 하면 된다.

하지만 여기엔 몇 가지 문제점이 있다. 

RubberDuck은 고무오리이기 때문에 날 수 없고, DecoyDuck은 나무오리이기 때문에 날 수도 없고, 꽉꽉소리도 낼 수 없다. 즉, 없는 기능을 메소드로 가지는 것이다.

그리고 꽉꽉 소리를 내는것에 대해서, MallardDuck과 RedheadDuck은 꽉꽉 하고 소리를 내지만, RubberDuck은 삑삑 하고 소리를 낸다.

요약하면 quack(), fly() 메소드는 서브클래스마다 바뀌는 부분인 것이다.


물론 서브클래스들에서 두 메소드를 적절히 정의하면 가능하긴 하다. 그러면 다음과 같이 구현 될 것이다.


하지만 이 방법들로는 코드 중복이 발생하기 때문에 코드의 보수성이 떨어진다. (MallardDuck과 RedheadDuck의 quack() 메소드가 동일. RubberDuck과 DecoyDuck의 fly() 메소드가 동일)


이 예시에서 quack(), fly() 메소드는 바뀌는 부분에 속하고, swim() 메소드는 바뀌지 않는 부분에 속한다.(물론 swim() 메소드도 구현하기에 따라서 바뀌는 부분이 될 수도 있지만, 여기서는 모두 동일하게 구현된다고 하자)

이렇게 바뀌는 부분은 다른 클래스(혹은 인터페이스)로 빼내어 구현해야한다.


바뀌는 부분을 따로 빼내어 다음과 같이 구현할 수 있다.


이와 같이 구현하면 코드 중복문제도 해결되고, 새로운 기능의 추가도 한결 쉬워진다.

가령 Duck의 서브클래스로 RocketDuck이 추가되고, 이 RocektDuck은 로켓을 타고 날아다닌다고 하면, FlyBehavior 인터페이스를 구현하는 FlyByRocekt 구상 클래스를 추가하면 끝이다.

또, MallardDuck 객체중 10% 확률로 날 수 없는 객체가 생성된다고 할 때, 그 MallardDuck 객체에게 FlyBehavior로 FlyNoWay 구상 클래스를 추가해 주면 그만이다.

'IT 관련 > 객체지향 설계' 카테고리의 다른 글

헐리우드 원칙(Hollywood Principle)  (0) 2019.10.01
블로그 이미지

서기리보이

,