제가 보는 책입니다.

블로그는 참조만 해주시고 진짜 공부하려면

책 사는 것을 권장합니다.

진짜 좋은 책입니다.

Head First - Design Patterns 입니다.


Strategy_Pattern.jar

아래에서 설명할 스트래티지 패턴의 예제입니다.

다운로드 받으시고 실행하시면 좀 더 이해하기 수월하실 겁니다.

자바파일입니다.


문제의 시작

어떤 팀은 성공적인 오리 연못 시뮬레이션 게임을 만들었다.

이 게임에서의 오리는 헤엄도 치고 꽥꽥거리는 소리도 내는 매우 다양한 오리 종류를 보여줄 수 있다.

처음으로 이 오리를 디자인한 사람들은 Duck이라는 수퍼클래스를 만들었고,

Duck 클래스를 확장하여 다른 종류의 오리들을 만들었다.


Duck클래스에는 quack(), swim(), display()등의 메소드가 존재한다.


이제 오리들을 날아다닐 수 있게 하고 싶다.

그렇다면!


어떻게 해결해볼까??

수퍼클래스인 Duck클래스에 fly() 메소드를 추가하면 되지 않을까??

정말 간단하다.


하지만 문제가 발생했다. 날 수 있는 오리도 있지만, 날지 못하는 오리 인형 같은 것들이 날라다니기 시작한 것이다.

수퍼클래스에 fly() 메소드가 추가되면서 일부 서브클래스에는 적합하지 않은 행동이 전부 추가된 것이다.


상속을 활용하여 코드를 재사용할 수 도 있지만 이런 부작용이 발생할 수 있다.


오리 종류마다 quack()메소드(울음 소리)를 오버라이드 한 것처럼 fly() 메소드 또한 오버라이드하면 되지않을까!?

오리 인형은 아무 동작도 하지않게 오버라이드하면 되겠다!


하지만 이 방법 또한 문제가 많다.

오리의 종류마다 오버라이드를 한다면 모든 오리의 행동을 알기 힘들다는 것이다.

또한, 런타임 도중에 오리의 특징을 바꾸기도 힘들다.


인터페이스는 어떨까???

fly() 메소드를 Duck 수퍼클래스에서 빼고 fly() 메소드가 들어가있는 Flyable 인터페이스를 만든다면!

하늘을 나는 오리에 대해서만 이 인터페이스를 구현하여 fly() 메소드를 집어넣으면 되겠다!!!!


모든 오리들이 꽥꽥 우는 것도 아니니 Quackable이라는 인터페이스를 같이 만들어보자!


으악.. 이 방법도 문제가 있다.

서브클래스에서 이 인터페이스들(Flyable, Quackable)을 구현한다면 날거나 울음 소리에 대해 전부 오버라이딩을 해야하므로

코드의 재사용을 전혀 할 수 없다.

100가지의 오리 종류가 있다면 100가지 모두 일일이 다 구현해야하는 것이다.


진짜 어떻게 해결해야하지??ㅠ_ㅠ

이 상황에 가장 어울리는 디자인 원칙이 있다.


애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리하라.


코드에 새로운 요구사항이 있을 때마다 바뀌는 부분이 있다면, 바뀌지 않는 다른 부분으로부터 골라내서 분리해야한다는 말이다.

바뀌는 부분은 따로 뽑아서 캡슐화한다면, 바뀌지 않는 부분에는 영향을 미치지 않은 채로 그 부분만 고치거나 확장할 수 있다.

모든 패턴은 시스템의 일부분을 다른 부분과 독립적으로 변화시킬 수 있는 방법을 제공하기 위한 것이다.


수퍼클래스 Duck에서 fly()와 quack()을 제외하면 다른 부분은 잘 작동한다.

그렇다면 달라지는 부분인 fly()와 quack()을 분리하여 새로운 집합을 만들어보자.


그리고 Duck()의 행동을 동적으로 혹은 유연하게 바꿀 수 있도록, fly와 quack부분에 대해 setter 메소드를 포함하자.


두번째 디자인 원칙!


구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.


여기서 말하는 인터페이스는 자바의 인터페이스라기 보다는 객체가 코드에 의해서 고정되지 않도록, 어떤 상위 형식에 맞춰서 프로그래밍함으로써

다형성을 활용해야한다는 것이다.

그리고 상위 형식에 맞춰서 프로그래밍하라는 원칙은 변수를 선언할 때는 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다는 말이다.

이렇게 하는 이유는 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 집어넣을 수 있기 때문이다.

또한, 이렇게 하면 변수를 선언하는 클래스에서 실제 객체의 형식을 몰라도 된다.


예를 들어..


Dog d = new Dog();

d.bark();


이렇게 한다면 구체적인 구현에 맞춰서 코딩을 해야한다.

하지만 상위 형식에 맞춰 코딩한다면??


Animal animal = new Dog();

animal.makeSound();


Dog라는 걸 알고 있긴 하지만 다형성을 활용하여 Animal에 대한 레퍼런스를 써도 된다는 말이다.


더 바람직한 방법은 인스턴스를 만드는 과정을 new Dog()처럼 직접만드는 대신 구체적으로 구현된 객체를 실행시에 대입하는 것이다.


a = getAnimal();

a.makeSound();


Animal의 하위 형식 가운데 어떤 형식인지 몰라도 makeSound()에 대해 올바른 반응을 할 수 있다.


어쨋든!!

위에서 분리한 fly와 quack을 인터페이스로 FlyBehavior 와 QuackBehavior로 구현을 하고 

그 인터페이스 안에 FlyWithWings, FlyNoway 그리고 Quack, Squeak, MuteQuack으로 구체적인 행동을 하는 클래스를 만든다.


이런 식으로 디자인하면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할 수 있다.

그런 행동이 더 이상 Duck클래스 안에 숨겨져 있지 않기 때문이다.


그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 클래스는

Duck클래스를 전혀 건드리지 않고도 새로운 행동을 추가할 수 있다.


이렇게 한다면 상속을 쓸 때 떠안는 부담을 전부 떨쳐 버리고 코드 재사용의 장점을 그대로 누릴 수 있다.


이 패턴이 바로 스트래티지 패턴이다!


스트래티지 패턴의 정의는! 바로바로!


알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다.

스트래티지 패턴을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다!

'CS기본지식 > 디자인 패턴' 카테고리의 다른 글

데코레이터 패턴(Decorator Pattern)  (0) 2017.08.28
옵저버 패턴(Observer Pattern)  (0) 2017.08.25

+ Recent posts