2014년 11월 13일 목요일

Command Pattern

 Command Pattern을 쓸 일이 있을까 싶었지만, 최근 필요가 생겼다. 이유는 여러 영상처리 알고리즘 수행을 매크로 처럼 다른 클래스에서 순서를 조합 해주면, 거기에 따라 수행하는것을 만들어야 했는데, Command Pattern 을 하지 않았다면, for문안에 수많은 switch case 문이 들어가고, 새로운 알고리즘이 추가 되었을때 마다 해당 클래스의 수정이 필요 했을 것이다. Command 패턴을 공부하는데 좋은 소스코드를 찾았는데, 매크로같은 기능, excel에서의 ctrl + z 같은 Undo 기능을 Command 패턴으로 구현한 것이다.

[-] Collapse
#include <iostream>
#include <vector>
using namespace std;


class Shape
{
public:
    virtual void Draw() { cout << "Shape Draw" << endl; }
};

class Rect : public Shape
{
public:
    virtual void Draw() { cout << "Rect Draw" << endl; }
};
class Circle : public Shape
{
public:
    virtual void Draw() { cout << "Circle Draw" << endl; }
};

// 모든 명령의 공통의 부모
struct ICommand
{
    virtual void Execute() = 0;
    virtual bool CanUndo() { return false; }
    virtual void Undo() {}
    virtual ~ICommand() {}
};

// 전체적인 변하지 않은 알고리즘은 부모가 변하는 것은 가상함수로 자식이 재정의
// => "template method"

// 그런데, 변하는 것이 알고리즘이 아니라 어떤 객체를 만들것인지에 결정이라면
// => "Factory Method"

class AddCommand : public ICommand
{
    vector<Shape*>& v;
public:
    AddCommand(vector<Shape*>& a) : v(a) {}

    virtual void Execute() { v.push_back(CreateShape()); }
    virtual bool CanUndo() { return true; }
    virtual void Undo()
    {
        Shape* p = v.back();
        v.pop_back();
        delete p;
    }
    // 결국 도형을 추가하는 명령은 어떤 도형인지만 변하게 된다.!
    // 변하는 부분을 최소화 하자. !!
    virtual Shape* CreateShape() = 0;
};



// 도형편집기에 사각형을 추가하는 명령
class AddRectCommand : public AddCommand
{
public:
    AddRectCommand(vector<Shape*>& a) : AddCommand(a) {}

    virtual Shape* CreateShape() { return new Rect; }
};


class AddCircleCommand : public AddCommand
{
public:
    AddCircleCommand(vector<Shape*>& a) : AddCommand(a) {}

    virtual Shape* CreateShape() { return new Circle; }
};


class DrawCommand : public ICommand
{
    vector<Shape*>& v;
public:
    DrawCommand(vector<Shape*>& a) : v(a) {}

    virtual void Execute()
    {
        for (int i = 0; i < v.size(); i++)
            v[i]->Draw();
    }
    virtual bool CanUndo() { return true; }
    virtual void Undo()    { system("cls"); }
};
//---------------------
// 여러 명령을 하나의 명령으로 관리하는 매크로 명령을 도입해 보자
class MacroCommand : public ICommand // Composite 패턴.!
{
    vector<ICommand*> v;
public:
    void AddCommand(ICommand* p) { v.push_back(p); }
    void Execute()
    {
        // 모든 명령을 실행한다.
        for (int i = 0; i < v.size(); i++)v[i]->Execute();
    }
};


#include <stack>
int main()
{
    vector<Shape*> v;
    stack<ICommand*> cmd_stack;

    MacroCommand* p = new MacroCommand;
    p->AddCommand(new AddRectCommand(v));
    p->AddCommand(new DrawCommand(v));
    p->Execute(); // 2개 명령 실행.

    MacroCommand* p2 = new MacroCommand;
    p2->AddCommand(new AddCircleCommand(v));
    p2->AddCommand( p );
    p2->Execute();


    while (1)
    {
        int cmd;
        cin >> cmd;

        if (cmd == 1)
        {
            ICommand* p = new AddRectCommand(v);
            p->Execute();
            cmd_stack.push(p); // 스택에 명령 보관
        }
        else if (cmd == 2)
        {
            ICommand* p = new AddCircleCommand(v);
            p->Execute();
            cmd_stack.push(p);
        }
        else if (cmd == 9)
        {
            ICommand* p = new DrawCommand(v);
            p->Execute();
            cmd_stack.push(p);
        }
        else if (cmd == 0)
        {
            ICommand* p = cmd_stack.top();
            cmd_stack.pop();

            if (p->CanUndo())
                p->Undo();

            delete p;
        }
    }
}

댓글 없음:

댓글 쓰기