2014년 6월 16일 월요일

design pattern

Object creation
Prototype
Factory Method
Abstract Factory
Builder
Singleton

Interface adaptation
Adapter
Bridge
Facade

Decoupling of Objects
Mediator
Observer

Abstract collection
Composite
Iterator

Behavioral extension
Visitor
Decorator
Chain of Responsibility

Algorithm encapsulation
Template method
Strategy
Command

Performance and object access
Flyweight
Proxy

State of object
Memento




Abstract factory
-switch-case는 class의 상속 관계(추상-구상)를 이용하면 없앨 수 있다. 
-client는 factory 클래스와 product 클래스의 인터페이스만 사용한다.





// Abstract factory pattern
// Coded by FunMV
class Scanner {}; // 생성할 product의 추상클래스
class Parser {};
class Codegenerator {};
class Optimizer {};

class  WindowsScanner: public Scanner {}; //product의 구상클래스
class WindowsParser: public Parser {};
class WindowsCodegenerator: public Codegenerator {};
class WindowsOptimizer: public Optimizer {};

class LinuxScanner: public Scanner {}; //product의 구상클래스
class LinuxParser: public Parser {};
class LinuxCodegenerator: public Codegenerator {};
class LinuxOptimizer: public Optimizer {};


class CompilerFactory // 추상팩토리 클래스
{
public:
virtual Scanner* CreateScanner()=0; //관련 기능들의 덩어리 생성예정
virtual Parser* CreateParser()=0;
virtual Codegenerator* CreateCodegenerator()=0;
virtual Optimizer* CreateOptimizer()=0;
};

// 구상 팩토리 클래스
// windows os에 맞는 컴파일러 관련 기능들의 덩어리 구현
class WindowsCompiler: public CompilerFactory
{
public:
Scanner* CreateScanner()
{
wscanner.reset(new WindowsScanner);
return wscanner.get();
}
Parser* CreateParser()
{
wparser.reset(new WindowsParser);
return wparser.get();
}
Codegenerator* CreateCodegenerator()
{
wcodegenerator.reset(new WindowsCodegenerator);
return wcodegenerator.get();
}
Optimizer* CreateOptimizer()
{
woptimizer.reset(new WindowsOptimizer);
return woptimizer.get();
}
private:
unique_ptr<Scanner> wscanner;
unique_ptr<Parser> wparser;
unique_ptr<Codegenerator> wcodegenerator;
unique_ptr<Optimizer> woptimizer;
};

// 구상 팩토리 클래스
// Linux os에 맞는 컴파일러 관련 기능들의 덩어리 구현
class LinuxCompiler: public CompilerFactory
{
public:

};


.....


// Abstract factory pattern
cout << endl << "Abstract factory pattern: Windows compiler creation" << endl;
// 구상 클래스를 규정하는 일 없이, 상호의존적이거나 관련있는 객체들의 그룹(덩어리)
// 을 생성하는 인터페이스를 제공
// [예제] Windows 타입인지 Linux타입인지만 정해서 객체 생성만 해주면 됨
// 컴파일러의 세부 기능들(OS타입에 따른)은 추상팩토리를 상속받은 구상팩토리에서 결정

unique_ptr<CompilerFactory> wCompiler(new WindowsCompiler); // 팩토리 객체 생성
Scanner *wscanner = wCompiler->CreateScanner(); // product 생성
Parser *wparser = wCompiler->CreateParser();
Codegenerator *wcodegenerator = wCompiler->CreateCodegenerator();
Optimizer *woptimizer = wCompiler->CreateOptimizer();


....





Factory method
-product를 직접 생성하지 않고, creator에 의해 생성한다.
  product *pt = new product; // (X)



-두 factory패턴은 구조가 비슷하다.
-구상 팩토리에서 product를 생성하고 리턴한다는 것은 동일
-템플릿메서드 패턴을 활용한 객체 생성패턴
-abstract factory는 유사클래스의 덩어리를 생성




// Factory method pattern for document creation
// Coded by FunMV
class Docu // 생성할 객체(product)의 추상클래스
{
public:
virtual void Print()=0;
};
class HwpDocu: public Docu
{
public:
void Print()
{
cout<<"Hangul~"<<endl;
}
};
class DocDocu: public Docu
{
public:
void Print()
{
cout<<"Docx~"<<endl;
}
};

