ラベル

blogger (2) C/C++ (23) CSS (1) DB (4) EXCEL (2) HTML (2) iphone (1) JavaScript (8) JScript (10) Lua (14) Objective-C (1) photoshop (1) PHP (10) Smarty (1) sqlite iphone (1) Tips (24) UML (1) VC++ (2) WSH (8) Xcode (2) コミュニケーション (2) コンパイルエラー (7) デザインパターン (4) テスト (3) マネージメント (1) 開発 (11) 心理学 (12) 生活 (2) 豆知識 (1) 遊び (1)
ラベル デザインパターン の投稿を表示しています。 すべての投稿を表示
ラベル デザインパターン の投稿を表示しています。 すべての投稿を表示

2010年7月9日金曜日

C++プログラマがLuaを勉強してみる第10回

◆Luaでテンプレートメソッドパターン
今回はLua初心者のくせにテンプレートメソッドパターンに挑戦してしまいました。
テンプレートメソッドパターンについてはここ参照
無理矢理感が否めないので、ぜんぜんちっげーよ!ってコメント待ってます!

まずは、簡単にクラス図の説明を書きます。
なんかどっかでみたよーな命名ですが気にしない気にしない
   CaffeineBeverageクラス(Superクラス)
               △
                |
   ----------------
   |                        |
Coffeeクラス      Teaクラス


以下ソースコード
CaffeineBeverage.lua
----------------------------------------
--CaffeineBeverageクラス
----------------------------------------
CaffeineBeverage = {}

-- new
function CaffeineBeverage:new(_caffeineBeverage)
 local obj = {}
 -- field
 if _caffeineBeverage then
  obj.name = _caffeineBeverage.name
 else
  obj.name = ""
 end

 self.__index = self
 setmetatable(obj, self)
 return obj
end

-- property
function CaffeineBeverage:set(_name)
 self.name = _name
end


-- __tostring over ride
function CaffeineBeverage:__tostring()
 return "name:"..self.name
end

-- clone
function CaffeineBeverage:clone()
 return CaffeineBeverage:new(self)
end

-- prepareRecipe
function CaffeineBeverage:prepareRecipe()
 CaffeineBeverage:boilWater()
 CaffeineBeverage:brew()
 CaffeineBeverage:pourInCup()
 CaffeineBeverage:addCondiments()
end

-- boilWater
function CaffeineBeverage:boilWater()
 print("お湯を沸かす。。。")
end

-- brew()
function CaffeineBeverage:brew()
 error("have to override")
end

-- pourInCup
function CaffeineBeverage:pourInCup()
 print("カップに注ぐ。。。")
end

-- addCondiments
function CaffeineBeverage:addCondiments()
 error("have to override")
end


Coffee.lua
dofile("CaffeineBeverage.lua") -- C/C++で言うところの#include

----------------------------------------
--Coffeeクラス
----------------------------------------
Coffee = {}

-- new
function Coffee:new(_coffee)
  obj = {}
 -- field
 if _coffee then
  obj = CaffeineBeverage:new(_coffee)
 else
  obj = CaffeineBeverage:new()
 end

 -- clone (override)
 obj.__index.brew = function()
  return Coffee:new(self)
 end

 -- brew (overrice)
 obj.__index.brew = function()
  print("コーヒーをドリップする")
 end

 -- addCondiments (override)
 obj.__index.addCondiments = function()
  print("砂糖とクリープを加える")
 end

 return obj
end


Tea.lua
dofile("CaffeineBeverage.lua")
----------------------------------------
--Teaクラス
----------------------------------------
Tea = {}

-- new
function Tea:new(_tea)
  obj = {}
 -- field
 if _tea then
  obj = CaffeineBeverage:new(_tea)
 else
  obj = CaffeineBeverage:new()
 end

 -- clone
 obj.__index.brew = function()
  return Tea:new(self)
 end

 -- brew
 obj.__index.brew = function()
  print("ティーバッグをお湯に浸す")
 end

 -- addCondiments
 obj.__index.addCondiments = function()
  print("砂糖とレモン")
 end

 return obj
end

main.lua
dofile("Coffee.lua")
dofile("Tea.lua")

----------------------------------------
-- main
----------------------------------------
function main()
 print("--コーヒーの生成")
 a = Coffee:new()
 a:set("コーヒー")
 print(a)
 a:prepareRecipe()

 print("\n")

 print("--紅茶の生成")
 b = Tea:new()
 b:set("紅茶")
 print(b)

 b:prepareRecipe()

 print("\n")

 print("--コピーコンストラクタの使用")
 c = Tea:new(b)
 print(c)
 c:set("レモンティ")
 print(c)
 print(b)

 print("\n")

 print("クローン")
 d = c:clone()
 print(d)
 d:set("レモンの紅茶")
 print(d)
 print(c)
end

-- call main
-- try catchみたいなの使ってます。
-- error()ってメソッドがthrowに相当します。
local status, err = pcall(main)
if status then
 -- do nothing
else
 print(err)
end

2010年5月26日水曜日

C++でTemplateMethodパターン

TemplateMethodパターンはスーパークラス(ベースクラス)でロジックの概要設計あるいは共通部分を実装し、詳細ロジックあるいは非共通部分をサブクラスで提供するパターンです。
大げさに言うとスーパークラスでプチフレームワークを実装するようなイメージです。
TemplateMethodパターンのメリットは
  • サブクラスで共通なロジックをスーパークラスで共通化できる。
  • ロジックの骨組みが明確になっている為、コードが把握し安い且つメンテナンスコストが軽減されやすい。
