|
C++で読むデザインパターン(Design Patterns read by C++)
デザインパターンの本って、眠い本が多いですね。^^; 「そんな薀蓄はいいから、コードで書いて見せてよ」という人(私)のために C++ のコードでデザインパターンを紹介します。とはいえ、やまざきの頭で解釈したことをそのままコードに書き出していますので、パターンの解釈の仕方が間違っているかもしれません。よって、これらのコードを鵜呑みにせず、各自 GoF 本などと照らし合わせて利用することをお薦めします。
パターンは主にオブジェクト指向の「継承機能」を使って実装されますが、C++
のテンプレートやリファレンスを使うことで継承を使わなくても ある程度実装できるパターンがあります。その継承を使わないパターンを「非継承版」として掲載しています(個人的には継承という概念が嫌いなので^^;)。継承版と非継承版は同じ動作をします(実装方法が違う)のでコードを比べてみると良いと思います。まぁ、「非継承版」の方は
お遊びのコードというか「こう書いてもいいじゃん」という、お試しのサンプルコードなのであまり本気にしないで下さい。
#非継承版って、実はジェネリックなコード(型に依存しないコード)であることに最近気が付きました。結構すごいことかも。
全23パターンを C++ で書いてみて思ったことは「デザインパターン」とは「設計の定石」のことなんだと思いました。「ここはこんなパターンで構築できるね」とか、「ここのコードはxxxのパターンで書かれているね」という会話が出来るようになればデザインパターンの恩恵を受けてコードの生産性を上げられるのでしょう。
なにかおかしなコードを発見した場合は連絡を下さい。これらのコードがみなさんのお役にたてば嬉しく思います。
あ、あと、このページをそのまま本にしませんか? この原稿を本にしてくれる出版社さんも募集しています。
こういう、シンプルなコードって、とっても重要だと思うのです。いやホントに。連絡をお待ちしています。
英語とか韓国語、中国語での出版も希望しています。
よろしくお願いします。
| [構造] Adapter パターン |
|
| 共通インターフェイスを提供する。 |
#include <stdio.h> // puts()
// 既存のクラス(インターフェースがバラバラ)
class Apple
{
public:
void AppleName()
{
puts( "りんご" ) ;
}
void AppleColor()
{
puts( "赤" ) ;
}
} ;
class Banana
{
public:
void BananaName()
{
puts( "バナナ" ) ;
}
void BananaColor()
{
puts( "黄" ) ;
}
} ;
// Adapter パターン
// 共通インターフェースを作る
class AdapterPtn
{
public:
virtual void NamePuts() = 0 ;
virtual void ColorPuts() = 0 ;
} ;
// 共通インターフェースにあわせる
class Apple2 : public AdapterPtn
{
Apple m_apple ;
public:
virtual void NamePuts()
{
m_apple.AppleName() ;
}
virtual void ColorPuts()
{
m_apple.AppleColor() ;
}
} ;
class Banana2 : public AdapterPtn
{
Banana m_banana ;
public:
virtual void NamePuts()
{
m_banana.BananaName() ;
}
virtual void ColorPuts()
{
m_banana.BananaColor() ;
}
} ;
// 実装
// ポインタで受け取るのは好きじゃないんだけど
void sub( AdapterPtn * in )
{
// 共通インターフェースで呼べる
in->NamePuts() ;
in->ColorPuts() ;
}
int main()
{
Apple2 a2 ; sub( & a2 ) ;
Banana2 b2 ; sub( & b2 ) ;
return 0 ;
}
/* 実行結果
りんご
赤
バナナ
黄
Press any key to continue
*/ |
|
| [構造] Adapter パターン (非継承版) |
|
| 共通インターフェイスを提供する。 |
#include <stdio.h> // puts()
// 既存のクラス(インターフェースがバラバラ)
class Apple
{
public:
void AppleName()
{
puts( "りんご" ) ;
}
void AppleColor()
{
puts( "赤" ) ;
}
} ;
class Banana
{
public:
void BananaName()
{
puts( "バナナ" ) ;
}
void BananaColor()
{
puts( "黄" ) ;
}
} ;
// Adapter パターン
// template の場合は、共通インターフェースを作る必要は無い。
// 共通インターフェースにあわせる
class Apple2
{
Apple m_apple ;
public:
void NamePuts() // 共通インターフェース
{
m_apple.AppleName() ;
}
void ColorPuts() // 共通インターフェース
{
m_apple.AppleColor() ;
}
} ;
class Banana2
{
Banana m_banana ;
public:
void NamePuts() // 共通インターフェース
{
m_banana.BananaName() ;
}
void ColorPuts() // 共通インターフェース
{
m_banana.BananaColor() ;
}
} ;
// 実装
template< class _AdapterPtn >
void sub( _AdapterPtn & in )
{
// 共通インターフェースで呼べる
in.NamePuts() ;
in.ColorPuts() ;
}
int main()
{
Apple2 a2 ; sub( a2 ) ;
Banana2 b2 ; sub( b2 ) ;
return 0 ;
}
/* 実行結果
りんご
赤
バナナ
黄
Press any key to continue
*/ |
|
| [構造] Bridge パターン |
|
| インターフェイスを2つに分けて橋渡し。それぞれ個別に拡張可能にする。 |
#include <stdio.h> // printf()
// インターフェース(下部)
class BridgeLowPtn
{
public:
virtual void NamePuts() = 0 ; // 共通 Low インターフェース
} ;
// インターフェース(上部)
class BridgeHiPtn
{
public:
BridgeLowPtn * m_low ; // Low とつながっている
BridgeHiPtn( BridgeLowPtn * in ) // Low と結合
: m_low( in )
{}
virtual void Puts() = 0 ; // 共通 Hi インターフェース
} ;
// 実装
class Apple : public BridgeLowPtn
{
public:
virtual void NamePuts() // 共通 Low インターフェース
{
printf( "リンゴ" ) ;
}
};
class Banana : public BridgeLowPtn
{
public:
virtual void NamePuts() // 共通 Low インターフェース
{
printf( "バナナ" ) ;
}
};
class Hirosue : public BridgeHiPtn
{
public:
Hirosue( BridgeLowPtn * in )
: BridgeHiPtn( in )
{}
// Hi と Low のインターフェースを橋渡しする
virtual void Puts() // 共通 Hi インターフェース
{
m_low->NamePuts() ; // 共通 Low インターフェース
printf( "をください\n" ) ;
}
};
class Honjyo : public BridgeHiPtn
{
public:
Honjyo( BridgeLowPtn * in )
: BridgeHiPtn( in )
{}
// Hi と Low のインターフェースを橋渡しする
virtual void Puts() // 共通 Hi インターフェース
{
m_low->NamePuts() ; // 共通 Low インターフェース
printf( "がほしいねん\n" ) ;
}
};
void sub( BridgeLowPtn * in_fruit )
{
Hirosue ryoko( in_fruit ) ;
ryoko.Puts() ;
Honjyo manami( in_fruit ) ;
manami.Puts() ;
}
int main()
{
Apple apple ; sub( & apple ) ;
Banana banana ; sub( & banana ) ;
return 0 ;
}
/* 実行結果
リンゴをください
リンゴがほしいねん
バナナをください
バナナがほしいねん
Press any key to continue
*/ |
|
| [構造] Bridge パターン (非継承版) |
|
| インターフェイスを2つに分けて橋渡し。それぞれ個別に拡張可能にする。 |
#include <stdio.h> // printf()
// 実装
class Apple
{
public:
void NamePuts() // 共通 Low インターフェース
{
printf( "リンゴ" ) ;
}
};
class Banana
{
public:
void NamePuts() // 共通 Low インターフェース
{
printf( "バナナ" ) ;
}
};
template< class _BridgeLowPtn >
class Hirosue
{
_BridgeLowPtn & m_low ;
public:
Hirosue( _BridgeLowPtn & in )
: m_low( in )
{}
// Hi と Low のインターフェースを橋渡しする
void Puts() // 共通 Hi インターフェース
{
m_low.NamePuts() ; // 共通 Low インターフェース
printf( "をください\n" ) ;
}
} ;
template< class _BridgeLowPtn >
class Honjyo
{
_BridgeLowPtn & m_low ;
public:
Honjyo( _BridgeLowPtn & in )
: m_low( in )
{}
// Hi と Low のインターフェースを橋渡しする
void Puts() // 共通 Hi インターフェース
{
m_low.NamePuts() ; // 共通 Low インターフェース
printf( "がほしいねん\n" ) ;
}
};
template< class _BridgeLowPtn >
void sub( _BridgeLowPtn & in_fruit )
{
Hirosue< _BridgeLowPtn > ryoko( in_fruit ) ;
ryoko.Puts() ; // 共通 Hi インターフェース
Honjyo< _BridgeLowPtn > manami( in_fruit ) ;
manami.Puts() ; // 共通 Hi インターフェース
}
int main()
{
Apple apple ; sub( apple ) ;
Banana banana ; sub( banana ) ;
return 0 ;
}
/* 実行結果
リンゴをください
リンゴがほしいねん
バナナをください
バナナがほしいねん
Press any key to continue
*/ |
|
| [構造] Composite パターン |
|
入れ物(器)と入れるモノを同じインターフェースにする。
木構造などの再起構造を使いたいときに使うパターン。
「枝」と「葉」に分けた場合、枝には枝と葉の両方を追加できる。 |
#include <stdio.h> // puts()
#include <string> // STL std::string
// Compositeパターン
class CompositePtn
{
public:
std::string m_name ; // 名前
CompositePtn( const std::string & in_name )
: m_name( in_name )
{}
// 共通インターフェース
virtual void Puts( const std::string & in_parent ) = 0 ;
} ;
// 実装
// 葉
class Leaf : public CompositePtn
{
public:
Leaf( const std::string & in_name )
: CompositePtn( in_name )
{}
// 共通インターフェース
virtual void Puts( const std::string & in_parent )
{
std::string str = in_parent + "/" + m_name ;
puts( str.c_str() ) ;
}
} ;
// 葉(終端)
class Null : public CompositePtn
{
public:
Null()
: CompositePtn( "" )
{}
// 共通インターフェース
virtual void Puts( const std::string & in_parent )
{}
} ;
// 枝
class Tree : public CompositePtn
{
// std::vector<CompositePtn *> tbl ; でも可
CompositePtn * m_left ; // 左の枝
CompositePtn * m_right ; // 右の枝
public:
Tree(
const std::string & in_name,
CompositePtn * in_left,
CompositePtn * in_right
)
: CompositePtn( in_name )
, m_left( in_left )
, m_right( in_right )
{}
// 共通インターフェース
virtual void Puts( const std::string & in_parent )
{
std::string str = in_parent + "/" + m_name ;
puts( str.c_str() ) ;
// 枝をたどる
m_left->Puts( str ) ;
m_right->Puts( str ) ;
}
};
int main()
{
// honjyo には apple と banana
Leaf apple( "apple" ) ;
Leaf banana( "banana" ) ;
Tree honjyo( "honjyo", &apple, &banana ) ;
// hirosue には cacao だけ
Leaf cacao( "cacao" ) ;
Null null ;
Tree hirosue( "hirosue", &cacao, &null ) ;
// 木の根っこ
// 左の枝には honjyo 右の枝には hirosue
Tree root( "root", &honjyo, &hirosue ) ;
// 全部表示してみましょう。
root.Puts( "" ) ;
return 0 ;
}
/* 実行結果
/root
/root/honjyo
/root/honjyo/apple
/root/honjyo/banana
/root/hirosue
/root/hirosue/cacao
Press any key to continue
*/
|
|
| [構造] Composite パターン(非継承版) |
|
入れ物(器)と入れるモノを同じインターフェースにする。
木構造などの再起構造を使いたいときに使うパターン。
「枝」と「葉」に分けた場合、枝には枝と葉の両方を追加できる。 |
#include <stdio.h> // puts()
#include <string> // STL std::string
// Compositeパターン
// 実装
// 葉
class Leaf
{
const std::string m_name ;
public:
Leaf( const std::string in_name )
: m_name( in_name )
{}
// 共通インターフェース
void Puts( const std::string & in_parent )
{
std::string str = in_parent + "/" + m_name ;
puts( str.c_str() ) ;
}
} ;
// 葉(終端)
class Null
{
public:
Null(){}
// 共通インターフェース
void Puts( const std::string & in_parent )
{}
} ;
// 枝
template< class _Left, class _Right >
class Tree
{
const std::string m_name ;
_Left & m_left ; // 左の枝
_Right & m_right ; // 右の枝
public:
Tree(
const std::string in_name,
_Left & in_left,
_Right & in_right
)
: m_name( in_name )
, m_left( in_left )
, m_right( in_right )
{}
// 共通インターフェース
void Puts( const std::string & in_parent )
{
std::string str = in_parent + "/" + m_name ;
puts( str.c_str() ) ;
// 枝をたどる
m_left.Puts( str ) ;
m_right.Puts( str ) ;
}
};
int main()
{
// honjyo には apple と banana
Leaf apple( "apple" ) ;
Leaf banana( "banana" ) ;
Tree< Leaf, Leaf > honjyo( "honjyo", apple, banana ) ;
// hirosue には cacao だけ
Leaf cacao( "cacao" ) ;
Null null ;
Tree< Leaf, Null > hirosue( "hirosue", cacao, null ) ;
// 木の根っこ
// 左の枝には honjyo 右の枝には hirosue
// なんか、すでに保守できないくらい複雑になっている^^;
Tree< Tree< Leaf, Leaf >, Tree< Leaf, Null > >
root( "root", honjyo, hirosue ) ;
// 全部表示してみましょう。
root.Puts( "" ) ;
return 0 ;
}
/* 実行結果
/root
/root/honjyo
/root/honjyo/apple
/root/honjyo/banana
/root/hirosue
/root/hirosue/cacao
Press any key to continue
*/ |
|
| [構造] Decorator パターン |
|
| 同じインターフェースで拡張するイメージ。機能を付加(飾り付け)するパターン。 |
#include <stdio.h> // puts()
// Decoratorパターン
class DecoratorPtn
{
public:
virtual void Puts() = 0 ; // インターフェース
} ;
// 実装
class Apple : public DecoratorPtn
{
public:
virtual void Puts() // インターフェース
{
puts( "リンゴ" ) ;
}
} ;
class Banana : public DecoratorPtn
{
public:
virtual void Puts() // インターフェース
{
puts( "バナナ" ) ;
}
};
class Good : public DecoratorPtn
{
DecoratorPtn * m_p_decorator ;
public:
Good( DecoratorPtn* in )
: m_p_decorator( in )
{}
virtual void Puts() // 拡張インターフェース
{
printf( "おいしい" ) ;
m_p_decorator->Puts() ; // オリジナルを呼ぶ
}
} ;
void sub( DecoratorPtn* in )
{
in->Puts() ;
// 拡張その1
Good good( in ) ;
good.Puts() ;
// 拡張その2
Good verygood( & good ) ;
verygood.Puts() ;
}
int main()
{
Apple apple ; sub( & apple ) ;
Banana banana ; sub( & banana ) ;
return 0 ;
}
/* 実行結果
リンゴ
おいしいリンゴ
おいしいおいしいリンゴ
バナナ
おいしいバナナ
おいしいおいしいバナナ
Press any key to continue
*/ |
|
| [構造] Decorator パターン (非継承版) |
|
| 同じインターフェースで拡張するイメージ。機能を付加(飾り付け)するパターン。 |
#include <stdio.h> // puts()
// Decoratorパターン
// 実装
class Apple
{
public:
void Puts() // インターフェース
{
puts( "リンゴ" ) ;
}
} ;
class Banana
{
public:
void Puts() // インターフェース
{
puts( "バナナ" ) ;
}
};
template< class _DecoratorPtn >
class Good
{
_DecoratorPtn & m_decorator ;
public:
Good( _DecoratorPtn & in )
: m_decorator( in )
{}
void Puts() // 拡張インターフェース
{
printf( "おいしい" ) ;
m_decorator.Puts() ; // オリジナルを呼ぶ
}
} ;
template< class _ObjectPtn >
void sub( _ObjectPtn in )
{
in.Puts() ;
// 拡張その1
Good< _ObjectPtn > good( in ) ;
good.Puts() ;
// 拡張その2
Good< Good< _ObjectPtn > > verygood( good ) ;
verygood.Puts() ;
}
int main()
{
Apple apple ; sub( apple ) ;
Banana banana ; sub( banana ) ;
return 0 ;
}
/* 実行結果
リンゴ
おいしいリンゴ
おいしいおいしいリンゴ
バナナ
おいしいバナナ
おいしいおいしいバナナ
Press any key to continue
*/ |
|
| [構造] Facade パターン |
|
| 複雑な処理を簡単なインターフェースで提供する。 |
#include <stdio.h> // printf()
// もともとある実装(本当はもっと複雑なクラスだと思われ^^;)
class Apple
{
public:
int value_Get()
{
return 120 ;
}
} ;
class Banana
{
public:
int value_Get()
{
return 60 ;
}
} ;
// Facadeパターンだけど、パターンと言えるのかね。
class FacadePtn
{
public:
virtual void Shopping() = 0 ; // 簡単なメソッド
} ;
// 実装
class Hirosue : public FacadePtn
{
public:
virtual void Shopping()
{
// 複雑な処理?を隠す
Apple apple ;
Banana banana ;
int val = 0 ;
val += apple.value_Get() * 2 ; // リンゴ2個
val += banana.value_Get() * 3 ; // バナナ3個
printf( "%d円じゃん。\n", val ) ;
}
} ;
class Honjyo : public FacadePtn
{
public:
virtual void Shopping()
{
// 複雑な処理?を隠す
Banana banana ;
int val = banana.value_Get() * 50 ; // バナナ50個
printf( "%d円やねん。\n", val ) ;
}
} ;
void sub( FacadePtn * in )
{
in->Shopping() ;
}
int main()
{
Hirosue ryoko ; sub( & ryoko ) ;
Honjyo manami ; sub( & manami ) ;
return 0 ;
}
/* 実行結果
420円じゃん。
3000円やねん。
Press any key to continue
*/ |
|
| [構造] Facade パターン (非継承版) |
|
| 複雑な処理を簡単なインターフェースで提供する。 |
// Facade パターン (template版)
// 複雑な処理を簡単にする。
#include <stdio.h> // printf()
// もともとある実装(本当はもっと複雑なクラスだと思われ^^;)
class Apple
{
public:
int value_Get()
{
return 120 ;
}
} ;
class Banana
{
public:
int value_Get()
{
return 60 ;
}
} ;
// Facadeパターンだけど、パターンと言えるのかね。
// 実装
class Hirosue
{
public:
void Shopping()
{
// 複雑な処理?を隠す
Apple apple ;
Banana banana ;
int val = 0 ;
val += apple.value_Get() * 2 ; // リンゴ2個
val += banana.value_Get() * 3 ; // バナナ3個
printf( "%d円じゃん。\n", val ) ;
}
} ;
class Honjyo
{
public:
void Shopping()
{
// 複雑な処理?を隠す
Banana banana ;
int val = banana.value_Get() * 50 ; // バナナ50個
printf( "%d円やねん。\n", val ) ;
}
} ;
template< class _FacadePtn >
void sub( _FacadePtn & in )
{
in.Shopping() ;
}
int main()
{
Hirosue ryoko ; sub( ryoko ) ;
Honjyo manami ; sub( manami ) ;
return 0 ;
}
/* 実行結果
420円じゃん。
3000円やねん。
Press any key to continue
*/ |
|
| [構造] Flyweight パターン |
|
インスタンスを共有してメモリを軽くする。キャッシュのような考え方だと思う。
Proxy の配列版とも言えるかも。 |
#include <stdio.h> // printf()
// Flyweight パターン
class FlyweightPtn
{
public:
virtual void Puts() = 0 ;
} ;
// 実装
class Apple : public FlyweightPtn
{
char * m_str ;
public:
Apple()
{
// もっとメモリを沢山必要とするclassだと思われ
m_str = "+--------+\n| リンゴ |\n+--------+" ;
}
virtual void Puts()
{
puts( m_str ) ;
}
} ;
class Banana : public FlyweightPtn
{
char * m_str ;
public:
Banana()
{
// もっとメモリを沢山必要とするclassだと思われ
m_str = "onnnnnnnno\n8 バナナ 8\n8uuuuuuuu8" ;
}
virtual void Puts()
{
puts( m_str ) ;
}
} ;
class FlyweightFactory
{
// インスタンス化するのは2つ
Apple m_apple ;
Banana m_banana ;
FlyweightPtn * m_p_tbl[4] ;
public:
FlyweightFactory()
{
// 4つ分のインスタンス(メモリ)を2つ分に減らす。
m_p_tbl[0] = & m_apple ;
m_p_tbl[1] = & m_apple ;
m_p_tbl[2] = & m_banana ;
m_p_tbl[3] = & m_banana ;
}
void Put( int in )
{
m_p_tbl[ in % 4 ]->Puts() ;
}
} ;
int main()
{
FlyweightFactory factory ;
for ( int i=0 ; i<6 ; ++i )
{
factory.Put( i ) ;
}
return 0 ;
}
/* 実行結果
+--------+
| リンゴ |
+--------+
+--------+
| リンゴ |
+--------+
onnnnnnnno
8 バナナ 8
8uuuuuuuu8
onnnnnnnno
8 バナナ 8
8uuuuuuuu8
+--------+
| リンゴ |
+--------+
+--------+
| リンゴ |
+--------+
Press any key to continue
*/
|
|
| [構造] Flyweight パターン(非継承版) |
|
template ではできないね。
インスタンスを共有してメモリを軽くする。キャッシュのような考え方だと思う。
Proxy の配列版とも言えるかも。 |
#include <stdio.h> // printf()
// Flyweight パターン(非継承版)
// 実装
class Apple
{
char * m_str ;
public:
Apple()
{
// もっとメモリを沢山必要とするclassだと思われ
m_str = "+--------+\n| リンゴ |\n+--------+" ;
}
virtual void Puts()
{
puts( m_str ) ;
}
} ;
class Banana
{
char * m_str ;
public:
Banana()
{
// もっとメモリを沢山必要とするclassだと思われ
m_str = "onnnnnnnno\n8 バナナ 8\n8uuuuuuuu8" ;
}
virtual void Puts()
{
puts( m_str ) ;
}
} ;
class FlyweightFactory
{
// インスタンス化するのは2つ
Apple m_apple ;
Banana m_banana ;
public:
void Put( int in )
{
// 4つ分のインスタンス(メモリ)を2つ分に減らす。
// わざわざパターンにしなくても switch で良いと思うのだけど
// 数が多くなるとたいへんだけどね。^^;
switch( in % 4 )
{
case 0 : m_apple.Puts() ; break ;
case 1 : m_apple.Puts() ; break ;
case 2 : m_banana.Puts() ; break ;
case 3 : m_banana.Puts() ; break ;
}
}
} ;
int main()
{
FlyweightFactory factory ;
for ( int i=0 ; i<6 ; ++i )
{
factory.Put( i ) ;
}
return 0 ;
}
/* 実行結果
+--------+
| リンゴ |
+--------+
+--------+
| リンゴ |
+--------+
onnnnnnnno
8 バナナ 8
8uuuuuuuu8
onnnnnnnno
8 バナナ 8
8uuuuuuuu8
+--------+
| リンゴ |
+--------+
+--------+
| リンゴ |
+--------+
Press any key to continue
*/
|
|
| [構造] Proxy パターン |
|
代理(仲介者)を通すパターン。これは分かりやすいし簡単だよね。
キャッシュのパターンにも使える。
Decorator にも似ているが、Proxy は機能拡張をしない。 |
#include <stdio.h>
// Proxyパターン
// オリジナルも代理も同じインターフェースを使う
class ProxyPtn
{
public:
virtual void Eat() = 0 ;
} ;
// 実装 オリジナル
class Apple : public ProxyPtn
{
public:
Apple()
{
puts( "リンゴを買う。" ) ;
}
virtual void Eat()
{
puts( "あー、おいしい。" ) ;
}
} ;
// 実装 代理
class AppleProxy : public ProxyPtn
{
Apple * m_p_apple ;
public:
AppleProxy()
: m_p_apple( 0 ) // 必要になるまで作らないことにする
{}
~AppleProxy()
{
if ( m_p_apple != 0 )
{
delete m_p_apple ;
m_p_apple = 0 ;
}
}
virtual void Eat()
{
if ( m_p_apple == 0 )
{
// 無いので作る
m_p_apple = new Apple ;
}
m_p_apple->Eat() ;
}
} ;
int main()
{
puts( "----オリジナル-----" ) ;
{
Apple apple1 ;
Apple apple2 ;
Apple apple3 ;
// 3つ買ったけど、食べるのは1つ
apple1.Eat() ;
apple1.Eat() ;
}
puts( "----代理-----" ) ;
{
AppleProxy proxy1 ;
AppleProxy proxy2 ;
AppleProxy proxy3 ;
// 3つ買ったけど、食べるのは1つ
proxy1.Eat() ;
proxy1.Eat() ;
}
return 0 ;
}
/* 実行結果
----オリジナル-----
リンゴを買う。
リンゴを買う。
リンゴを買う。
あー、おいしい。
あー、おいしい。
----代理-----
リンゴを買う。
あー、おいしい。
あー、おいしい。
Press any key to continue
*/ |
|
| [構造] Proxy パターン (非継承版) |
|
代理(仲介者)を通すパターン。これは分かりやすいし簡単だよね。
キャッシュのパターンにも使える。
Decorator にも似ているが、Proxy は機能拡張をしない。 |
#include <stdio.h>
#include <vector> // std::vector
// Proxyパターン
// 実装 オリジナル
class Apple
{
public:
Apple()
{
puts( "リンゴを買う。" ) ;
}
void Eat()
{
puts( "あー、おいしい。" ) ;
}
} ;
// 実装 代理
class AppleProxy
{
// std::vector を使って強引に構築してみました。
// 効率的とは言い難いが、ま、ご参考ってことで。^^;
std::vector< Apple > m_apple ;
public:
virtual void Eat()
{
if ( m_apple.size() == 0 )
{
// コピーコンストラクタも動いちゃうけどね
m_apple.resize( 1 ) ;
}
m_apple[0].Eat() ;
}
} ;
int main()
{
puts( "----オリジナル-----" ) ;
{
Apple apple1 ;
Apple apple2 ;
Apple apple3 ;
// 3つ買ったけど、食べるのは1つ
apple1.Eat() ;
apple1.Eat() ;
}
puts( "----代理-----" ) ;
{
AppleProxy proxy1 ;
AppleProxy proxy2 ;
AppleProxy proxy3 ;
// 3つ買ったけど、食べるのは1つ
proxy1.Eat() ;
proxy1.Eat() ;
}
return 0 ;
}
/* 実行結果
----オリジナル-----
リンゴを買う。
リンゴを買う。
リンゴを買う。
あー、おいしい。
あー、おいしい。
----代理-----
リンゴを買う。
あー、おいしい。
あー、おいしい。
Press any key to continue
*/ |
|
| [振る舞い] Chain of Responsibility パターン |
|
| バケツリレー(「たらい回し」ともいう)なパターン |
#include <stdio.h> // printf()
// Chain of Responsibility パターン
class ChainPtn
{
public:
virtual void Shopping( int in ) = 0 ;
} ;
// 実装
class Apple : public ChainPtn
{
ChainPtn * m_chain ; // 次
public:
Apple( ChainPtn* in )
: m_chain( in )
{}
virtual void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
if ( in >= 120 )
{
printf( "リンゴ(120円)を1つ買う ->\n" ) ;
in -= 120 ;
}
else
{
printf( "リンゴ(120円)は買えない ->\n" ) ;
}
m_chain->Shopping( in ) ; // 次の店(ここがミソ)
}
} ;
class Banana : public ChainPtn
{
ChainPtn * m_chain ; // 次
public:
Banana( ChainPtn* in )
: m_chain( in )
{}
virtual void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
if ( in >= 60 )
{
printf( "バナナ(60円)を1つ買う ->\n" ) ;
in -= 60 ;
}
else
{
printf( "バナナ(60円)は買えない ->\n" ) ;
}
m_chain->Shopping( in ) ; // 次の店(ここがミソ)
}
} ;
class Home : public ChainPtn
{
public:
virtual void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
printf( "買い物終わり。おつかれさま。\n" ) ;
}
} ;
int main()
{
Home home ; // 買い物終わり。家に帰る。
Banana banana( & home ) ; // バナナを買ったら家に帰る。
Apple apple( & banana ) ; // リンゴを買ったらバナナを買う。
puts( "--- 10円で買い物 ---" ) ;
apple.Shopping( 10 ) ;
puts( "--- 100円で買い物 ---" ) ;
apple.Shopping( 100 ) ;
puts( "--- 150円で買い物 ---" ) ;
apple.Shopping( 150 ) ;
puts( "--- 300円で買い物 ---" ) ;
apple.Shopping( 300 ) ;
return 0 ;
}
/* 実行結果
--- 10円で買い物 ---
所持金 10 円。リンゴ(120円)は買えない ->
所持金 10 円。バナナ(60円)は買えない ->
所持金 10 円。買い物終わり。おつかれさま。
--- 100円で買い物 ---
所持金 100 円。リンゴ(120円)は買えない ->
所持金 100 円。バナナ(60円)を1つ買う ->
所持金 40 円。買い物終わり。おつかれさま。
--- 150円で買い物 ---
所持金 150 円。リンゴ(120円)を1つ買う ->
所持金 30 円。バナナ(60円)は買えない ->
所持金 30 円。買い物終わり。おつかれさま。
--- 300円で買い物 ---
所持金 300 円。リンゴ(120円)を1つ買う ->
所持金 180 円。バナナ(60円)を1つ買う ->
所持金 120 円。買い物終わり。おつかれさま。
Press any key to continue
*/ |
|
| [振る舞い] Chain of Responsibility パターン (非継承版) |
|
| バケツリレー(「たらい回し」ともいう)なパターン |
#include <stdio.h> // printf()
// 実装
template< class _ChainPtn >
class Apple
{
_ChainPtn & m_chain ; // 次
public:
Apple( _ChainPtn & in )
: m_chain( in )
{}
void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
if ( in >= 120 )
{
printf( "リンゴ(120円)を1つ買う ->\n" ) ;
in -= 120 ;
}
else
{
printf( "リンゴ(120円)は買えない ->\n" ) ;
}
m_chain.Shopping( in ) ; // 次の店(ここがミソ)
}
} ;
template< class _ChainPtn >
class Banana
{
_ChainPtn & m_chain ; // 次
public:
Banana( _ChainPtn & in )
: m_chain( in )
{}
void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
if ( in >= 60 )
{
printf( "バナナ(60円)を1つ買う ->\n" ) ;
in -= 60 ;
}
else
{
printf( "バナナ(60円)は買えない ->\n" ) ;
}
m_chain.Shopping( in ) ; // 次の店(ここがミソ)
}
} ;
class Home
{
public:
void Shopping( int in )
{
printf( "所持金%4d 円。", in ) ;
printf( "買い物終わり。おつかれさま。\n" ) ;
}
} ;
int main()
{
Home home ; // 買い物終わり
Banana< Home > banana( home ) ; // バナナを買ったら家に帰る
Apple< Banana< Home > > apple( banana ) ; // リンゴを買ったらバナナを買う
puts( "--- 10円で買い物 ---" ) ;
apple.Shopping( 10 ) ;
puts( "--- 100円で買い物 ---" ) ;
apple.Shopping( 100 ) ;
puts( "--- 150円で買い物 ---" ) ;
apple.Shopping( 150 ) ;
puts( "--- 300円で買い物 ---" ) ;
apple.Shopping( 300 ) ;
return 0 ;
}
/* 実行結果
--- 10円で買い物 ---
所持金 10 円。リンゴ(120円)は買えない ->
所持金 10 円。バナナ(60円)は買えない ->
所持金 10 円。買い物終わり。おつかれさま。
--- 100円で買い物 ---
所持金 100 円。リンゴ(120円)は買えない ->
所持金 100 円。バナナ(60円)を1つ買う ->
所持金 40 円。買い物終わり。おつかれさま。
--- 150円で買い物 ---
所持金 150 円。リンゴ(120円)を1つ買う ->
所持金 30 円。バナナ(60円)は買えない ->
所持金 30 円。買い物終わり。おつかれさま。
--- 300円で買い物 ---
所持金 300 円。リンゴ(120円)を1つ買う ->
所持金 180 円。バナナ(60円)を1つ買う ->
所持金 120 円。買い物終わり。おつかれさま。
Press any key to continue
*/ |
|
| [振る舞い] Command パターン |
|
実行(execute)部分を共通インターフェースにすることで、
実行結果の保存や再実行などの実装が楽になるパターン。 |
#include <stdio.h> // puts()
// 既存のclass
class Apple
{
public:
void NamePut()
{
puts( "これは「リンゴ」です。" ) ;
}
public:
void ColorPut()
{
puts( "色は「赤」です。" ) ;
}
} ;
// Commandパターン、共通インターフェースを提供
class CommandPtn
{
public:
virtual void Execute() = 0 ; // 実行
} ;
// 実装
class NameCommand : public CommandPtn
{
Apple * m_p_apple ;
public:
NameCommand( Apple* in )
{
m_p_apple = in ;
}
virtual void Execute() // 実行内容を実装
{
m_p_apple->NamePut() ;
}
} ;
class ColorCommand : public CommandPtn
{
Apple * m_p_apple ;
public:
ColorCommand( Apple* in )
{
m_p_apple = in ;
}
virtual void Execute() // 実行内容を実装
{
m_p_apple->ColorPut() ;
}
} ;
// コマンド
enum COMMAND
{
NAME_PUT_CMD = 0,
COLOR_PUT_CMD = 1,
} ;
void execute(
enum COMMAND * in_tbl,
int in_tbl_size,
Apple & in_apple
)
{
NameCommand apple_name_cmd( & in_apple ) ;
ColorCommand apple_color_cmd( & in_apple ) ;
// コマンド群を登録(配列にする意味があるのかな?^^;)
// インターフェースが共通だから配列に登録できるってことだよね。
CommandPtn * cmd_tbl[2] ;
cmd_tbl[NAME_PUT_CMD] = & apple_name_cmd ;
cmd_tbl[COLOR_PUT_CMD] = & apple_color_cmd ;
// コマンド実行
for ( int i=0 ; i<in_tbl_size ; ++i )
{
cmd_tbl[ in_tbl[i] ]->Execute() ;
}
}
int main()
{
Apple apple ;
puts( "----普通に実行----" ) ;
{
apple.NamePut() ;
apple.NamePut() ;
apple.ColorPut() ;
apple.ColorPut() ;
}
puts( "----コマンドテーブルを作って実行----" ) ;
{
enum COMMAND tbl[] =
{
NAME_PUT_CMD,
NAME_PUT_CMD,
COLOR_PUT_CMD,
COLOR_PUT_CMD,
} ;
execute( tbl, sizeof(tbl)/sizeof(tbl[0]), apple ) ;
}
return 0 ;
}
/* 実行結果
----普通に実行----
これは「リンゴ」です。
これは「リンゴ」です。
色は「赤」です。
色は「赤」です。
----コマンドテーブルを作って実行----
これは「リンゴ」です。
これは「リンゴ」です。
色は「赤」です。
色は「赤」です。
Press any key to continue
*/ |
|
| [振る舞い] Command パターン (非継承版) |
|
実行(execute)部分を共通インターフェースにすることで、
実行結果の保存や再実行などの実装が楽になるパターン。 |
#include <stdio.h> // puts()
// 既存のclass
class Apple
{
public:
void NamePut()
{
puts( "これは「リンゴ」です。" ) ;
}
public:
void ColorPut()
{
puts( "色は「赤」です" ) ;
}
} ;
// コマンド実装
enum COMMAND
{
NAME_PUT_CMD = 0,
COLOR_PUT_CMD = 1,
} ;
void execute(
enum COMMAND * in_tbl,
int in_tbl_size,
Apple & in_apple
)
{
// わざわざ継承なんてしなくたって、switch でいいのに…
// と思っったので、継承を使わないで、Command パターンを実装。
// なぜ、継承にこだわるのかは不明。これじゃだめなの?^^;
// パターンとは言わないってことかも。
// コマンド実行
for ( int i=0 ; i<in_tbl_size ; ++i )
{
switch ( in_tbl[i] )
{
case NAME_PUT_CMD : in_apple.NamePut() ; break ;
case COLOR_PUT_CMD : in_apple.ColorPut() ; break ;
} ;
}
}
int main()
{
Apple apple ;
puts( "----普通に実行----" ) ;
{
apple.NamePut() ;
apple.NamePut() ;
apple.ColorPut() ;
apple.ColorPut() ;
}
puts( "----コマンドテーブルを作って実行----" ) ;
{
enum COMMAND tbl[] =
{
NAME_PUT_CMD,
NAME_PUT_CMD,
COLOR_PUT_CMD,
COLOR_PUT_CMD,
} ;
execute( tbl, sizeof(tbl)/sizeof(tbl[0]), apple ) ;
}
return 0 ;
}
/* 実行結果
----普通に実行----
これは「リンゴ」です。
これは「リンゴ」です。
色は「赤」です
色は「赤」です
----コマンドテーブルを作って実行----
これは「リンゴ」です。
これは「リンゴ」です。
色は「赤」です
色は「赤」です
Press any key to continue
*/ |
|
| [振る舞い] Interpreter パターン |
|
ミニ言語の構築ができる。
Composite とほぼ同じだがインタプリタの用途で使う特化したパターンらしい。 |
#include <stdio.h> // printf()
#include <deque> // STL std::deque<>
class CompositePtn // Composite パターン
// (InterpreterPtn とすべきですが…わざとです^^;)
{
public:
// 共通インターフェース(実行)
virtual int Interpret() = 0 ;
} ;
// 実装
class CommandValue : public CompositePtn
{
public:
char m_val ; // '0'〜'9'
virtual int Interpret() // 実行
{
return m_val - '0' ;
}
} ;
class CommandMul : public CompositePtn
{
public:
CompositePtn * m_left_value ;
CompositePtn * m_right_value ;
char * m_syntax ;
CommandMul()
{
// E -> E * F
m_syntax = "E -> E * F" ;
}
virtual int Interpret() // 実行
{
int l = m_left_value->Interpret() ;
int r = m_right_value->Interpret() ;
int a = l * r ;
printf( "interpret trace: %d = %d * %d\n", a, l, r ) ;
return a ;
}
} ;
class CommandDiv : public CompositePtn
{
public:
CompositePtn * m_left_value ;
CompositePtn * m_right_value ;
char * m_syntax ;
CommandDiv()
{
// E -> E / F
m_syntax = "E -> E / F" ;
}
virtual int Interpret() // 実行
{
int l = m_left_value->Interpret() ;
int r = m_right_value->Interpret() ;
int a = l / r ;
printf( "interpret trace: %d = %d / %d\n", a, l, r ) ;
return a ;
}
} ;
class CommandAdd : public CompositePtn
{
public:
CompositePtn * m_left_value ;
CompositePtn * m_right_value ;
char * m_syntax ;
CommandAdd()
{
// E -> E + E
m_syntax = "E -> E + E" ;
}
virtual int Interpret() // 実行
{
int l = m_left_value->Interpret() ;
int r = m_right_value->Interpret() ;
int a = l + r ;
printf( "interpret trace: %d = %d + %d\n", a, l, r ) ;
return a ;
}
} ;
class CommandSub : public CompositePtn
{
public:
CompositePtn * m_left_value ;
CompositePtn * m_right_value ;
char * m_syntax ;
CommandSub()
{
// E -> E - E
m_syntax = "E -> E - E" ;
}
virtual int Interpret() // 実行
{
int l = m_left_value->Interpret() ;
int r = m_right_value->Interpret() ;
int a = l - r ;
printf( "interpret trace: %d = %d - %d\n", a, l, r ) ;
return a ;
}
} ;
class StackTbl
{
// 処理中のコマンドと構文
std::deque< char > m_command_stack ;
std::deque< CompositePtn * > m_syntax_stack ;
// 確定した構文(delete 用)
std::deque< CompositePtn * > m_delete_tbl ;
public:
~StackTbl()
{
for ( int i=0 ; i<m_delete_tbl.size() ; ++i )
{
delete m_delete_tbl[i] ;
}
}
void CommandStackPut()
{
printf( "command_stack: " ) ;
int i = m_command_stack.size()-1 ;
for ( ; i>=0 ; --i )
{
printf( "%c, ", m_command_stack[i] ) ;
}
printf( "\n" ) ;
}
// 構文解析
template< class _Command >
bool Parse( _Command & in_cmd )
{
// _1234_6_8_
// "0 -> 5 7 9" ;
if ( m_command_stack.size() > 2
&& m_command_stack[2] == in_cmd.m_syntax[5]
&& m_command_stack[1] == in_cmd.m_syntax[7]
&& m_command_stack[0] == in_cmd.m_syntax[9]
)
{} else return false ; // NG
printf( "syntax trace : '%s'\n", in_cmd.m_syntax ) ;
// m_syntax に登録(CompositePtn の Tree を作成)
_Command * cmd = new _Command() ;
m_delete_tbl.push_back( cmd ) ; // delete 用
cmd->m_right_value = m_syntax_stack[0] ;
m_syntax_stack.pop_front() ;
cmd->m_left_value = m_syntax_stack[0] ;
m_syntax_stack.pop_front() ;
m_syntax_stack.push_front( cmd ) ;
// m_command を更新
m_command_stack.pop_front() ;
m_command_stack.pop_front() ;
m_command_stack[0] = in_cmd.m_syntax[0] ;
return true ; // OK
}
bool ParseValue()
{
if ( m_command_stack.size() > 0
&& m_command_stack[0] >= '0' // 数値
&& m_command_stack[0] <= '9' // 数値
)
{} else return false ; // NG
printf( "syntax trace : value %c\n", m_command_stack[0] ) ;
// m_syntax に登録(CompositePtn の Leaf を作成)
CommandValue * val = new CommandValue() ;
m_delete_tbl.push_back( val ) ; // delete 用
val->m_val = m_command_stack[0] ;
m_syntax_stack.push_front( val ) ;
// m_command を更新
m_command_stack[0] = 'F' ; // 数値 -> _F_
return true ; // OK
}
// E -> F
bool ParseEF()
{
if ( m_command_stack.size() > 0
&& m_command_stack[0] == 'F'
)
{} else return false ; // NG
m_command_stack[0] = 'E' ;
return true ; // OK
}
void CommandPush( char in )
{
m_command_stack.push_front( in ) ;
}
// 実行(最後に残った CompositePtn の根っこを実行)
int Interpret()
{
if ( m_command_stack.size() != 1
|| m_command_stack[0] != 'E'
)
{
puts( "syntax error" ) ;
return 0 ; // DUMMY
}
return m_syntax_stack[0]->Interpret() ;
}
} ;
// インタープリタ
int interpret( char * in_cmd )
{
StackTbl stack_tbl ;
CommandMul cmd_mul ;
CommandDiv cmd_div ;
CommandAdd cmd_add ;
CommandSub cmd_sub ;
// 構文解析
int size = strlen( in_cmd ) ;
int i = 0 ;
for ( ; ; ) // Hit しなくなるまで繰り返す
{
stack_tbl.CommandStackPut() ;
if ( stack_tbl.ParseValue() ) continue ;
if ( stack_tbl.Parse( cmd_mul ) ) continue ;
if ( stack_tbl.Parse( cmd_div ) ) continue ;
if ( stack_tbl.Parse( cmd_add ) ) continue ;
if ( stack_tbl.Parse( cmd_sub ) ) continue ;
// E -> F
if ( stack_tbl.ParseEF() )
{
// 先読み
// このままループに戻ると +, - が Hit してしまう。
// +, - より *, / を優先するため、
// 先読みして *, / がある場合は command_stack に積む。姑息ぅ
if ( i<size )
{
if ( in_cmd[i] == '*'
|| in_cmd[i] == '/'
)
{
stack_tbl.CommandPush( in_cmd[i] ) ;
for ( ++i ; in_cmd[i] == ' ' ; ) ++i ;
}
}
continue ;
}
// 次のコマンド
if ( i<size )
{
stack_tbl.CommandPush( in_cmd[i] ) ;
for ( ++i ; in_cmd[i] == ' ' ; ) ++i ;
continue ;
}
break ;
}
return stack_tbl.Interpret() ; // 実行
}
int main()
{
printf( "%d\n", interpret( "+" ) ) ;
printf( "syntax error\n" ) ;
printf( "%d\n", interpret( "1" ) ) ;
printf( "%d\n", 1 ) ;
printf( "%d\n", interpret( "1 + 2 * 3" ) ) ;
printf( "%d\n", 1 + 2 * 3 ) ;
printf( "%d\n", interpret( "1 + 2 + 3 + 4" ) ) ;
printf( "%d\n", 1 + 2 + 3 + 4 ) ;
printf( "%d\n", interpret( "1 - 2 - 3 - 4" ) ) ;
printf( "%d\n", 1 - 2 - 3 - 4 ) ;
printf( "%d\n", interpret( "1 + 2 - 3 + 4" ) ) ;
printf( "%d\n", 1 + 2 - 3 + 4 ) ;
printf( "%d\n", interpret( "1 + 2 * 3 + 4" ) ) ;
printf( "%d\n", 1 + 2 * 3 + 4 ) ;
printf( "%d\n", interpret( "1 * 2 + 3 * 4" ) ) ;
printf( "%d\n", 1 * 2 + 3 * 4 ) ;
printf( "%d\n", interpret( "2 * 3 * 4 * 5" ) ) ;
printf( "%d\n", 2 * 3 * 4 * 5 ) ;
printf( "%d\n", interpret( "9 / 3 / 2" ) ) ;
printf( "%d\n", 9 / 3 / 2 ) ;
printf( "%d\n", interpret( "3 * 9 / 6" ) ) ;
printf( "%d\n", 3 * 9 / 6 ) ;
return 0 ;
}
/* 実行結果
command_stack:
command_stack: +,
syntax error
0
syntax error
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
1
1
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
command_stack: E, +,
command_stack: E, +, 2,
syntax trace : value 2
command_stack: E, +, F,
command_stack: E, +, E, *,
command_stack: E, +, E, *, 3,
syntax trace : value 3
command_stack: E, +, E, *, F,
syntax trace : 'E -> E * F'
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
interpret trace: 6 = 2 * 3
interpret trace: 7 = 1 + 6
7
7
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
command_stack: E, +,
command_stack: E, +, 2,
syntax trace : value 2
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
command_stack: E, +,
command_stack: E, +, 3,
syntax trace : value 3
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
command_stack: E, +,
command_stack: E, +, 4,
syntax trace : value 4
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
interpret trace: 3 = 1 + 2
interpret trace: 6 = 3 + 3
interpret trace: 10 = 6 + 4
10
10
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
command_stack: E, -,
command_stack: E, -, 2,
syntax trace : value 2
command_stack: E, -, F,
command_stack: E, -, E,
syntax trace : 'E -> E - E'
command_stack: E,
command_stack: E, -,
command_stack: E, -, 3,
syntax trace : value 3
command_stack: E, -, F,
command_stack: E, -, E,
syntax trace : 'E -> E - E'
command_stack: E,
command_stack: E, -,
command_stack: E, -, 4,
syntax trace : value 4
command_stack: E, -, F,
command_stack: E, -, E,
syntax trace : 'E -> E - E'
command_stack: E,
interpret trace: -1 = 1 - 2
interpret trace: -4 = -1 - 3
interpret trace: -8 = -4 - 4
-8
-8
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
command_stack: E, +,
command_stack: E, +, 2,
syntax trace : value 2
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
command_stack: E, -,
command_stack: E, -, 3,
syntax trace : value 3
command_stack: E, -, F,
command_stack: E, -, E,
syntax trace : 'E -> E - E'
command_stack: E,
command_stack: E, +,
command_stack: E, +, 4,
syntax trace : value 4
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
interpret trace: 3 = 1 + 2
interpret trace: 0 = 3 - 3
interpret trace: 4 = 0 + 4
4
4
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E,
command_stack: E, +,
command_stack: E, +, 2,
syntax trace : value 2
command_stack: E, +, F,
command_stack: E, +, E, *,
command_stack: E, +, E, *, 3,
syntax trace : value 3
command_stack: E, +, E, *, F,
syntax trace : 'E -> E * F'
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
command_stack: E, +,
command_stack: E, +, 4,
syntax trace : value 4
command_stack: E, +, F,
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
interpret trace: 6 = 2 * 3
interpret trace: 7 = 1 + 6
interpret trace: 11 = 7 + 4
11
11
command_stack:
command_stack: 1,
syntax trace : value 1
command_stack: F,
command_stack: E, *,
command_stack: E, *, 2,
syntax trace : value 2
command_stack: E, *, F,
syntax trace : 'E -> E * F'
command_stack: E,
command_stack: E, +,
command_stack: E, +, 3,
syntax trace : value 3
command_stack: E, +, F,
command_stack: E, +, E, *,
command_stack: E, +, E, *, 4,
syntax trace : value 4
command_stack: E, +, E, *, F,
syntax trace : 'E -> E * F'
command_stack: E, +, E,
syntax trace : 'E -> E + E'
command_stack: E,
interpret trace: 2 = 1 * 2
interpret trace: 12 = 3 * 4
interpret trace: 14 = 2 + 12
14
14
command_stack:
command_stack: 2,
syntax trace : value 2
command_stack: F,
command_stack: E, *,
command_stack: E, *, 3,
syntax trace : value 3
command_stack: E, *, F,
syntax trace : 'E -> E * F'
command_stack: E,
command_stack: E, *,
command_stack: E, *, 4,
syntax trace : value 4
command_stack: E, *, F,
syntax trace : 'E -> E * F'
command_stack: E,
command_stack: E, *,
command_stack: E, *, 5,
syntax trace : value 5
command_stack: E, *, F,
syntax trace : 'E -> E * F'
command_stack: E,
interpret trace: 6 = 2 * 3
interpret trace: 24 = 6 * 4
interpret trace: 120 = 24 * 5
120
120
command_stack:
command_stack: 9,
syntax trace : value 9
command_stack: F,
command_stack: E, /,
command_stack: E, /, 3,
syntax trace : value 3
command_stack: E, /, F,
syntax trace : 'E -> E / F'
command_stack: E,
command_stack: E, /,
command_stack: E, /, 2,
syntax trace : value 2
command_stack: E, /, F,
syntax trace : 'E -> E / F'
command_stack: E,
interpret trace: 3 = 9 / 3
interpret trace: 1 = 3 / 2
1
1
command_stack:
command_stack: 3,
syntax trace : value 3
command_stack: F,
command_stack: E, *,
command_stack: E, *, 9,
syntax trace : value 9
command_stack: E, *, F,
syntax trace : 'E -> E * F'
command_stack: E,
command_stack: E, /,
command_stack: E, /, 6,
syntax trace : value 6
command_stack: E, /, F,
syntax trace : 'E -> E / F'
command_stack: E,
interpret trace: 27 = 3 * 9
interpret trace: 4 = 27 / 6
4
4
Press any key to continue
*/ |
|
| [振る舞い] Iterator パターン |
|
| STL にあるので詳細は省略。要するに、ポインタのような動作をするモノ。 |
#include <stdio.h> // puts()
#ifdef WIN32
#ifdef _DEBUG
// VC++6.0 だと std::string と std::vector を一緒に使うと
// warning がでる。ので抑止。ったく。
#pragma warning( disable : 4786 )
#endif // _DEBUG
#endif // WIN32
#include <string> // STL std::string
#include <vector> // STL std::vector<>
// Iterator パターン
class IteratorObject
{} ;
class IteratorPtn
{
virtual IteratorObject * Iterator() = 0 ; // イテレータ取得
virtual IteratorObject * Next() = 0 ; // 次
} ;
// 実装
class Fruit : public IteratorObject
{
public :
std::string m_name ; // 名前
Fruit( std::string in )
: m_name( in )
{}
} ;
class Kato : public IteratorPtn
{
#define TBL_MAX 10
IteratorObject * m_tbl[TBL_MAX] ;
int m_tbl_size ;
int m_point ;
public:
Kato()
{
m_tbl_size = 0 ;
m_point = 0 ;
}
~Kato()
{
for ( int i=0 ; i<m_tbl_size ; ++i )
{
delete m_tbl[i] ; // new したら delete する^^;
}
}
void Have( IteratorObject * in )
{
if ( m_tbl_size < TBL_MAX )
{
m_tbl[m_tbl_size++] = in ; // テーブルに追加
}
}
virtual IteratorObject * Iterator() // イテレータ取得
{
m_point = 0 ;
return Next() ;
}
virtual IteratorObject * Next() // 次
{
if ( m_point < m_tbl_size )
{
return m_tbl[m_point++] ;
}
return 0 ;
}
#undef TBL_MAX
} ;
int main()
{
// 自作イテレータ
{
Kato ai ; // 誰?
ai.Have( new Fruit( "Apple" ) ) ;
ai.Have( new Fruit( "Banana" ) ) ;
ai.Have( new Fruit( "Cacao" ) ) ;
// こんな感じに使うのがミソ。
IteratorObject * it = ai.Iterator() ;
for ( ; it != 0 ; it = ai.Next() )
{
puts( ((Fruit*)it)->m_name.c_str() ) ;
}
}
//---------------------
// STL なら簡単なのにねー。
std::vector< std::string > ryoko ; // だからぁ誰?
ryoko.push_back( "Apple" ) ;
ryoko.push_back( "Banana" ) ;
ryoko.push_back( "Cacao" ) ;
// STLでのイテレータ
puts( "------" ) ;
{
std::vector< std::string >::iterator it = ryoko.begin() ;
for ( ; it != ryoko.end() ; ++it )
{
puts( it->c_str() ) ;
}
}
// 個人的には(ランダムアクセス可能な)こっちの使い方が好み。
// でも、これはイテレータとは言わないらしい。^^;
puts( "------" ) ;
{
const int tbl_max = ryoko.size() ;
for ( int i=0 ; i<tbl_max ; ++i )
{
puts( ryoko[i].c_str() ) ;
}
}
return 0 ;
}
/* 実行結果
Apple
Banana
Cacao
------
Apple
Banana
Cacao
------
Apple
Banana
Cacao
Press any key to continue
*/ |
|
| [振る舞い] Mediator パターン |
|
| ML(メイリングリスト)のようなイメージ。まとめ者のいるパターン。 |
#include <stdio.h> // printf()
#include <string> // STL std::string
// Mediator パターン
class MediatorPtn // 相談役/調停者/まとめ者
{
public:
virtual void MessagePut( const std::string & in_str ) = 0 ;
} ;
class MemberPtn // 各メンバー
{
public :
MediatorPtn * m_mediator ; // まとめ者
virtual void MessageGet( const std::string & in_str ) = 0 ;
} ;
// メンバーの実装
class Apple : public MemberPtn
{
public:
virtual void MessageGet( const std::string & in_str )
{
if ( in_str == "リンゴ" )
{
puts( "リンゴと言えば…バ、ナ、ナ" ) ;
m_mediator->MessagePut( "バナナ" ) ; // まとめ者に通達
}
}
} ;
class Banana : public MemberPtn
{
public:
virtual void MessageGet( const std::string & in_str )
{
if ( in_str == "バナナ" )
{
puts( "バナナと言えば…カ、カ、オ" ) ;
m_mediator->MessagePut( "カカオ" ) ; // まとめ者に通達
}
}
} ;
class Cacao : public MemberPtn
{
public:
virtual void MessageGet( const std::string & in_str )
{
if ( in_str == "カカオ" )
{
puts( "カカオと言えば…リ、ン、ゴ" ) ;
m_mediator->MessagePut( "リンゴ" ) ; // まとめ者に通達
}
}
} ;
// まとめ者の実装
class Hirosue : public MediatorPtn
{
std::string m_message ; // 通達メッセージ
// 支配下のメンバを登録(ホントは MemberPtn のテーブルにしたいね)
Apple m_apple ;
Banana m_banana ;
Cacao m_cacao ;
public:
Hirosue()
{
m_message = "リンゴ" ; // リンゴからスタート
m_apple.m_mediator = this ; // まとめ者は Hirosue
m_banana.m_mediator = this ; // まとめ者は Hirosue
m_cacao.m_mediator = this ; // まとめ者は Hirosue
}
// 意見を収集
virtual void MessagePut( const std::string & in_str )
{
m_message = in_str ;
}
// みんなに通知
void Notice()
{
std::string str = m_message ;
m_apple.MessageGet( str ) ;
m_banana.MessageGet( str ) ;
m_cacao.MessageGet( str ) ;
}
} ;
int main()
{
Hirosue ryoko ; // まとめ者
for (int i=0 ; i<7 ; ++i )
{
ryoko.Notice() ; // みんなに通知
}
return 0 ;
}
/* 実行結果
リンゴと言えば…バ、ナ、ナ
バナナと言えば…カ、カ、オ
カカオと言えば…リ、ン、ゴ
リンゴと言えば…バ、ナ、ナ
バナナと言えば…カ、カ、オ
カカオと言えば…リ、ン、ゴ
リンゴと言えば…バ、ナ、ナ
Press any key to continue
*/ |
|
| [振る舞い] Mediator パターン(非継承版) |
|
| ML(メイリングリスト)のようなイメージ。まとめ者のいるパターン。 |
#include <stdio.h> // printf()
#include <string> // STL std::string
// メンバーの実装
template< class _MediatorPtn >
class Apple
{
public:
_MediatorPtn * m_mediator ; // まとめ者
void MessageGet( const std::string & in_str )
{
if ( in_str == "リンゴ" )
{
puts( "リンゴと言えば…バ、ナ、ナ" ) ;
m_mediator->MessagePut( "バナナ" ) ; // まとめ者に通達
}
}
} ;
template< class _MediatorPtn >
class Banana
{
public:
_MediatorPtn * m_mediator ; // まとめ者
void MessageGet( const std::string & in_str )
{
if ( in_str == "バナナ" )
{
puts( "バナナと言えば…カ、カ、オ" ) ;
m_mediator->MessagePut( "カカオ" ) ; // まとめ者に通達
}
}
} ;
template< class _MediatorPtn >
class Cacao
{
public:
_MediatorPtn * m_mediator ; // まとめ者
void MessageGet( const std::string & in_str )
{
if ( in_str == "カカオ" )
{
puts( "カカオと言えば…リ、ン、ゴ" ) ;
m_mediator->MessagePut( "リンゴ" ) ; // まとめ者に通達
}
}
} ;
// まとめ者の実装
class Hirosue
{
std::string m_message ; // 通達メッセージ
// 支配下のメンバを登録(ホントはテーブルにしたいね)
Apple< Hirosue > m_apple ;
Banana< Hirosue > m_banana ;
Cacao< Hirosue > m_cacao ;
public:
Hirosue()
{
m_message = "リンゴ" ; // リンゴからスタート
m_apple.m_mediator = this ; // まとめ者は Hirosue
m_banana.m_mediator = this ; // まとめ者は Hirosue
m_cacao.m_mediator = this ; // まとめ者は Hirosue
}
// 意見を収集
void MessagePut( const std::string & in_str )
{
m_message = in_str ;
}
// みんなに通知
void Notice()
{
std::string str = m_message ;
m_apple.MessageGet( str ) ;
m_banana.MessageGet( str ) ;
m_cacao.MessageGet( str ) ;
}
} ;
int main()
{
Hirosue ryoko ; // まとめ者
for (int i=0 ; i<7 ; ++i )
{
ryoko.Notice() ; // みんなに通知
}
return 0 ;
}
/* 実行結果
リンゴと言えば…バ、ナ、ナ
バナナと言えば…カ、カ、オ
カカオと言えば…リ、ン、ゴ
リンゴと言えば…バ、ナ、ナ
バナナと言えば…カ、カ、オ
カカオと言えば…リ、ン、ゴ
リンゴと言えば…バ、ナ、ナ
Press any key to continue
*/ |
|
| [振る舞い] Memento パターン(非継承版) |
|
継承するまでもないよね。
ステータス情報をセーブしたりロードしたりするパターン。 |
#include <stdio.h> // printf()
#include <stdlib.h> // rand()
#include <vector> // STL std::vector<>
// Memento パターンだけど、パターンと言うのかな?
class Memento // 保存情報(ステータス)
{
friend class Hirosue ; // 値が見れる/触れるのは Hirosue だけ。
int m_weight ; // 体重
} ;
class Hirosue
{
#define START_KG 43
public :
Memento m_stat ; // 保存情報(ステータス)
Hirosue()
{
m_stat.m_weight = START_KG ; // Kg
}
int Diet()
{
int apple = 1 + rand() % 5 ; // リンゴ
int banana = 1 + rand() % 5 ; // バナナ
m_stat.m_weight += banana - apple ; // バナナは増えるリンゴは減る
printf( "リンゴ%2d個、バナナ%2d本 -> %3d Kg\n",
apple, banana, m_stat.m_weight
) ;
return m_stat.m_weight - START_KG ; // どのくらい変化したか?
}
#undef START_KG
} ;
int main()
{
std::vector< Memento > m_save_tbl ; // 過去の記録
Hirosue ryoko ;
for ( int i=0 ; i<10 ; ++i )
{
// 今の状態を保存
m_save_tbl.push_back( ryoko.m_stat ) ;
int w = ryoko.Diet() ;
if ( w < -2 ) // 3Kg減
{
puts( "やせ過ぎ、1つ前に戻す。" ) ;
ryoko.m_stat = m_save_tbl.back() ;
continue ;
}
if ( w > 2 ) // 3Kg増
{
puts( "太り過ぎ、1つ前に戻す。" ) ;
ryoko.m_stat = m_save_tbl.back() ;
continue ;
}
}
return 0 ;
}
/* 実行結果
リンゴ 2個、バナナ 3本 -> 44 Kg
リンゴ 5個、バナナ 1本 -> 40 Kg
やせ過ぎ、1つ前に戻す。
リンゴ 5個、バナナ 5本 -> 44 Kg
リンゴ 4個、バナナ 4本 -> 44 Kg
リンゴ 3個、バナナ 5本 -> 46 Kg
太り過ぎ、1つ前に戻す。
リンゴ 1個、バナナ 1本 -> 44 Kg
リンゴ 2個、バナナ 3本 -> 45 Kg
リンゴ 2個、バナナ 2本 -> 45 Kg
リンゴ 1個、バナナ 3本 -> 47 Kg
太り過ぎ、1つ前に戻す。
リンゴ 3個、バナナ 2本 -> 44 Kg
Press any key to continue
*/ |
|
| [振る舞い] Observer パターン |
|
| ドキュメント(データ)が更新された時、関連するビュー(観察者)に知らされるパターン。 |
#include <stdio.h> // printf()
#include <stdlib.h> // rand()
// Observer パターン(観察者)
class ObserverPtn
{
public:
virtual void Update( int in_n ) = 0 ;
} ;
// 実装
class Apple : public ObserverPtn
{
public:
virtual void Update( int in_n ) // 知らせを受ける
{
printf( "リンゴなら %d 個やな。\n", in_n / 120 ) ;
}
} ;
class Banana : public ObserverPtn
{
public:
virtual void Update( int in_n ) // 知らせを受ける
{
printf( "バナナなら %d 本、買えまっせ〜\n", in_n / 60 ) ;
}
} ;
class Honjyo
{
#define TBL_MAX 8
ObserverPtn * m_tbl[TBL_MAX] ;
int m_tbl_size ;
int m_value ;
public:
Honjyo()
: m_tbl_size( 0 )
, m_value( 0 )
{}
public:
void Attach( ObserverPtn* in ) // 観察者を登録
{
if ( m_tbl_size < TBL_MAX )
{
m_tbl[m_tbl_size++] = in ;
}
}
public:
void DoSomething()
{
if ( (rand()%2) == 0 )
{
// 変化あり
// お小遣いGet!!(200円まで)
m_value += rand() % 200 ;
printf( "お小遣い %d 円!\n", m_value ) ;
// みんな(観察者)に知らせる
for ( int i=0 ; i<m_tbl_size ; ++i )
{
m_tbl[i]->Update( m_value ) ;
}
}
else
{
// 変化なし
puts( "ひまやな〜" ) ;
}
}
#undef TBL_MAX
} ;
int main()
{
Honjyo manami ; // 観察される人
Apple apple_shop ; // 観察する人?
Banana banana_shop ; // 観察する人?
manami.Attach( & apple_shop ) ;
manami.Attach( & banana_shop ) ;
for ( int i=0 ; i<8 ; ++i )
{
manami.DoSomething() ;
}
return 0 ;
}
/* 実行結果
ひまやな〜
ひまやな〜
お小遣い 100 円!
リンゴなら 0 個やな。
バナナなら 1 本、買えまっせ〜
ひまやな〜
お小遣い 178 円!
リンゴなら 1 個やな。
バナナなら 2 本、買えまっせ〜
お小遣い 340 円!
リンゴなら 2 個やな。
バナナなら 5 本、買えまっせ〜
お小遣い 445 円!
リンゴなら 3 個やな。
バナナなら 7 本、買えまっせ〜
ひまやな〜
Press any key to continue
*/ |
|
| [振る舞い] Observer パターン (非継承版) |
|
| ドキュメント(データ)が更新された時、関連するビュー(観察者)に知らされるパターン。 |
#include <stdio.h> // printf()
#include <stdlib.h> // rand()
// Observer パターン(観察者)
// 実装
class Apple
{
public:
void Update( int in_n ) // 知らせを受ける
{
printf( "リンゴなら %d 個やな。\n", in_n / 120 ) ;
}
} ;
class Banana
{
public:
void Update( int in_n ) // 知らせを受ける
{
printf( "バナナなら %d 本、買えまっせ〜\n", in_n / 60 ) ;
}
} ;
template< class _ObserverPtn1, class _ObserverPtn2 >
class Honjyo
{
// ホントはテーブルにしたいけど、template では無理だね。
_ObserverPtn1 & m_observer1 ;
_ObserverPtn2 & m_observer2 ;
int m_value ;
public:
Honjyo( _ObserverPtn1 & in1, _ObserverPtn2 & in2 ) // 観察者
: m_observer1( in1 )
, m_observer2( in2 )
, m_value( 0 )
{}
void DoSomething()
{
if ( (rand()%2) == 0 )
{
// 変化あり
// お小遣いGet!!(200円まで)
m_value += rand() % 200 ;
printf( "お小遣い %d 円!\n", m_value ) ;
// みんな(観察者)に知らせる
m_observer1.Update( m_value ) ;
m_observer2.Update( m_value ) ;
}
else
{
// 変化なし
puts( "ひまやな〜" ) ;
}
}
} ;
int main()
{
Apple apple_shop ; // 観察する人?
Banana banana_shop ; // 観察する人?
// 観察される人
Honjyo< Apple, Banana > manami( apple_shop, banana_shop ) ;
for ( int i=0 ; i<8 ; ++i )
{
manami.DoSomething() ;
}
return 0 ;
}
/* 実行結果
ひまやな〜
ひまやな〜
お小遣い 100 円!
リンゴなら 0 個やな。
バナナなら 1 本、買えまっせ〜
ひまやな〜
お小遣い 178 円!
リンゴなら 1 個やな。
バナナなら 2 本、買えまっせ〜
お小遣い 340 円!
リンゴなら 2 個やな。
バナナなら 5 本、買えまっせ〜
お小遣い 445 円!
リンゴなら 3 個やな。
バナナなら 7 本、買えまっせ〜
ひまやな〜
Press any key to continue
*/
|
|
| [振る舞い] State パターン |
|
状態をインスタンスにするパターン。継承を使う最も簡単な例ともいえる。
他の似ているパターンの微妙な違い
TemplateMethod :
静的な拡張(ルーチンの共通化)が目的
Strategy : 入れ替え可能な状態として作りこむのが目的
* State :
頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // puts()
#include <stdlib.h> // rand()
// Stateパターン
class StatePtn
{
public:
virtual void Eat() = 0 ; // 食べる(共通動作)
} ;
// 実装
class Apple : public StatePtn
{
public:
virtual void Eat()
{
puts( "リンゴ大好き!。パクパク。" ) ;
}
} ;
class Banana : public StatePtn
{
public:
virtual void Eat()
{
puts( "バナナおいしいね。ムシャムシャ。" ) ;
}
} ;
class Hirosue
{
StatePtn * m_stat ; // 状態、1つしか持たない。
public:
Hirosue()
: m_stat( 0 )
{}
public:
void Have( StatePtn * in )
{
m_stat = in ;
}
public:
void DoSomething()
{
// なにかをする。(状態によってなにをするのかが変わる)
if ( m_stat != 0 ) m_stat->Eat() ; // 持っているモノを食べる
else puts( "なにも持ってませーん。" ) ;
}
} ;
int main()
{
Apple apple ;
Banana banana ;
Hirosue ryoko ;
for ( int i=0 ; i<10 ; ++i )
{
// 乱数で行動を決定
int r = rand() % 4 ;
printf( "%d : %d ", i, r ) ;
switch ( r )
{
case 0 :
ryoko.Have( & apple ) ;
puts( "リンゴをもらいました。" ) ;
break ;
case 1 :
ryoko.Have( & banana ) ;
puts( "バナナをもらいました。" ) ;
break ;
case 2 :
ryoko.DoSomething() ; // なにかをする
break ;
default:
puts( "ひまですぅ" ) ; // なにもしない
break ;
}
// end switch r
}
// end for i
return 0 ;
}
/* 実行結果
0 : 1 バナナをもらいました。
1 : 3 ひまですぅ
2 : 2 バナナおいしいね。ムシャムシャ。
3 : 0 リンゴをもらいました。
4 : 1 バナナをもらいました。
5 : 0 リンゴをもらいました。
6 : 2 リンゴ大好き!。パクパク。
7 : 2 リンゴ大好き!。パクパク。
8 : 2 リンゴ大好き!。パクパク。
9 : 0 リンゴをもらいました。
Press any key to continue
*/ |
|
| [振る舞い] State パターン(非継承版) |
|
状態をインスタンスにするパターン。
template では入れ替えは難しいですね。できないことはないけどいまいちです。
他の似ているパターンの微妙な違い
TemplateMethod :
静的な拡張(ルーチンの共通化)が目的
Strategy : 入れ替え可能な状態として作りこむのが目的
* State :
頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // puts()
#include <stdlib.h> // rand()
// Stateパターン
// 実装
class Apple
{
public:
void Eat()
{
puts( "リンゴ大好き!。パクパク。" ) ;
}
} ;
class Banana
{
public:
void Eat()
{
puts( "バナナおいしいね。ムシャムシャ。" ) ;
}
} ;
enum STAT { NOTHING, APPLE, BANANA, } ;
template< class _StatA, class _StatB >
class Hirosue
{
enum STAT m_stat ; // 状態、1つしか持たない。
_StatA & m_a ;
_StatB & m_b ;
public:
Hirosue(
_StatA & in_a,
_StatB & in_b
)
: m_stat( NOTHING )
, m_a( in_a )
, m_b( in_b )
{}
public:
void Have( const enum STAT & in )
{
m_stat = in ;
}
public:
void DoSomething()
{
// なにかをする。(状態によってなにをするのかが変わる)
// このパターンはすなおに継承版を使うべきかも。
// switch を使うといまいち。
switch ( m_stat )
{
case APPLE : m_a.Eat() ; break ;
case BANANA : m_b.Eat() ; break ;
default : puts( "なにも持ってませーん。" ) ; break ;
}
}
} ;
int main()
{
Apple apple ;
Banana banana ;
Hirosue< Apple, Banana > ryoko( apple, banana ) ;
for ( int i=0 ; i<10 ; ++i )
{
// 乱数で行動を決定
int r = rand() % 4 ;
printf( "%d : %d ", i, r ) ;
switch ( r )
{
case 0 :
ryoko.Have( APPLE ) ;
puts( "リンゴをもらいました。" ) ;
break ;
case 1 :
ryoko.Have( BANANA ) ;
puts( "バナナをもらいました。" ) ;
break ;
case 2 :
ryoko.DoSomething() ; // なにかをする
break ;
default:
puts( "ひまですぅ" ) ; // なにもしない
break ;
}
// end switch r
}
// end for i
return 0 ;
}
/* 実行結果
0 : 1 バナナをもらいました。
1 : 3 ひまですぅ
2 : 2 バナナおいしいね。ムシャムシャ。
3 : 0 リンゴをもらいました。
4 : 1 バナナをもらいました。
5 : 0 リンゴをもらいました。
6 : 2 リンゴ大好き!。パクパク。
7 : 2 リンゴ大好き!。パクパク。
8 : 2 リンゴ大好き!。パクパク。
9 : 0 リンゴをもらいました。
Press any key to continue
*/ |
|
| [振る舞い] Strategy パターン |
|
戦略を考える「頭脳(アルゴリズム)」を入れ替えるパターン
他の似ているパターンの微妙な違い
TemplateMethod
: 静的な拡張(ルーチンの共通化)が目的
* Strategy : 入れ替え可能な状態として作りこむのが目的
State :
頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // puts()
// Strategy パターン?
enum COLOR { RED, BLUE, YELLOW, } ; // 色
enum SHAPE { BALL, BOX, COLUMN, } ; // 形
class StrategyPtn
{
public:
virtual char * FinalAnswer(
enum COLOR in_color, // 色
enum SHAPE in_shape // 形
) = 0 ;
} ;
// 実装
class Hirosue : public StrategyPtn // 戦略アルゴリズム
{
public:
virtual char * FinalAnswer(
enum COLOR in_color, // 色
enum SHAPE in_shape // 形
)
{
// ホントはいろいろ複雑な計算をしていると思われ
if ( in_shape == BALL ) return "これはリンゴかな?" ;
else return "たぶんバナナでしょ" ;
}
} ;
class Honjyo : public StrategyPtn // 戦略アルゴリズム
{
public:
virtual char * FinalAnswer(
enum COLOR in_color, // 色
enum SHAPE in_shape // 形
)
{
// ホントはいろいろ複雑な計算をしていると思われ
if ( in_color == RED ) return "赤いのはリンゴや" ;
else return "他の色はバナナや" ;
}
} ;
class Apple
{
StrategyPtn * m_strategy ; // 戦略アルゴリズム
public:
// ↓切り替え可能( Stateパターンほど頻繁に切り替えるわけではない)
Apple( StrategyPtn * in_strategy )
{
m_strategy = in_strategy ;
}
void Answer_Put()
{
puts( m_strategy->FinalAnswer( RED, BALL ) ) ;
}
} ;
class Banana
{
StrategyPtn * m_strategy ;
public:
Banana( StrategyPtn * in_strategy ) // <- 切り替え可能
{
m_strategy = in_strategy ;
}
void Answer_Put()
{
puts( m_strategy->FinalAnswer( YELLOW, COLUMN ) ) ;
}
} ;
int main()
{
Hirosue ryoko ;
Honjyo manami ;
{
Apple apple( & ryoko ) ;
Banana banana( & manami ) ;
apple.Answer_Put() ;
banana.Answer_Put() ;
}
{
Apple apple( & manami ) ;
Banana banana( & ryoko ) ;
apple.Answer_Put() ;
banana.Answer_Put() ;
}
return 0 ;
}
/* 実行結果
これはリンゴかな?
他の色はバナナや
赤いのはリンゴや
たぶんバナナでしょ
Press any key to continue
*/ |
|
| [振る舞い] Strategy パターン (非継承版) |
|
戦略を考える「頭脳(アルゴリズム)」を入れ替えるパターン
他の似ているパターンの微妙な違い
TemplateMethod
: 静的な拡張(ルーチンの共通化)が目的
* Strategy : 入れ替え可能な状態として作りこむのが目的
State :
頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // puts()
// 実装
enum COLOR { RED, BLUE, YELLOW, } ; // 色
enum SHAPE { BALL, BOX, COLUMN, } ; // 形
class Hirosue // 戦略アルゴリズム
{
public:
char * FinalAnswer(
enum COLOR in_color, // 色
enum SHAPE in_shape // 形
)
{
// ホントはいろいろ複雑な計算をしていると思われ
if ( in_shape == BALL ) return "これはリンゴかな?" ;
else return "たぶんバナナでしょ" ;
}
} ;
class Honjyo // 戦略アルゴリズム
{
public:
char * FinalAnswer(
enum COLOR in_color, // 色
enum SHAPE in_shape // 形
)
{
// ホントはいろいろ複雑な計算をしていると思われ
if ( in_color == RED ) return "赤いのはリンゴや" ;
else return "他の色はバナナや" ;
}
} ;
template< class _StrategyPtn >
class Apple
{
_StrategyPtn m_strategy ; // 戦略アルゴリズム <- 切り替え可能
public:
void Answer_Put()
{
puts( m_strategy.FinalAnswer( RED, BALL ) ) ;
}
} ;
template< class _StrategyPtn >
class Banana
{
_StrategyPtn m_strategy ; // 戦略アルゴリズム <- 切り替え可能
public:
void Answer_Put()
{
puts( m_strategy.FinalAnswer( YELLOW, COLUMN ) ) ;
}
} ;
int main()
{
{
Apple< Hirosue > apple ;
Banana< Honjyo > banana ;
apple.Answer_Put() ;
banana.Answer_Put() ;
}
{
Apple< Honjyo > apple ;
Banana< Hirosue > banana ;
apple.Answer_Put() ;
banana.Answer_Put() ;
}
return 0 ;
}
/* 実行結果
これはリンゴかな?
他の色はバナナや
赤いのはリンゴや
たぶんバナナでしょ
Press any key to continue
*/ |
|
| [振る舞い] TemplateMethod パターン |
|
一部の機能を後で実装する。委譲ではなく、継承を使うのが TemplateMethod のパターン。
TemplateMethod というより SkeletonMethod と呼んだほうが良いよね。
他の似ているパターンの微妙な違い
*
TemplateMethod : 静的な拡張(ルーチンの共通化)が目的
Strategy :
入れ替え可能な状態として作りこむのが目的
State : 頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // printf()
// TemplateMethod パターン
class Kato
{
// 後で実装(肉付け)するメソッド
virtual char * Name() = 0 ; // <- 未実装
virtual char * Color() = 0 ; // <- 未実装
public:
// 一部の機能は未実装のスケルトン(骨組み)メソッド
void TemplateMethod()
{
printf(
"%s の色は %s ですネ。\n",
Name(), // <- 未実装
Color() // <- 未実装
) ;
}
} ;
// 実装
class Apple : public Kato
{
virtual char * Name() { return "リンゴ" ; } ;
virtual char * Color() { return "赤" ; } ;
} ;
class Banana : public Kato
{
virtual char * Name() { return "バナナ" ; } ;
virtual char * Color() { return "黄色" ; } ;
} ;
int main()
{
Apple apple ;
Banana banana ;
apple.TemplateMethod() ;
banana.TemplateMethod() ;
return 0 ;
}
/* 実行結果
リンゴ の色は 赤 ですネ。
バナナ の色は 黄色 ですネ。
Press any key to continue
*/ |
|
| [振る舞い] TemplateMethod パターン (非継承版) |
|
一部の機能を後で実装する。委譲ではなく、継承を使うのが TemplateMethod のパターン。
TemplateMethod というより SkeletonMethod と呼んだほうが良いよね。
他の似ているパターンの微妙な違い
*
TemplateMethod : 静的な拡張(ルーチンの共通化)が目的
Strategy :
入れ替え可能な状態として作りこむのが目的
State : 頻繁に入れ替えるのが主な目的 |
#include <stdio.h> // printf()
// TemplateMethod パターン
template< class _TemplateMethodPtn >
class Kato
{
// 後で実装(肉付け)するメソッド
_TemplateMethodPtn m_object ; // <- 未実装
public:
// 一部の機能は未実装のスケルトン(骨組み)メソッド
void TemplateMethod()
{
printf(
"%s の色は %s ですネ。\n",
m_object.Name(), // <- 未実装
m_object.Color() // <- 未実装
) ;
}
} ;
// 実装
class Apple
{
public:
char * Name() { return "リンゴ" ; } ;
char * Color() { return "赤" ; } ;
} ;
class Banana
{
public:
char * Name() { return "バナナ" ; } ;
char * Color() { return "黄色" ; } ;
} ;
int main()
{
Kato< Apple > apple ;
Kato< Banana > banana ;
apple.TemplateMethod() ;
banana.TemplateMethod() ;
return 0 ;
}
/* 実行結果
リンゴ の色は 赤 ですネ。
バナナ の色は 黄色 ですネ。
Press any key to continue
*/ |
|
| [振る舞い] Visitor パターン |
|
訪れて処理するというパターン。
データ構造に依存しない処理構造を作るのが目的。
Composite パターンとの相性がいい。 |
#include <stdio.h> // printf()
// Visitorパターン
class AppleShop ; // 具体的な店1
class BananaShop ; // 具体的な店2
class VisitorPtn // 抽象的な訪問者
{
public:
virtual void Visit( AppleShop * in_apple ) = 0 ;
virtual void Visit( BananaShop * in_banana ) = 0 ;
} ;
class AcceptorPtn // 抽象的な店
{
public:
virtual void Accept( VisitorPtn * in_visitor ) = 0 ;
} ;
// 実装
// お店を実装
class AppleShop : public AcceptorPtn
{
public:
virtual void Accept( VisitorPtn* in_visitor )
{
in_visitor->Visit( this ) ;
}
} ;
class BananaShop : public AcceptorPtn
{
public:
virtual void Accept( VisitorPtn* in_visitor )
{
in_visitor->Visit( this ) ;
}
} ;
// お客さん(訪問者)を実装
class Hirosue : public VisitorPtn
{
public:
virtual void Visit( AppleShop * in_apple )
{
puts( "リンゴは1つ下さいな。" ) ;
}
public:
virtual void Visit( BananaShop * in_banana )
{
puts( "バナナはいらないんです。" ) ;
}
} ;
class Honjyo : public VisitorPtn
{
public:
virtual void Visit( AppleShop * in_apple )
{
puts( "リンゴを2つくれへん?。" ) ;
}
public:
virtual void Visit( BananaShop * in_banana )
{
puts( "バナナはあるだけ全部もらっとくわ。" ) ;
}
} ;
// 買い物する町を実装
int main()
{
// お店4つ
AcceptorPtn * tbl[4] =
{
new AppleShop,
new BananaShop,
new AppleShop,
new BananaShop,
} ;
// 2人が順番に4つの店に訪問する
Hirosue ryoko ;
Honjyo manami ;
const int tbl_size = sizeof(tbl)/sizeof(tbl[0]) ;
{ for ( int i=0 ; i<tbl_size ; ++ i )
{
tbl[i]->Accept( & ryoko ) ;
tbl[i]->Accept( & manami ) ;
}}
// 店を閉める
{ for ( int i=0 ; i<tbl_size ; ++ i )
{
delete tbl[i] ;
}}
return 0 ;
}
/* 実行結果
リンゴは1つ下さいな。
リンゴを2つくれへん?。
バナナはいらないんです。
バナナはあるだけ全部もらっとくわ。
リンゴは1つ下さいな。
リンゴを2つくれへん?。
バナナはいらないんです。
バナナはあるだけ全部もらっとくわ。
Press any key to continue
*/ |
|
| [振る舞い] Visitor パターン (非継承版) |
|
訪れて処理するというパターン。
データ構造に依存しない処理構造を作るのが目的。
Composite パターンとの相性がいい。 |
#include <stdio.h> // printf()
// 実装
// お店を実装
class AppleShop
{
public:
template< class _VisitorPtn >
void Accept( _VisitorPtn & in_visitor )
{
in_visitor.Visit( this ) ;
}
} ;
class BananaShop
{
public:
template< class _VisitorPtn >
void Accept( _VisitorPtn & in_visitor )
{
in_visitor.Visit( this ) ;
}
} ;
// お客さん(訪問者)を実装
class Hirosue
{
public:
void Visit( AppleShop * in_apple )
{
puts( "リンゴは1つ下さいな。" ) ;
}
public:
void Visit( BananaShop * in_banana )
{
puts( "バナナはいらないんです。" ) ;
}
} ;
class Honjyo
{
public:
void Visit( AppleShop * in_apple )
{
puts( "リンゴを2つくれへん?。" ) ;
}
public:
void Visit( BananaShop * in_banana )
{
puts( "バナナはあるだけ全部もらっとくわ。" ) ;
}
} ;
// 買い物する町を実装
int main()
{
// お店4つ(template だと、テーブルに出来ないのが悲しいね。)
AppleShop shop_1 ;
BananaShop shop_2 ;
AppleShop shop_3 ;
BananaShop shop_4 ;
// 2人が順番に4つの店に訪問する
Hirosue ryoko ;
Honjyo manami ;
// テーブルでないので、ループにもできない。
// でも、構造は分かりやすくなっているかも。
shop_1.Accept( ryoko ) ; shop_1.Accept( manami ) ;
shop_2.Accept( ryoko ) ; shop_2.Accept( manami ) ;
shop_3.Accept( ryoko ) ; shop_3.Accept( manami ) ;
shop_4.Accept( ryoko ) ; shop_4.Accept( manami ) ;
return 0 ;
}
/* 実行結果
リンゴは1つ下さいな。
リンゴを2つくれへん?。
バナナはいらないんです。
バナナはあるだけ全部もらっとくわ。
リンゴは1つ下さいな。
リンゴを2つくれへん?。
バナナはいらないんです。
バナナはあるだけ全部もらっとくわ。
Press any key to continue
*/ |
|
| [生成] AbstractFactory パターン |
|
| 抽象的な製品を作る抽象的な工場。 |
#include <stdio.h> // puts()
// AbstractFactory パターン
class ProductPtn // 抽象的な製品
{
public:
virtual void Name_Put() = 0 ; // 製品の共通機能
} ;
class AbstractFactoryPtn // 抽象的な工場
{
public:
virtual ProductPtn * Create() = 0 ; // 製品の製造
virtual void Name_Put() = 0 ; // 工場の共通機能
} ;
// 具体的な製品を実装
class Apple : public ProductPtn
{
public:
virtual void Name_Put() // 製品の共通機能
{
printf( "リンゴ" ) ;
}
} ;
class Banana : public ProductPtn
{
public:
virtual void Name_Put() // 製品の共通機能
{
printf( "バナナ" ) ;
}
} ;
// 具体的な工場を実装
class AppleFactory : public AbstractFactoryPtn
{
public:
virtual ProductPtn * Create() // 製品の製造
{
return new Apple ;
}
public:
virtual void Name_Put() // 工場の共通機能
{
printf( "リンゴ工場" ) ;
}
} ;
class BananaFactory : public AbstractFactoryPtn
{
public:
virtual ProductPtn * Create() // 製品の製造
{
return new Banana ;
}
public:
virtual void Name_Put() // 工場の共通機能
{
printf( "バナナ工場" ) ;
}
} ;
// 製品や工場に対して抽象的なコードが書ける。
// 製品や工場の具体的な仕様を知らなくてもよい。
void sub( AbstractFactoryPtn * in_factory )
{
printf( "「" ) ;
in_factory->Name_Put() ; // 工場の共通機能
printf( "」で作る製品は「" ) ;
{
ProductPtn * fruit = in_factory->Create() ; // 製品の製造
fruit->Name_Put() ; // 製品の共通機能
delete fruit ; // 製品の破棄
}
printf( "」です。\n" ) ;
}
int main()
{
AppleFactory apple_factory ; // リンゴ工場を作成
BananaFactory banana_factory ; // バナナ工場を作成
sub( & apple_factory ) ;
sub( & banana_factory ) ;
return 0 ;
}
/* 実行結果
「リンゴ工場」で作る製品は「リンゴ」です。
「バナナ工場」で作る製品は「バナナ」です。
Press any key to continue
*/ |
|
| [生成] AbstractFactory パターン (非継承版) |
|
| 抽象的な製品を作る抽象的な工場。 |
#include <stdio.h> // puts()
// 具体的な製品を実装
class Apple
{
public:
void Name_Put() // 製品の共通機能
{
printf( "リンゴ" ) ;
}
} ;
class Banana
{
public:
void Name_Put() // 製品の共通機能
{
printf( "バナナ" ) ;
}
} ;
// 具体的な工場を実装
class AppleFactory
{
Apple m_apple ;
public:
Apple & Create() // 製品の製造
{
return m_apple ;
}
public:
void Name_Put() // 工場の共通機能
{
printf( "リンゴ工場" ) ;
}
} ;
class BananaFactory
{
Banana m_banana ;
public:
Banana & Create() // 製品の製造
{
return m_banana ;
}
public:
void Name_Put() // 工場の共通機能
{
printf( "バナナ工場" ) ;
}
} ;
// 製品や工場に対して抽象的なコードが書ける。
// 製品や工場の具体的な仕様を知らなくてもよい。
template< class _AbstractFactoryPtn >
void sub( _AbstractFactoryPtn & in_factory )
{
printf( "「" ) ;
in_factory.Name_Put() ; // 工場の共通機能
printf( "」で作る製品は「" ) ;
{
in_factory.Create().Name_Put() ; // 製品の製造&共通機能
}
printf( "」です。\n" ) ;
}
int main()
{
AppleFactory apple_factory ; // リンゴ工場を作成
BananaFactory banana_factory ; // バナナ工場を作成
sub( apple_factory ) ;
sub( banana_factory ) ;
return 0 ;
}
/* 実行結果
「リンゴ工場」で作る製品は「リンゴ」です。
「バナナ工場」で作る製品は「バナナ」です。
Press any key to continue
*/ |
|
| [生成] Builder パターン |
|
| 生成過程を共通化できる。Facade パターンとも似ているが、こっちは生成過程に特化しているらしい。 |
#include <stdio.h> // puts()
#include <string> // STL std::string
// Builder パターン
class BuilderPtn
{
public:
std::string m_result_str ; // 結果
// 共通化された生成過程
virtual void Step1() = 0 ;
virtual void Step2() = 0 ;
virtual void Step3() = 0 ;
virtual void Step4() = 0 ;
} ;
// 実装
class Apple : public BuilderPtn
{
public:
virtual void Step1() { m_result_str += "リンゴを洗う\n" ; }
virtual void Step2() { m_result_str += "ナイフで6つに切る\n" ; }
virtual void Step3() { m_result_str += "ナイフで皮をむく\n"; }
virtual void Step4() { m_result_str += "食べる「パクパク」" ; }
} ;
class Banana : public BuilderPtn
{
public:
virtual void Step1() {} // 具体的な作業無し
virtual void Step2() {} // 具体的な作業無し
virtual void Step3() { m_result_str += "バナナの皮をむく\n" ; }
virtual void Step4() { m_result_str += "食べる「ムシャムシャ」" ; }
} ;
void sub( BuilderPtn * in_builder )
{
puts( "あー、おなかすいた。" ) ;
// 生成過程を共通化できる。
in_builder->Step1() ;
in_builder->Step2() ;
in_builder->Step3() ;
in_builder->Step4() ;
// 生成結果を表示
puts( in_builder->m_result_str.c_str() ) ;
puts( "あー、おいしかった。\n" ) ;
}
int main()
{
Apple apple ;
Banana banana ;
sub( & apple ) ;
sub( & banana ) ;
return 0 ;
}
/* 実行結果
あー、おなかすいた。
リンゴを洗う
ナイフで6つに切る
ナイフで皮をむく
食べる「パクパク」
あー、おいしかった。
あー、おなかすいた。
バナナの皮をむく
食べる「ムシャムシャ」
あー、おいしかった。
Press any key to continue
*/ |
|
| [生成] Builder パターン (非継承版) |
|
| 生成過程を共通化できる。 Facade パターンとも似ているが、こっちは生成過程に特化しているらしい。 |
#include <stdio.h> // puts()
#include <string> // STL std::string
// 実装
class Apple
{
public:
std::string m_result_str ; // 結果
void Step1() { m_result_str += "リンゴを洗う\n" ; }
void Step2() { m_result_str += "ナイフで6つに切る\n" ; }
void Step3() { m_result_str += "ナイフで皮をむく\n"; }
void Step4() { m_result_str += "食べる「パクパク」" ; }
} ;
class Banana
{
public:
std::string m_result_str ; // 結果
void Step1() {} // 具体的な作業無し
void Step2() {} // 具体的な作業無し
void Step3() { m_result_str += "バナナの皮をむく\n" ; }
void Step4() { m_result_str += "食べる「ムシャムシャ」" ; }
} ;
template< class _BuilderPtn >
void sub( _BuilderPtn & in_builder )
{
puts( "あー、おなかすいた。" ) ;
// 生成過程を共通化できる。
in_builder.Step1() ;
in_builder.Step2() ;
in_builder.Step3() ;
in_builder.Step4() ;
// 生成結果を表示
puts( in_builder.m_result_str.c_str() ) ;
puts( "あー、おいしかった。\n" ) ;
}
int main()
{
Apple apple ;
Banana banana ;
sub( apple ) ;
sub( banana ) ;
return 0 ;
}
/* 実行結果
あー、おなかすいた。
リンゴを洗う
ナイフで6つに切る
ナイフで皮をむく
食べる「パクパク」
あー、おいしかった。
あー、おなかすいた。
バナナの皮をむく
食べる「ムシャムシャ」
あー、おいしかった。
Press any key to continue
*/ |
|
| [生成] FactoryMethod パターン |
|
インスタンスの生成は派生クラスに任せる。
FactoryMethod は TemplateMethod から呼ばれる簡単な生成パターン。 |
#include <stdio.h> // puts()
// FactoryMethod パターン
class ProductPtn // 抽象的な製品
{
public:
virtual void Name_Put() = 0 ; // 製品の共通機能
} ;
class FactoryMethodPtn // 工場パターン
{
// 製品のインスタンス生成を分離するところが FactoryMethod の特徴?
virtual ProductPtn * FactoryMethod() = 0 ; // <- 未実装
public:
void TemplateMethod() // 工場の製品紹介(骨組み)
{
printf( "今年は「" ) ;
{
ProductPtn * product = FactoryMethod() ; // <- 未実装
product->Name_Put() ; // 製品の共通機能
delete product ;
}
printf( "」がおいしいですよ。\n" ) ;
}
} ;
// 具体的な製品の実装
class Apple : public ProductPtn
{
public:
virtual void Name_Put() // 製品の共通機能
{
printf( "リンゴ" ) ;
}
} ;
class Banana : public ProductPtn
{
public:
virtual void Name_Put() // 製品の共通機能
{
printf( "バナナ" ) ;
}
} ;
// 具体的な工場の実装
class AppleFactory : public FactoryMethodPtn // リンゴ工場
{
public:
virtual ProductPtn * FactoryMethod() // 製品のインスタンス生成
{
// 個人的には new して返すのは嫌いなんだけど。
return new Apple() ;
}
} ;
class BananaFactory : public FactoryMethodPtn // バナナ工場
{
public:
virtual ProductPtn * FactoryMethod() // 製品のインスタンス生成
{
// 個人的には new して返すのは嫌いなんだけど。
return new Banana() ;
}
} ;
int main()
{
AppleFactory apple_factory ; // リンゴ工場
BananaFactory banana_factory ; // バナナ工場
apple_factory.TemplateMethod() ; // 工場の製品紹介
banana_factory.TemplateMethod() ; // 工場の製品紹介
return 0 ;
}
/* 実行結果
今年は「リンゴ」がおいしいですよ。
今年は「バナナ」がおいしいですよ。
Press any key to continue
*/ |
|
| [生成] FactoryMethod パターン (非継承版) |
|
インスタンスの生成は派生クラスに任せる。
FactoryMethod は TemplateMethod から呼ばれる簡単な生成パターン。 |
#include <stdio.h> // puts()
template< class _ProductPtn >
class FactoryMethodPtn // 工場パターン
{
public:
void TemplateMethod() // 工場の製品紹介(骨組み)
{
printf( "今年は「" ) ;
{
_ProductPtn product ; // <- 未実装
product.Name_Put() ; // 製品の共通機能
}
printf( "」がおいしいです。\n" ) ;
}
} ;
// 具体的な製品の実装
class Apple
{
public:
void Name_Put() // 製品の共通機能
{
printf( "リンゴ" ) ;
}
} ;
class Banana
{
public:
void Name_Put() // 製品の共通機能
{
printf( "バナナ" ) ;
}
} ;
int main()
{
FactoryMethodPtn< Apple > apple_factory ; // リンゴ工場
FactoryMethodPtn< Banana > banana_factory ; // バナナ工場
apple_factory.TemplateMethod() ; // 工場の製品紹介
banana_factory.TemplateMethod() ; // 工場の製品紹介
return 0 ;
}
/* 実行結果
今年は「リンゴ」がおいしいです。
今年は「バナナ」がおいしいです。
ress any key to continue
*/ |
|
| [生成] Prototype パターン |
|
クローンを作る。「クローンパターン」と言ったほうがわかりやすいのにね。
あらかじめ試作品を作っておいて、必要な時にコピー(複製)するパターン。 |
#include <stdio.h> // printf()
// Prototype パターン
class PrototypePtn // クローンメソッドがPrototypeの特徴?
{
public:
virtual PrototypePtn * Clone() = 0 ; // クローンメソッド
// 共通機能?(もっと複雑な設定値があると思われ)
char m_value ; // 値
} ;
// 具体的な実装
class Apple : public PrototypePtn
{
public:
Apple()
{
m_value = 'a' ;
}
virtual PrototypePtn * Clone() // クローン
{
Apple * prototype = new Apple ;
prototype->m_value = m_value ; // 値
return prototype ;
}
} ;
class Banana : public PrototypePtn
{
public:
Banana()
{
m_value = 'b' ;
}
virtual PrototypePtn * Clone() // クローン
{
Banana * prototype = new Banana ;
prototype->m_value = m_value ; // 値
return prototype ;
}
} ;
int main()
{
Apple apple ;
Banana banana ;
#define MAX 8
PrototypePtn * box[MAX][MAX] ; // 箱
// 箱に並べる
{
for ( int y=0 ; y<MAX ; ++y )
{
for ( int x=0 ; x<MAX ; ++x )
{
box[y][x] = (x + y) % 3 ? apple.Clone() : banana.Clone() ;
}
// 途中から種類を変えてみたりして、^^;
if ( y == 2 ) apple.m_value = 'A' ;
if ( y == 4 ) banana.m_value = 'B' ;
}
}
// 箱の中身を表示
{
for ( int y=0 ; y<MAX ; ++y )
{
for ( int x=0 ; x<MAX ; ++x )
{
printf( " %c", box[y][x]->m_value ) ;
}
printf( "\n" ) ;
}
}
// 破棄
{
for ( int y=0 ; y<MAX ; ++y )
{
for ( int x=0 ; x<MAX ; ++x )
{
delete box[y][x] ;
}
}
}
#undef MAX
return 0 ;
}
/* 実行結果
b a a b a a b a
a a b a a b a a
a b a a b a a b
b A A b A A b A
A A b A A b A A
A B A A B A A B
B A A B A A B A
A A B A A B A A
Press any key to continue
*/ |
|
| [生成] Prototype パターン (非継承版) |
|
クローンを作る。「クローンパターン」と言ったほうがわかりやすいのにね。
あらかじめ試作品を作っておいて、必要な時にコピー(複製)するパターン。 |
#include <stdio.h> // printf()
// Prototype パターン
class PrototypePtn // クローンメソッド無しで実装してみたりして^^;
{
public:
// 共通機能?(もっと複雑な設定値があると思われ)
char m_value ; // 値
} ;
// 具体的な実装
int main()
{
PrototypePtn apple ; apple.m_value = 'a' ;
PrototypePtn banana ; banana.m_value = 'b' ;
#define MAX 8
PrototypePtn box[MAX][MAX] ; // 箱
// 箱に並べる
{
for ( int y=0 ; y<MAX ; ++y )
{
for ( int x=0 ; x<MAX ; ++x )
{
// C++ なら普通にコピーしても問題無いよね。^^;
box[y][x] = (x + y) % 3 ? apple : banana ;
}
// 途中から種類を変えてみたりして、^^;
if ( y == 2 ) apple.m_value = 'A' ;
if ( y == 4 ) banana.m_value = 'B' ;
}
}
// 箱の中身を表示
{
for ( int y=0 ; y<MAX ; ++y )
{
for ( int x=0 ; x<MAX ; ++x )
{
printf( " %c", box[y][x].m_value ) ;
}
printf( "\n" ) ;
}
}
#undef MAX
return 0 ;
}
/* 実行結果
b a a b a a b a
a a b a a b a a
a b a a b a a b
b A A b A A b A
A A b A A b A A
A B A A B A A B
B A A B A A B A
A A B A A B A A
Press any key to continue
*/ |
|
| [生成] Singleton パターン |
|
インスタンスを一つしか生成しないパターン。
場合によってはインスタンスの数を制限するのにも使う。
わりとよく使われているパターンだと思う。 |
#include <stdio.h> // puts()
// 既存のクラス
class Apple
{
int m_cnt ; // 総数
public:
Apple() : m_cnt( 6 ) {} // 総数6個
void Eat( int in )
{
if ( m_cnt >= in )
{
m_cnt -= in ;
printf( "%d 個、食べました。残りは %d 個です。\n", in, m_cnt ) ;
}
else
{
printf( "%d 個ありません。残りは %d 個です。\n", in, m_cnt ) ;
}
}
} ;
// Singleton パターン
class SingletonPtn
{
static Apple * m_apple ; // インスタンスは一つ
SingletonPtn() {} // <- このクラスのインスタンス化を抑止
public:
static Apple* Instance_Get()
{
// Apple を1つしか作らないのが Singleton の特徴
if ( m_apple == 0 )
{
m_apple = new Apple() ;
}
return m_apple ;
}
static void Instance_Delete()
{
if ( m_apple != 0 )
{
delete m_apple ;
m_apple = 0 ;
}
}
} ;
Apple* SingletonPtn::m_apple = 0 ; // インスタンスは一つ
int main()
{
Apple * ryoko = SingletonPtn::Instance_Get() ;
Apple * manami = SingletonPtn::Instance_Get() ;
Apple * ai = SingletonPtn::Instance_Get() ;
ryoko ->Eat( 1 ) ;
manami->Eat( 2 ) ;
ai ->Eat( 1 ) ;
ryoko ->Eat( 1 ) ;
manami->Eat( 2 ) ;
ai ->Eat( 1 ) ;
SingletonPtn::Instance_Delete() ;
return 0 ;
}
/* 実行結果
1 個、食べました。残りは 5 個です。
2 個、食べました。残りは 3 個です。
1 個、食べました。残りは 2 個です。
1 個、食べました。残りは 1 個です。
2 個ありません。残りは 1 個です。
1 個、食べました。残りは 0 個です。
Press any key to continue
*/ |
|
| [生成] Singleton パターン (非継承版) |
|
インスタンスを一つしか生成しないパターン。
場合によってはインスタンスの数を制限するのにも使う。
わりとよく使われているパターンだと思う。 |
#include <stdio.h> // puts()
// 既存のクラス
class Apple
{
int m_cnt ; // 総数
public:
Apple() : m_cnt( 6 ) {} // 総数6個
void Eat( int in )
{
if ( m_cnt >= in )
{
m_cnt -= in ;
printf( "%d 個、食べました。残りは %d 個です。\n", in, m_cnt ) ;
}
else
{
printf( "%d 個ありません。残りは %d 個です。\n", in, m_cnt ) ;
}
}
} ;
// Singleton パターン なんて使わなくても、
// リファレンスを使えばよいような気も。ダメ?^^;
int main()
{
Apple apple ; // インスタンスは1つ
Apple & ryoko = apple ;
Apple & manami = apple ;
Apple & ai = apple ;
ryoko .Eat( 1 ) ;
manami.Eat( 2 ) ;
ai .Eat( 1 ) ;
ryoko .Eat( 1 ) ;
manami.Eat( 2 ) ;
ai .Eat( 1 ) ;
return 0 ;
}
/* 実行結果
1 個、食べました。残りは 5 個です。
2 個、食べました。残りは 3 個です。
1 個、食べました。残りは 2 個です。
1 個、食べました。残りは 1 個です。
2 個ありません。残りは 1 個です。
1 個、食べました。残りは 0 個です。
Press any key to continue
*/ |
|
last update 2003/03/28
since 2001/12/11
|
| やまざきのおすすめエレクトロニクス |
|
| やまざきのおすすめ本 |
|
| やまざきのおすすめDVD |
|
| やまざきのおすすめCD |
|
|