// 생성해 주는 Factory 클래스
// product(doc)를 직접 생성하지 않고
// Interface를 통해 생성
class InterfaceDocu
{
public:
Docu* CreatProduct()
{
return CreateFactory();
}

private:
virtual Docu* CreateFactory() = 0;
unique_ptr<Docu> pDoc;
};

template <typename T> // 생성 객체 타입 결정은 구성 클래스에서 함
class InterfaceTemplate: public InterfaceDocu
{
public:
Docu* CreateFactory()
{
pd.reset(new T);
return pd.get();
}
private:
unique_ptr<T> pd;
};

.....

// Factory method pattern
cout << endl << "Factory method pattern as object creation" << endl;
// 객체 생성은 추상클래스에서, 타입결정은 구상클래스에서 수행
// 만일 이렇게 하지 않으면 생성할 객체의 타입이 추가될 때마다
// 중복된 코드를 가지는 구상클래스들을 만들어야 함

// Factory method by using template
cout << endl << "Factory method by template" << endl;
unique_ptr<InterfaceDocu> hwpDoc(new InterfaceTemplate<HwpDocu>); //팩토리 객체 생성
unique_ptr<InterfaceDocu> docDoc(new InterfaceTemplate<DocDocu>);
Docu *phwp = hwpDoc->CreatProduct(); //product 생성
Docu *pdoc = docDoc->CreatProduct();
phwp->Print();
pdoc->Print();

.....

Factory method by template
Hangul~
Docx~





Prototype 
-그래픽 편집기에서 각 그래픽 요소들이 객체로 미리 생성되어져 있고, 사용자가 특정 그래픽 요소를 선택하면 해당객체는 복제됨.



- 즉, 상기한 구조가 객체 복제를 사용한 구조로 바뀜



 Facade 
-client가 서브시스템의 클래스들을 직접 사용하는 것을 막지는 않는다.
-Facade 객체는 하나만 존재하면 될 경우가 많으므로, 주로 singleton 패턴 형태로 구현하는 경우가 많다.

-C++에서는 namespace와 유사
namespace
{
    class Y
   {
        static int a;
    public:
        void f();
   };
   class Z;
   void foo();
}

int X::Y::a=0;
class X::Z
{
    int u, v, w;
public:
   Z(int b);
   int g();
};



Builder
-RTF 문서를 읽는다고 하였을 때, 보통 RTF 문서를 parsing하는 알고리즘은 변경될 필요가 없다.  



Bridge(=handle/body)
-client에 구현을 숨기거나 lib의 version upgrade를 위해 사용 시에 유용하다.
-아래 그림에서 왼쪽은 os에 독립적으로 icon, menu등을 그릴수 있다.
-또한 오른쪽을 lib upgrade로 보면 upgrade와 독립적으로 그릴 수 있다. 이것은 lib갱신을 독립적으로  할 수 있음을 의미.



Command
(1) command 생성(실제 실행할 명령(receiver)을 인자로 넘겨줌). callback함수의 역할
(2) 생성된 command 저장(callback함수를 저장하는 역할)
     Invoker는 callback함수(등록함수)를 저장하고 호출해줌
(3) Invoker에서 어떤 event(예, 마우스 클릭)가 들어오면 해당 callback함수(command)가 실행
     command의 Excute()함수 실행
(4) Excute에 의해 receiver의 action실행




State
상태기계(state machine)를 설계할 때 사용
context class 내에 정의된 것들:
     상태 객체
     초기 상태
     상태를 지정할 수 있는 함수 정의:   void setState(State *);
     user가 발생시키는 행위에 대응하는 함수(이 함수가 상태의 handle()를 호출)

State클래스는 인터페이스 역할
     가능한 여러 상태가 구상 클래스로 하위에 정의됨
     가능한 여러 handle 함수의 인터페이스를 정의
     구상 상태 클래스에서 실제 handle함수를 정의 함
               handle함수 내에서 상태 변경이 발생
              (context의 setState를 호출하고, 인자로는 다음 상태를 넘겨줌)

 