などです。
それではクラス図をご覧ください。
ちょっとメソッドとか多いですけど簡単な継承関係です。
このクラス図で重要なのは
  • move()
  • canMove()
  • canMoveEach()
  • moveEach()
で、Characterクラスのmove()がテンプレートメソッドに相当します。
次にソースを見てみましょう、用意するのは次のファイルです今回からクラスのcppファイルを用意しない方が見やすいかなと思って.hだけ用意しました。
  • main.cpp
  • Character.h
  • Monster.h
Character.h
#ifndef _CHARACTER_H_
#define _CHARACTER_H_

#include 

class Character
{
    //----------------------------------
    //  field
    //----------------------------------
private:
    int HP;
    int MP;

    //----------------------------------
    //  property
    //----------------------------------
public:
    const int getHP() {
        return this->HP;
    }
    Character& setHP(const int _hp) {
        this->HP = _hp;
        return *this;
    }
    const int getMP() {
        return this->MP;
    }
    Character& setMP(const int _mp) {
        this->MP = _mp;
        return *this;
    }

    //----------------------------------
    //  method
    //----------------------------------
public:
    Character() :
        HP(100),
        MP(100)
    {
        //  do nothing...
    }

    virtual ~Character() {
        //  do nothing...
    }

    void move() {   //  テンプレートメソッド
        //  move可能判定
        if (this->canMove()) {
            //  move
            this->moveEach();
        }
    }
private:
    virtual const bool canMove() {
        //  HPが0ならmove不可
        if (this->getHP() <= 0) {
            return false;
        }
        return this->canMoveEach();
    }

protected:
    virtual const bool canMoveEach() = 0;
    virtual void moveEach() = 0;
};
#endif
move()のなかでmove可能ならばmoveするという大まかなロジックを実装しています。

Monster.h
#ifndef _MONSTER_H_
#define _MONSTER_H_

#include "Character.h"

class Monster : public Character
{
 //----------------------------------
 // method
 //----------------------------------
protected:
 virtual ~Monster() {
  // do nothing...
 }
 virtual const bool canMoveEach() {
  printf("canMoveEach\n");
  return true;
 }

 virtual void moveEach() {
  printf("moveEach\n");
 }
};
#endif
Monster独自のcanMove()、move()をcanMoveEach()、moveEach()で実装しています。Eachっていう名前の付け方は一般的ではない気がします。

main.cpp
#include 
#include "Monster.h"

void main(void)
{
    //  Monster生成
    Character* character = new Monster();

    //  move
    character->move();

    //  HPに0を設定
    character->setHP(0);

    //  move
    character->move();

    delete character;
    printf("hit any key...\n");
    while(getchar() == EOF) {}
}

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

2010年4月25日日曜日

C++でSingletonパターン

Singletonパターンはクラスのインスタンスが一つしかないことを保証するパターンです。
Singletonパターンのメリットは
  • 不要なインスタンスの生成を禁止することができる。
です。
まずは次のクラス図をご覧ください

このクラス図で重要なのは
  • getInstance()メソッド(static Singleton& getInstance())
  • privateなコンストラクタ(Singleton)
の二つですこれらによりsingletonを実現します。
つぎにソースを見てみましょう、用意するのは次のファイルです。
  • main.cpp
  • Singleton.h
  • Singleton.cpp
ますはSingleton.hです。
#ifndef _SINGLETON_H_
#define _SINGLETON_H_

class Singleton
{
public:

// get instance
static Singleton& getInstance(); // ①

// to string
const char*const toString(); // ②

// destructor
~Singleton(); // ③

private:
// constructor
Singleton(); // ④
Singleton(const Singleton& _singleton); // ⑤

// initialize
const bool init(); // ⑥

// operator(=)
Singleton& operator=(const Singleton& _singleton); // ⑦

};
#endif

①唯一のインスタンスを取得するためのインタフェースです。
②文字列化のインタフェースです。Singletonパターンとは関係有りません。
③デストラクタです。
④⑤インスタンスの生成を禁止するためにコンストラクタをprivateで宣言しています。
⑥初期化メソッドですSingletonパターンとは関係ありません。
⑦operator=をprivateで宣言することによりインスタンスのコピーを禁止しています。Singletonパターンとは関係有りません。

次にSingleton.cppファイルを見てみます。
#include "Singleton.h"

Singleton& Singleton::getInstance()
{
static Singleton singleton; // ①

static bool isAlreadyInit = false; // ②
if (isAlreadyInit) {
// do nothing...
}
else {
isAlreadyInit = singleton.init();
}
return singleton;
}

// to string
const char*const Singleton::toString()
{
static char* str = "singleton";
return str;
}

// constructor
Singleton::Singleton()
{
// do nothing...
}

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

// initialize
const bool Singleton::init()
{
// do nothing...
return true;
}

①唯一のインスタンスをstaticで定義しています。
②初期化処理を最初の1回だけ行うために初期化済みフラグを定義して初期化したらtrueを代入します。
注:この実装はスレッドセーフではありません。

最後にmain.cppです。

#include "Singleton.h"
#include 

void main (void)
{
Singleton& apple = Singleton::getInstance();    // ①
printf("%s\n", apple.toString());       // ②
printf("hit any key...\n");             // ③
while(getchar() == EOF);                // ④
}

①getInstance()メソッドでインスタンスを取得しています。
②取得したインスタンスのtoString()メソッドを呼び出しています。
③"hit any key..."を標準出力に表示しています。
④いずれかのkeyが押されるまでループしています。

以上でSingletonパターンの説明は終わりです。ソースコードはコピペで動かせるハズですので是非試してみてください。