2013년 12월 14일 토요일

Design pattern - Sterategy pattern

새로운 방식의 행위, 기능을 추가하거나 기존 방법을 변경할 때 편리하다.  전략패턴 내에서는 행위, 기능이 상위의 인터페이스 클래스를 통해 관리된다.   따라서 전략패턴을 사용하는 클라이언트는 전략패턴 인터페이스의 가상함수를 통해 객체의 구체적 행위에 접근한다.

인터페이스를 통한 연결을 이용하므로 기존의 행위를 변경하거나, 새로운 행위를 추가, 삭제할 때, 클라이언트나 추상 클래스의 변경없이 행위 클래스만 바꾸므로 유지 및 보수가 편리하고 모듈간 독립성을 유지할 수 있다.


전략 패턴의 표기에 일반적으로 사용되는 UML표기는 여기를 참고한다.



위 그림의 출처는 여기를 참고한다.  그림을 보면 두 추상 클래스는 불변이고, 하위의 자식 클래스만 수정된다.



전략 패턴 사용을 위해서는 두 클래스의 그룹을 만들어야 한다.


(1) 행위를 정의하는 그룹: Interface(JAVA), 또는 추상클래스(C++)를 만든다. 이것을 부모로 해서 상속받은 구체적 행위를 정의하는 class가 다수 존재하게 한다.

(2) 행위를 사용하는 그룹: 클라이언트(Client)라고도 부르고 (1)의 행위를 정의하는 추상 클래스 객체를 멤버로 포함(association)한다.  Client class를 상속받은 다수의 class를 다시 정의하며, 이 클래스들이 행위를 가지거나 사용하는 주체가 된다.  각 주체가 어떤 행위를 선택할지는 Client class내의 setter함수를 사용한다.

 
정리해서 생각해 보면, client class를 중심으로 행위 변경은 행위 그룹의 하단(child)에서, 행위를 사용하는 주체는 client class의 하단(child)에서 추가, 삭제, 수정된다.





(예1) 오리의 종류에 따른 행위 표현
#include <cstdlib>
#include <iostream>

using namespace std;


// 인터페이스 클래스의 정의
// Fly라는 행위에 대한 인터페이스
class FlyBehavior
{
public:
  virtual void fly() {};
};

// Fly 행위에 대한 구체적 행위를 나타내는 클래스
class Fly_Normal: public FlyBehavior
{
public:
  void fly()
  {
    cout << "오리날기 기본 - 훨훨!!!" << endl;
  }
};

class Fly_None: public FlyBehavior
{
public:
   void fly()
   {
     cout << "날 수 없음" << endl;
   }
};


// 오리 울음 행위를 나타내는 인터페이스 클래스
class QuackBehavior
{
public:
   virtual void quack() {};
};

// 오리 울음의 종류별 구체적 구현을 나타내는 클래스
class Quack_Normal: public QuackBehavior
{
public:
   void quack()
   {
    cout << "오리 울음 기본 - 꽥꽥!!!" << endl;
   }
};

class Quack_Beep: public QuackBehavior
{
public:
   void quack()
   {
    cout << "오리 울음 삑삑이 - 삑삑!!!" << endl;
   }
};



// Client class
// 행위를 사용하는 클래스
class Duck
{
private:
   QuackBehavior *quackBehavior; // 울음 정의 인터페이스
   FlyBehavior *flyBehavior; // 나는 모습을 정의하는 인터페이스

   void deletQuack() // quack behavior를 해제하는 함수
   {
    if(quackBehavior !=0)
    {
     cout << "이전에 설정된 quack behavior가 있다, 메모리 해제" << endl;
     delete quackBehavior;
    }
 }

 void deleteFly() // Fly behavior를 해제하는 함수
 {
    if(flyBehavior != 0)
    {
     cout << "이전에 설정된 fly behavior가 있다. 메로리 해제" << endl;
    }
 }


public:
   Duck()
   {
      quackBehavior = 0;
      flyBehavior = 0;
      cout << "오리 인스턴스 생성" << endl;
   }
  ~Duck()
  {
     deletQuack();
     deleteFly();

    cout << "오리 인스턴스 소멸" << endl;
  }

 // 나는 행위 추상함수 호출(구체적 행위는 숨겨짐)
 void fly()
 {
    flyBehavior->fly();
 }

