본문 바로가기

프로그래밍/패턴,설계,자료구조

스트래티지 패턴 ( Strategy Pattern ) [ Head First Design Pattern 내용중에서 ]

스트래티지 패턴 이란? 알고리즘을 정의하고 각각을 캡슐화 하여 사용할수 있도록 만든다. 스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할수 있다.

예제 소스는 책에 있는 내용대로 하였으며  AS3.0으로 작성하였다.

"연못 시뮬레이션 게임"을 만든다고 가정했을때 그 안에 존재하는 다양한 오리 종류를 보여 줄수있다.

일단 " 오리 " 라는 객체를 두고 생각해 봤을때 달라지는 부분을 찾아내고 달라지지 않는 부분으로 부터 분리 시킨다.

달라지는 부분 즉 각 행동의 집합( 날다, 소리내다)군을 알고리즘 군으로 생각하고 그에 따라 기능 인터페이스를 구현한다.

디자인 원칙 : 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리 시킨다. 

// 날수 있는 클래스를 새로 만들때 무조건 구현하도록
public interface  IFlyBehavior
{	
function fly() :void;		
}
// 소리낼수 있는  클래스를 새로 만들때 무조건 구현하도록
public interface IQuackBehavior
{		
function quack() :void;
}
이렇게 작성된 interface로 필요한 기능에 따라 해당 클래스에서 구현을 해준다.

	public class FlyWithWings implements IFlyBehavior
	{
		public function FlyWithWings()
		{
		}
		
		public function fly():void
		{
			trace("자유롭게 날고 있어요");
		}
	}
	public class FlyNoWay implements IFlyBehavior
	{
		public function FlyNoWay()
		{
		}
		
		public function fly():void
		{
			trace("나에게 날개를 달라");
		}
	}
	public class MuteQuack implements IQuackBehavior
	{
		public function MuteQuack()
		{
		}
		
		public function quack():void
		{
			trace("조용~~");
		}
	}
	public class Quack implements IQuackBehavior
	{
		public function Quack()
		{
		}
		
		public function quack():void
		{
			trace("Quack  Quack");
		}
	}

 

Duck이라는 클래스에 행동관련 내용을 구현한 이후에 상속을 받아 서브 클래스상에서 override 하는 방식을 쓸수도 있다.

하지만 어떠한 행동에 대한 수정요청이 들어왔을 경우 해당하는 서브클래스상에서는 일일히 같은 작업을 해야 할것이다.

패턴을 쓸려고 하는 이유가 여기서 나온다.

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

 public class Duck 
 {
  public var iFlyBehavior : IFlyBehavior; //날다 
  public var iQuackBehavior : IQuackBehavior;//소리내다
  
  public function Duck()
  {   
  }

  public function performFly() :void
  {   iFlyBehavior.fly();   }
  
  public function setFlyBehavior( fb : IFlyBehavior  ) :void
  {   iFlyBehavior = fb;  }
    
  public function performQuack():void
  {   iQuackBehavior.quack();   }
  
  public function setQuackBehavior( qb : IQuackBehavior  ) :void
  {   iQuackBehavior = qb;  }
  
  public function swim() :void
  {   trace("둥둥~");  }
  
  public  function display() :void 
  {   throw new Error("must be overriden");  }
  
 }
이런식으로 디자인하면 다른형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할수 있다. 그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck클래스를 전혀 건드리지 않고도 새로운 행동을 추가할수 있다.
	public class MallardDuck extends Duck
	{
		public function MallardDuck()
		{
			this.iQuackBehavior = new Quack();
			this.iFlyBehavior = new FlyWithWings();
		}
				
		// 추상 메소드 구현 
		override public function display() :void
		{
			trace("물오리");		
		}  
	}
테스트
	public class MiniDuckSimulator extends Sprite
	{
		public function MiniDuckSimulator()
		{
			var mallard : Duck = new MallardDuck();
			mallard.performQuack();			
			mallard.performFly();					
			mallard.setFlyBehavior( new FlyRocketPowered() );			
			mallard.performFly();			
			
		}
	}

디자인 원칙 : 상속보다는 구성을 활용한다.

구성이란? Duck클래스에 FlyBehavior와 QuackBehavior가 있으며 , 각 각 행동과 꽥꽥거리는 행동을 위임받는다.
두 클래스를 이런식으로 합치는 것을 구성( Composition)을 이용하는 것이라 한다.("A에는 B가 있다")

클라이언트에서는 날기 행동과 꽥꽥거리는 행동 모두에 대해서 캡슐화된 알고리즘을 활용한다. 그로 인해 유연성을 크게 향상 시킬수 있으며 객체에서 올바른 행동 인터페이스를 구현하기만 하면 실행시에 행동을 바꿀수도 있게 한다.