// State pattern
// 알갱이(candy) 뽑기 기계
// Coded by FunMV
//=======================================
// Current state    Event    Next state   
//---------------------------------------
//     empty      refillCandy    noCoin    
//     noCoin    insertCoin    coin
//     noCoin    ejectCoin     noCoin
//     coin        ejectCoin     noCoin
//     coin        turnCrank     ready
//     coin        insertCoin     coin
//     ready       outCandy     empty (candy 수=0)
//     ready       outCandy     noCoin (candy 수>0)
//========================================
class GumballMachine;
class State // 상태노드를 나타내는 인터페이스
{           // 상태노드는 Action을 내부 함수로 가져야 함
public:
virtual void insertCoin() = 0; //동전 삽입
virtual void ejectCoin() = 0; // 동전 배출
virtual void turnCrank() = 0; // 레버 회전
virtual void refillCandy(int candy) = 0; // candy 리필
virtual void outCandy() = 0; // candy 배출
virtual void printState() = 0; // 상태 출력
};


// 상태 1: 캔디 Ready
// 각 상태에서는 외부로 나가는(상태를 바꾸는) Action에 대해서만 구현 필요
// Candy ready 상태에서는 outCandy()이 상태를 바꾸는 Action이다.
class readyState: public State
{
public:
readyState(GumballMachine *gMachine): gbMachine(gMachine)
{}
void insertCoin() {}
void ejectCoin() {}
void turnCrank() {}
void  refillCandy(int candy) {}
void outCandy(); // state를 변경시키는 action
void printState() { cout<< "현재 상태: readyState" <<endl << endl; }
private:
// 각 상태는 context객체의 pointer를 가지고 있어야 함
GumballMachine *gbMachine;
};

// 상태 2: 캔디 매진
class emptyState: public State
{
public:
emptyState(GumballMachine *gMachine): gbMachine(gMachine)
{}
void insertCoin() {}
void ejectCoin() {}
void turnCrank() {}
void refillCandy(int candy); // state를 변경시키는 action
void outCandy() {}
void printState() { cout<< "현재 상태: emptyState" <<endl<<endl; }

private:
GumballMachine *gbMachine;
};


// 상태 3: 동전 투입
class coinState: public State
{
public:

coinState(GumballMachine *gMachine): gbMachine(gMachine), countCoin(0)
{}
void insertCoin() { countCoin++; cout << "동전 삽입: " << countCoin << endl; }
void ejectCoin();
void turnCrank(); // state를 변경시키는 action
void refillCandy(int candy) {}
void outCandy() {}
void printState() { cout<< "현재 상태: coinState" <<endl<<endl; }
private:
GumballMachine *gbMachine;
int countCoin;
};

// 상태 4: 동전 부족
class noCoinState: public State
{
public:
noCoinState(GumballMachine *gMachine): gbMachine(gMachine)
{}
void insertCoin(); // state를 변경시키는 action
void ejectCoin() { cout << "동전 없음" << endl; }
void turnCrank() { cout << "동전 없음" << endl; }
void refillCandy(int candy) {}
void outCandy() {}
void printState() { cout<< "현재 상태: noCoinState" <<endl<<endl; }

private:
GumballMachine *gbMachine;
};




// 뽑기 기계 class로 내부에 정의되어야 하는 것들:
// (1) 이 기계에서 사용하는 Actions 
// (2) 현재 state를 저장하기 위한 pointer
// (3) 존재하는 state 객체 모두 생성
// (4) 현재 기계가 가진 candy의 갯수
class GumballMachine
{
public:
GumballMachine(int candy): _candy(candy), _ready(new readyState(this))
{
_noCoin.reset(new noCoinState(this));
_coin.reset(new  coinState(this));
_empty.reset(new emptyState(this));
//_ready.reset(new readyState(this));

currentState = _noCoin.get(); // 초기 상태는 no_coin
}
void setState(unique_ptr<State> &inState)
{
currentState = inState.get();
}

// Action 1: 동전 삽입
void insertCoin()
{
currentState->insertCoin();
}

// Action 2: 동전 반환
void ejectCoin()
{
currentState->ejectCoin();
}

// Action 3: 캔디 충전
void refillCandy(int candy)
{
currentState->refillCandy(candy);
}

// Action 4: 레버 회전
void turnCrank()
{
currentState->turnCrank();
}

// Action 5: 캔디 배출
void outCandy()
{
currentState->outCandy();
}

// 현재 상태 출력
void printState()
{
currentState->printState();
}

// 상태 객체 Get 함수
unique_ptr<State>& getNoCoin() { return _noCoin; }
unique_ptr<State>& getCoin() { return _coin; }
unique_ptr<State>& getEmpty() { return _empty; }
unique_ptr<State>& getReady() { return _ready; }

