본문 바로가기

Design patterns

스트래티지 패턴(strategy pattern)

스트래티지 패턴(Strategy Pattern)


스트래티지 패턴(Strategy Pattern)에서는 알고리즘군을 정의하고 각각을 캡슐화 하여 교환해 사용할 수 있도록 만든다. 스트래티지 패턴을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.



예제를 통해 알아보도록하자(예제는 헤드퍼스트의 디자인패턴 책에서 인용)


1. 간단한 오리 어플리케이션


한 개발자는 오리 어플리케이션을 만드는 회사에서 근무하고있다.

현재 오리 어플리케이션엔 울기, 헤엄치기의 기능이 있으며 성공적인 서비스를 진행하고있다.

이 어플리케이션은 UML을 과 같이 추상클래스인 Duck을 만들어 오리들의 공통항목인 꽥(quack)과 헤엄(swim), 그리고 모습그려주기(display)를 정의하였고,  이렇게 생성한 추상클래스로 MallardDuck과 RedHeadDuck 등 여러 오리를 구현했다.


그러던 중 임원회의에서 새로운 기능(날아다니기)이 필요하다는 의견이 나와 개발자는 오리 어플리케이션에 날아다니는 기능을 추가해야한다.



2. 어플리케이션의 기능 추가


이때 개발자는 생각한다. 수퍼클래스인 Duck에 fly()를 추가하면 모든 오리들이 상속을 받으니 간단하게 구현이 가능하겠네? 하고 Duck에 fly()를 추가했다.

그런데 이때 심각한 문제가 발생, Duck을 이용해 만든 객체인 고무오리인형이 날아다니는 것이다.


결국 모든 오리가 날아다니는 것은 아니므로 날지못하는 오리의 fly를 오버라이드하여 아무 행동도 하지 않게끔 처리하였으나. 앞으로 새로운 오리를 생성할 때 오리가 나는지 못나는지에 따라 계속 오버라이드를 해주어야한다.


그렇다면 인터페이스를 사용하면 어떨까.


인터페이스를 이용해 Flyable과 Quackable을 만들어 각 객체별로 적절하게 implements 한다면? 그러나 이 방법도 문제가 있다. 

인터페이스엔 단순 선언만 되어있고 구현된 코드는 전혀 들어가있지 않기때문에 fly가 날개짓에서 활강으로 변하게 된다면 Flyable을 이용한 수많은 객체들에 구현한 소스코드를 변경하여야된다. 코드의 재사용성따윈 개나 주는 상황이 되어버린다.

따라서 인터페이스활용 또한 좋은 방법은 아니다.


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

'어플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다.'

이 원칙은 다음과 같은 식으로 생각 할 수 있다.

'바뀌는 부분은 따로 뽑아서 캡슐화시킨다. 그렇게 하면 나중에 바뀌지 않는 부분에는 영향을 미치지 않은 채로 그 부분만 수정할 수 있다.'


3. 스트래티지 패턴(Strategy Pattern)


바뀌는 부분 : 울기(quack), 날기(fly)

바뀌지 않는 부분 : 헤엄치기(swim)


그렇다면 바뀌는 부분을 캡슐화해보자

FlyBehavior 인터페이스에 fly을 선언한 후 날개짓(FlayWithWings)과 날지못함(FlyNoWay), 그리고 로켓으로 날아가는 FlyRocketPowerd를 만들었다. 

QuackBehavior 인터페이스엔 quack을, 그리고 꽥꽥 우는 Quack과 빼액! 하는 Squeak, 그리고 울지 못하는 MuteQuack을 만듬.


이제 바뀌는 부분의 캡슐화가 끝났다. 만약 날아가는, 혹은 울음이 변경된다면 FlyBehavior와 QuackBehavior를 implements하는 새로운 class를 만들면 된다.



이와 같이 바뀌는 부분과 바뀌지 않는 부분을 구분하여 설계하는 것이 스트래티지 패턴이다.

=====소스코드=====


StrategyPattern.zip




//FlyBehavior.java
package designpattern.strategy.filyBehavior;
public interface FlyBehavior {
	public void fly();
}
 
//QuackBehavior.java
package designpattern.strategy.quackBehavior;
public interface QuackBehavior {
	public void quack();
}
//Duck.java
package designpattern.strategy.duck;
import designpattern.strategy.filyBehavior.*;
import designpattern.strategy.quackBehavior.*;

public abstract class Duck {
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;
	
	public Duck(){
		
	}
	
	public abstract void display();
	public void performFly(){
		flyBehavior.fly();
	}
	public void performQuack(){
		quackBehavior.quack();
	}
	public void swim(){
		System.out.println("모든 오리는 물에 뜹니다. 장난감 오리를 포함해서요");
	}
	public void setFlyBehavior(FlyBehavior fb){
		flyBehavior = fb;
	}
	public void setQuackBehavior(QuackBehavior qb){
		quackBehavior = qb;
	}
}
//FlyWithWings
package designpattern.strategy.filyBehavior;

public class FlyWithWings implements FlyBehavior {
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("날고있습니다!");		
	}
}

</pre></div><div>
//Quack.java
package designpattern.strategy.quackBehavior;
public class Quack implements QuackBehavior{
	public void quack(){
		System.out.println("꽤애애액!!");
	}
}
//MallardDuck.java package designpattern.strategy.duck; import designpattern.strategy.filyBehavior.FlyWithWings; import designpattern.strategy.quackBehavior.Quack; public class MallardDuck extends Duck{ public MallardDuck(){ quackBehavior = new Quack(); //MallardDuck은 꽥꽥 소리를 낸다. flyBehavior = new FlyWithWings(); //MallardDuck은 날아다닐 수 있다. } @Override public void display() { // TODO Auto-generated method stub System.out.println("저는 물오리입니다!"); } }
//RobotDuck.java
package designpattern.strategy.duck;

import designpattern.strategy.filyBehavior.FlyNoWay;
import designpattern.strategy.quackBehavior.Quack;

public class RobotDuck extends Duck{

	public RobotDuck(){
		quackBehavior = new Quack();		//RubberDuck은 삑삑 소리를 낸다.
		flyBehavior = new FlyNoWay();		//RubberDuck은 날아다닐 수 없다 ㅠㅠㅠ.
	}
	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("저는  오리로봇입니다!");
	}

}

//DuckSimulation.java
package designpattern.strategy.main;

import designpattern.strategy.duck.Duck;
import designpattern.strategy.duck.MallardDuck;
import designpattern.strategy.duck.RobotDuck;
import designpattern.strategy.filyBehavior.FlyRocketPowered;

public class DuckSimulator {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Duck mallard = new MallardDuck();
		mallard.performQuack();
		mallard.performFly();
		mallard.display();
		//mallard.swim();
		
		System.out.println("===================");
		
		Duck robot = new RobotDuck();
		robot.performQuack();
		robot.performFly();
		robot.setFlyBehavior(new FlyRocketPowered());
		robot.performFly();

	}

}

다음 챕터는 옵저버패턴.


열심히......공부하자