機能
振る舞いを柔軟に変更する
もうちょっと抽象的に言えば
アルゴリズムをカプセル化して交換可能にする。
実装例
// 振る舞いを変えたいクラス class Model{ // 振る舞いを移譲する BehaviorInterface behave; public function perform(){ behave->perform(); } } // 振る舞いをインターフェースで持つことで実行時オブジェクトの詳細を知る必要がなくなる interface BehaviorInterface{ void perform(); } // 実装は派生先で行う class ConcreteBehave implements BehaviorInterface{ public function perform(){ ... } }
検討
振る舞いを替えるなら多態性を使って実装もできる。その方が簡潔な場合もあると思う。
例えば動物に対して、
class Animal{ abstract public void shout(); } class Dog extends Animal{ public void shout(){ wan(); } } class Cat extends Animal{ public void shout(){ nya(); } }
この場合犬の種類とか興味がなければこの方がいいと思う。犬や猫の間で振る舞いを変えることもほぼないだろうし。
でもそれでは難しい場合がある。例えばゲームのキャラクターの場合武器を装備させる場合どうするか、もちろん武器はたくさんある。剣もあれば斧、杖、弓、槍・・。
普通に考えて武器は持ち替えるものだしまさにStrategyパターンそのもの!
class Character{ Weapon weapon; public void setWeapon(Weapon w){ weapon = w; } public void attack(){ weapon.attack(); } } interface Weapon{ public void attack(); } class Sword implements Weapon{ public void attack(){ gusa(); } } class Ax implements Weapon{ public void attack(){ doka(); } }
他の例としてメモリーアロケーターもStrategyパターンが使える。
ライブラリを用意する時勝手気ままにメモリを確保できない場合がある。この場合ライブラリ側ではメモリーアロケーターからメモリを確保するようにしておく。クライアントは実行時にアロケーターを設定しておくことで自分でメモリの管理を行うことができる。
// クライアントはこのクラスを継承したクラスを実装する class MemoryAllocator{ virtual void* alloc(int size); virtual void free(const void* buffer); } // ライブラリの中ではこのクラスを通してメモリを確保する // クライアントは実行時にアロケーターを渡しておく class MemoryManager : MemoryAllocator{ MemoryAllcator* _allocator; MemoryManager(allocator){ _allocator = allocator; } void* alloc(int size){ return _allocator->alloc(size); } void free(const void* buffer){ _allocator->free(buffer); } }
では犬猫との違いはなんだろうか?
犬や猫のように動物の種類にフォーカスが当たっている時はの鳴き声は一般的なものだけを考えればいいだろうから多態性を使った方がシンプルだ。
でも犬が中心の話で「クーン」とか「ガルル」みたいな鳴き声になったりするような場合はstrategyパターンの方がいい。
重要なのは変化する部分はどこかということ。
他にもいろんなところでStrategyパターンが使えると思うで考えてみよう!!
車のタイヤ(ノーマル、スタッドレス、オフロード・・)とかカーテン(遮光性、保温性・・)とか。
ではでは、また。