        // 기타 Get 함수
int getNumCandy() { return _candy; }
void setNumCandy(int candy) { _candy = candy; }
private:
unique_ptr<State> _noCoin; // 가능한 상태 객체 선언
unique_ptr<State> _coin;
unique_ptr<State> _empty;
unique_ptr<State> _ready;

State* currentState;
int _candy;
};




MVC
Model, View, 그리고 Controller 패턴(모형)으로 UI 설계에 많이 사용된다.


UI(User Interface)에서 사용자가 발생시키는 event는 controller(전략 패턴 사용)를 통해 model의 변수(parameters)를 변경시키고, model은 notify(observer 패턴)에 의해 view를 갱신한다.

.Controller는 view의 전략 객체로 이것을 바꾸면 view의 행동이 바뀜
.View(특히 GUI에서)는 여러 단계로 겹쳐져 있는 window, panel, button, text, label 등으로 구성된다. 각 출력 항목은 복합 객체(window 등) 또는 Leaf(버턴 등)이 될 수 있다.  화면 갱신 시에는 최상위 구성 요소한테만 화면을 갱신하라고 요청한다.  나머지는 composite 패턴에 의해 자동으로 처리된다.

따라서 MVC 모델은 내부에 3가지 패턴을 적용하는데, observer, strategy, composite이다.




Composite
추상클래스를 하나 만들고 상속받은 다양한 자식클래스를 만들어 다양한 자식클래스들을 동일 클래스 다루듯 사용하는 패턴이다.

컴포지트 패턴이란 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다.

#include <iostream>
#include <vector>
#include <string>
 
using std::cout;
using std::vector;
using std::string;
 
class Component
{
  public:
   virtual void list() const = 0;
   virtual ~Component(){};
};
 
class Leaf : public Component 
{
  public:
   explicit Leaf(int val) : value_(val) 
   {
   }
   void list() const 
   {
      cout << "   " << value_ << "\n";
   }
  private:
   int value_;
};
 
class Composite : public Component 
{
  public:
   explicit Composite(string id) : id_(id) 
   {
   }
   void add(Component *obj)
   {
      table_.push_back(obj);
   }
   void list() const
   {
      cout << id_ << ":" << "\n";
      for (vector<Component*>::const_iterator it = table_.begin(); 
       it != table_.end(); ++it)
      {
         (*it)->list();
      }
   }
  private:
   vector <Component*> table_;
   string id_;
};
 
int main()
{
   Leaf num0(0);
   Leaf num1(1);
   Leaf num2(2);
   Leaf num3(3);
   Leaf num4(4);
   Composite container1("Container 1");
   Composite container2("Container 2");
 
   container1.add(&num0);
   container1.add(&num1);
 
   container2.add(&num2);
   container2.add(&num3);
   container2.add(&num4);
 
   container1.add(&container2);
   container1.list();
   return 0;
}

Leaf이나 Composite모두 Component에서 상속 받아 만든 동일 구상 클래스이다. 
container1은 2개의 Leaf노드를 가진다.
container2는 3개의 Leaf노드를 가진다.

container1은 container2도 가지므로 container1 밑에 2개의 Leaf와 3개의 Leaf를 가진 container2를 가진 Tree구조가 형성된다.

Composite클래스의 list명령으로 Tree를 모두 출력해 볼 수 있다.






Reference
장세찬, GoF 디자인 패턴 활용, 한빛 미디어
서완수 역, Head first Design Patterns, O'Reilly



댓글 없음:

댓글 쓰기