2010年5月9日日曜日

C++でStrategyパターン

Strategyパターンはアルゴリズム(振る舞い)をカプセル化(クラス化)するパターンです。
Singletonパターンのメリットは
  • 振る舞いを簡単に交換することが出来る。
  • 振る舞いを動的に交換することが出来る。
です。
まずは次のクラス図をご覧ください

見ました?なんか複雑だからってあんまり見てないでしょ?
ちゃんとクリックして拡大して見てくださいね。
このクラス図で重要なのは
  • MonsterStrategyクラス
ですMonsterのmove()の振る舞いをMonsterStrategyクラスに委譲しています。
つぎにソースを見てみましょう、用意するのは次のファイルです。

  • main.cpp
  • Monster.h
  • Monster.cpp
  • MonsterStrategy.h
  • MonsterStrategy.cpp
  • MonsterStrategyFreedom.h
  • MonsterStrategyFreedom.cpp
  • MonsterStrategyGangan.h
  • MonsterStrategyGangan.cpp
まずはMonster.hです。
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include "MonsterStrategy.h"

class Monster
{
    //----------------------------------
    // constant
    //----------------------------------
    #define STRATEGY_MAX_NUM 8

    //----------------------------------
    // field
    //----------------------------------
private:
    MonsterStrategy* m_strategyDictionary[STRATEGY_MAX_NUM];    //  ①
    MonsterStrategy::Type m_currentStrategyType;                //  ②

    //----------------------------------
    // property
    //----------------------------------
public:
    const MonsterStrategy::Type getCurrentStrategyType();
    Monster& setCurrentStrategyType(const MonsterStrategy::Type _type);

private:
    MonsterStrategy** getStrategyDictionary();
    const bool addStrategy(const MonsterStrategy::Type _type);

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    Monster();

    // destructor
    ~Monster();

    // initialize
    const bool init();

    // move
    Monster& move();    //  ③

private:
    // operator(=)
    Monster& operator=(const Monster& _monster);
};
#endif
①Monsterの行動アルゴリズム辞書です。今回は配列を使用していますがDictionary系クラスを私用するのが良いでしょう。
②現在の行動アルゴリズムです。
③Monsterに行動指示を出します。

次にMonster.cppです。
#include "Monster.h"
#include "MonsterStrategyFreedom.h"
#include "MonsterStrategyGangan.h"
#include 

//-----------------------------------
// property
//-----------------------------------
const MonsterStrategy::Type Monster::getCurrentStrategyType()
{
    return this->m_currentStrategyType;
}
Monster& Monster::setCurrentStrategyType(const MonsterStrategy::Type _type)
{
    printf("strategy %d -> %d\n", this->getCurrentStrategyType(), _type);
    this->m_currentStrategyType = _type;
    return *this;
}
MonsterStrategy** Monster::getStrategyDictionary()
{
    return this->m_strategyDictionary;
}
const bool Monster::addStrategy(const MonsterStrategy::Type _type)
{
    if (STRATEGY_MAX_NUM <= _type) {
        throw "error strategy max num";
    }
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    if (strategyDictionary[_type] != NULL) {
        // already exist
        return false;
    }

    switch (_type) {
        case MonsterStrategy::ST_FREEDOM:
            strategyDictionary[_type] = new MonsterStrategyFreedom();
            break;
        case MonsterStrategy::ST_GANGAN:
            strategyDictionary[_type] = new MonsterStrategyGangan();
            break;
        default:
            throw "invalid type";
            break;
    }

    return true;
}

//-----------------------------------
// method
//-----------------------------------
// constructor
Monster::Monster() :
    m_currentStrategyType(MonsterStrategy::ST_FREEDOM)
{
    // do nothing...
}
// destructor
Monster::~Monster()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    for (int i = 0; i < STRATEGY_MAX_NUM; i++) {
        delete strategyDictionary[i];
    }

}

// initialize
const bool Monster::init()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    for (int i = 0; i < STRATEGY_MAX_NUM; i++) {
        strategyDictionary[i] = NULL;
    }

    const MonsterStrategy::Type strategyTypeArray[] = {
        MonsterStrategy::ST_FREEDOM,
        MonsterStrategy::ST_GANGAN,
    };
    const int size = sizeof(strategyTypeArray) / sizeof(MonsterStrategy::Type); //  ①
    for (int i = 0; i < size; i++) {
        this->addStrategy(strategyTypeArray[i]);
    }

    return true;
}

