class GameCharecter { public: virtual int getHP() const; ... };
1. 비가상 함수 인터페이스 (NVI : non-virtual interface) 관용구
이 관용구에 쓰인 가상함수를 포함하는 비가상 함수를 가상 함수의 랩퍼(wrapper)라고 부른다.
가상 함수가 호출되기 전에 어떤 상태를 구성하고, 호출된 후에 어떤 상태를 없애는 작업을 랩퍼를 통해 공간적으로 보장된다.
ex) Mutex Lock/Unlock, Log 정보, 각종 유효성 Check
class GameCharecter { public: virtual int getHP() const { ... // 가상함수 호출전 작업 int retVal = calcHP(); ... // 가상함수 호출후 작업 return retVal; } private: virtual int calcHP() const { ... } };
2. 함수 포인터로 구현한 전략(Strategy) 패턴
단, 계산 함수는 이제 클래스의 멤버 함수가 아니라서 public 멤버가 아닌 부분은 건드릴 수가 없다.
class GameCharacter; int defaultCalcHP(const GameCharacter& gc); class GameCharacter { public: typedef int (*calcHP)(const GameCharacter&); explicit GameCharacter(calcHP chp = defaultCalcHP) : pCalcHP(chp) {} int getHP() const { return pCalcHp(*this); } private: calcHP pCalcHP; }; class EvilGuy : public GameCharacter { ... }; int loseHealthQuickly(const GameCharacter&); int loseHealthSlowly(const GameCharacter&); EvilGuy e1(loseHealthQuickly); EvilGuy e2(loseHealthSlowly);
3. tr1::function으로 구현한 전략 패턴
tr1::function 계열의 객체는 함수호출성 개체(callable entity)를 가질 수 있고, 이들 개체는 주어진 시점에서 예상되는 시그너처와 호환되는 시그너처를 갖고 있다.
class GameCharacter; int defaultCalcHP(const GameCharacter& gc); class GameCharacter { public: typedef std::tr1::function<int (const GameCharacter&)> calcHP; explicit GameCharacter(calcHP chp = defaultCalcHP) : pCalcHP(chp) {} int getHP() const { return pCalcHp(*this); } private: calcHP pCalcHP; }; short calcHealth(const GameCharacter&); // return이 short ? struct HealthCalc { int operator() (const GameCharacter&) const { ... } }; class GameLevel { public: float health(const GameCharacter&) const; // 어라 ? 이건 float ? }; class EvilGuy : public GameCharacter { ... }; class EvilCandyGuy : public GameCharacter { ... }; EvilGuy e1(calcHealth); EvilCandyGuy ec(HealthCalc()); GameLevel cl; EvilGuy e2(std::tr1::bind(&GameLevel::health, cl, _1));
4. 고전적인 전략 패턴
GameCharacter 와 HealthCalcFunc 의 2개의 Class를 두고 이 Class들을 상속받아서 하위 Class들만 만든다. 그리고 GameCharacter 타입의 모든 객체는 HealthCalcFunc 타입의 객체에 대한 포인터를 포함하도록 설계한다.
class GameCharacter; class calcHP { public: virtual int calc(const GameCharacter& gc) const { ... } }; calcHP defaultCalcHP; class GameCharacter { public: explicit GameCharacter(calcHP chp = defaultCalcHP) : pCalcHP(chp) {} int getHP() const { return pCalcHp->calc(*this); } private: calcHP* pCalcHP; };
* 가상 함수 대신에 쓸 수 있는 다른 방법으로 NVI 관용구 및 전략 패턴을 들 수 있습니다. 이 중 NVI 관용구는 그 자체가 템플릿 메서드 패턴의 한 예입니다.
* 객체에 필요한 기능을 멤버 함수로부터 클래스 외부 비멤버 함수로 옮기면, 그 비멤버 함수는 그 클래스의 public 멤버가 아닌 것들을 접근할 수 없다는 단점이 생깁니다.
* tr1::function 객체는 일반화된 함수 포인터처럼 동작합니다. 이 객체는 주어진 대상 시그너처와 호환되는 모든 함수호출성 개체를 지원합니다.
댓글 없음:
댓글 쓰기