 // 울음 행위 추상 함수 호출
 void  quack()
 {
    quackBehavior->quack();
 }


 // setter 함수
 // 구체적 행위를 여기서 설정 
 // 오리 울음을 바꾸는 setter함수
 void setQuackBehavior(QuackBehavior *qb)
 {
    deletQuack();  // 기 설정된 behavior가 있으면 해제
    quackBehavior = qb;
 }

 // 오리의 나는 모습을 바꾸는 setter 함수
 void setFlyBehavior(FlyBehavior *fb)
 {
    deleteFly(); // 기 설정된 behavior가 있으면 해제
    flyBehavior = fb;
 }
};



// client class를 상속 받아 행위를
// 가지는 주체를 설정한다. 
// 청둥 오리 클래스
class MallardDuck: public Duck
{
public:
 MallardDuck()
 {
    cout << "청둥오리 인스턴스 생성" << endl;
    setQuackBehavior(new Quack_Normal); //청둥오리는 Normal 하게 운다
    setFlyBehavior(new Fly_Normal); // 청둥오리는 Normal 하게 난다
 }
};

// 장난감 오리 클래스
class ToyDuck: public Duck
{
public:
 ToyDuck()
 {
    cout << "장난감 오리 인스턴스 생성" << endl;
    setQuackBehavior(new Quack_Beep); //장난감오리는 Beep로 운다
    setFlyBehavior(new Fly_None); // 장난감오리는 못 난다
 }
};


void main()
{
 // 청둥오리 인스턴스 생성
 Duck* k= new MallardDuck;
 k->quack();
 k->fly();
 delete k;

 cout <<endl<<endl;

 // 장난감 오리 인스턴스 생성
 Duck* toy = new ToyDuck;
 toy->quack();
 toy->fly();
 delete toy;

}



(예2) Pattern Matching 

(1) 행위 정의(class match, 추상클래스): SelectFeat() 가상함수를 가짐
-픽셀 밝기 사용 child: class pixelMatch: public match, SelectFeat()에서 밝기사용 매칭의 구현
-에지를 사용 child: class edgeMatch: public match, SelectFeat()에서 에지사용 매칭 구현

// Interface class
class Match
{
public:
virtual void SelectFeat()=0;
};

class pixelMatch: public Match
{
public:
void SelectFeat()
{
cout << "Matching pixels ...\n" << endl;
}
};

class edgeMatch: public Match
{
public:
void SelectFeat()
{
cout << "Matching edges ...\n" << endl;
}
};


(2) class Match를 사용하는 client class
-추상 class patFind는 멤버변수로 Match 객체를 가짐
-find()함수를 가짐: 설정된 방법으로 matching 실행
-setter함수를 통해 match 실행에서 특징 타입(밝기 또는 에지) 설정

-행위를 사용하는 주체가 되는 클래스는 patFind 클래스를 상속한다.
 class edgeFind: public patFind
 class pixelFind: public patFind


// Client class
class patFind
{
public:
patFind() { useFeat_=0; }
~patFind() { deleteFind(); }

void find()
{
cout << "Matching started..." << endl;
useFeat_->SelectFeat();
}
void SetFeat(Match *ft)
{
deleteFind();
useFeat_ = ft;
}
void deleteFind()
{
if(useFeat_)

cout << "기 설정된 특징이 있음. 제거.." << endl;
delete useFeat_;
}
}

private:
Match *useFeat_;
};


// 행위를 가지거나 사용하는 주체가 되는 클래스
class edgeFind: public patFind
{
public:
edgeFind()
{
cout << "Edge matching 인스턴스 생성" << endl;
SetFeat(new edgeMatch);
}
};

class pixelFind: public patFind
{
public:
pixelFind()
{
cout << "Pixel matching 인스턴스 생성" << endl;
SetFeat(new pixelMatch);
}
};



(3) main에서 사용

// Pattern matching 인스턴스 생성(pixel 사용 시)
patFind* find = new pixelFind();
find->find();
delete find;


// Pattern matching 인스턴스 생성(edge 사용 시)
patFind* find = new edgeFind();
find->find();
delete find;





References
[1] Head First Design Pattern, Chapter 1.
[2] 장문석, Escort GoF의 디자인 패턴, 24장
[3] D:\다운로드_2013\패턴_테스트\strg_patt


댓글 없음:

댓글 쓰기