// move
Monster& Monster::move()
{
    MonsterStrategy** strategyDictionary = this->getStrategyDictionary();
    strategyDictionary[this->getCurrentStrategyType()]->move(*this);
    return *this;
}
①配列のサイズを求めています。

次にMonsterStrategy.hです。
#ifndef _MONSTER_STRATEGY_H_
#define _MONSTER_STRATEGY_H_

class Monster;

class MonsterStrategy
{
public:
    //----------------------------------
    // constant
    //----------------------------------
    enum Type {
        ST_FREEDOM = 0, // 自由
        ST_GANGAN,  // がんがん行こうぜ
    };  //  ①

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategy();

    // destructor
    virtual ~MonsterStrategy();

    // move
    virtual void move(Monster& _monster) = 0;   //  ②

private:
    // operator(=)
    MonsterStrategy& operator=(const MonsterStrategy& _monsterStrategy);

};
#endif
①Monsterの行動アルゴリズムのタイプです。
②Monsterの行動アルゴリズムが記述されているメソッドです。タイプ毎にアルゴリズムは違うので純粋仮想関数にしています。

次にMonsterStrategy.cppです。
#include "MonsterStrategy.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategy::MonsterStrategy()
{
    // do nothing...
}

// destructor
MonsterStrategy::~MonsterStrategy()
{
    // do nothing...
}
特に何も実装はなしです。
次に
MonsterStrategyFreedom.h
MonsterStrategyFreedom.cpp
MonsterStrategyGangan.h
MonsterStrategyGangan.cpp
をまとめて記述します。
#ifndef _MONSTER_STRATEGY_FREEDOM_H_
#define _MONSTER_STRATEGY_FREEDOM_H_

#include "MonsterStrategy.h"

class Monster;

class MonsterStrategyFreedom : public MonsterStrategy // ①
{
public:

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategyFreedom();

    // destructor
    ~MonsterStrategyFreedom();

    // move
    void move(Monster& _monster);   //  ②

private:
    // operator(=)
    MonsterStrategyFreedom& operator=(const MonsterStrategyFreedom& _monsterStrategyFreedom);

};
#endif
#include "MonsterStrategyFreedom.h"
#include "MonsterStrategy.h"
#include "Monster.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategyFreedom::MonsterStrategyFreedom()
{
    // do nothing...
}

// destructor
MonsterStrategyFreedom::~MonsterStrategyFreedom()
{
    // do nothing...
}

// move
void MonsterStrategyFreedom::move(Monster& _monster)
{
    printf("freedom\n");
}
#ifndef _MONSTER_STRATEGY_GANGAN_H_
#define _MONSTER_STRATEGY_GANGAN_H_

#include "MonsterStrategy.h"

class Monster;

class MonsterStrategyGangan : public MonsterStrategy    //  ①
{
public:

    //----------------------------------
    // mothod
    //----------------------------------
public:
    // constructor
    MonsterStrategyGangan();

    // destructor
    ~MonsterStrategyGangan();

    // move
    void move(Monster& _monster);   //  ②

private:
    // operator(=)
    MonsterStrategyGangan& operator=(const MonsterStrategyGangan& _monsterStrategyGangan);

};
#endif
#include "MonsterStrategyGangan.h"
#include "MonsterStrategy.h"
#include "Monster.h"
#include 

//----------------------------------
// mothod
//----------------------------------
// constructor
MonsterStrategyGangan::MonsterStrategyGangan()
{
    // do nothing...
}

// destructor
MonsterStrategyGangan::~MonsterStrategyGangan()
{
    // do nothing...
}

// move
void MonsterStrategyGangan::move(Monster& _monster)
{
    printf("inochi\n");
}
①MonsterStrategyを継承しています。
②オーバーライドしたmove()メソッドです。

最後にmain.cppです。
#include "Monster.h"
#include 

void main(void)
{
    Monster monster;    //  モンスター生成
    monster.init();     //  初期化
    monster.move();     //  move
    monster.setCurrentStrategyType(MonsterStrategy::ST_GANGAN); //  戦略変更
    monster.move();     //  move

    printf("hit any key...");
    while(getchar() == EOF){}
}

以上でStrategyパターンの説明は終わりです。クラス数は少し多いですがそんなに難しいパターンではないはずです。
ソースコードはコピペで動かせるハズですので是非試してみてください。

0 件のコメント:

コメントを投稿