Strategyパターン
Strategyパターンは一連の戦略を定義してそれぞれをカプセル化し、それらを交換可能にします。
Strategyパターンによって、戦略を使用するクライアントとは独立して、戦略を変更可能になります。
Strategyパターン構成図
- Strategy(戦略)の役
- 戦略を利用するためのインタフェースの役
- コード例では、QuackBehaviorクラスが相当
- ConcreateStrategy (具体戦略)の役
- Strategy役のインタフェースを実際に実装する役。ここで具体的な戦略(作戦・方策・方法・アルゴリズム)を実際にプログラミングします。
- コード例では、Quack、Squak、MuteQuackクラスが相当
- Context(文脈)の役
- Strategy役を利用する役。ConcreateStrategy役のインスタンスを持っていて、必要に応じでそれを利用します。利用する場合は、必ずインタフェースを使用してください。
- コード例ではDuck、RedHeadDuck、RubberDuck、DecoyDuckクラスが相当
Strategyパターンのアイディアは、Context(文脈)が「委譲」によってアルゴリズム(ConcreateStrategy (具体戦略))を交換できるようにすることです。
委譲とは、ある機能をもつオブジェクトを生成してオブジェクトに処理を依頼することです。
(Strategyパターン構成図では、Contextクラスのstrategyメンバ変数が相当します)
コード例
アヒルの種別ごとに、アヒルの鳴き方(振舞い)を動的に変更します。
コード例のクラス図
例えば、無口なアメリカホシハジロがいた場合は、クラス内のコンストラクタで生成するインスタンスのクラスをQuackクラスからMuteQuackクラスに置き換えるだけで可能です。
[無口になるアメリカホシハジロ]
/// <summary>
/// 無口なアメリカホシハジロ
/// </summary>
public class RedHeadDuck : Duck
{
public RedHeadDuck()
{
cry = new MuteQuack();
}
}
コード例[C#]
using System.Collections.Generic;
namespace Strategy_Pattern_CSharp
{
class Program
{
static void Main(string[] args)
{
// 通常のアヒルを生成
Duck duck = new Duck(new SmallQuack());
// アメリカホシハジロを生成
Duck redHead = new RedHeadDuck(new Quack());
// ゴムのアヒルを生成
Duck rubber = new RubberDuck(new Squeak());
// 偽物のアヒルを生成
Duck decoy = new DecoyDuck(new MuteQuack());
// アヒルの鳴き声
duck.quack();
// アメリカホシハジロの鳴き声
redHead.quack();
// ゴムのアヒルの鳴き声
rubber.quack();
// 偽物のアヒルの鳴き声
decoy.quack();
System.Console.WriteLine("------------");
// リストに格納して実行することもできます
List<Duck> duckList = new List<Duck>();
duckList.Add(duck);
duckList.Add(redHead);
duckList.Add(rubber);
duckList.Add(decoy);
foreach (var item in duckList)
{
item.quack();
}
}
}
/// <summary>
/// 鳴き声インタフェース
/// </summary>
public interface QuackBehavior
{
void sound();
}
/// <summary>
/// 静かな(アヒルなどの)ガーガー鳴き声クラス
/// </summary>
public class SmallQuack : QuackBehavior
{
public void sound()
{
System.Console.WriteLine("Normal sound : kua! kua!");
}
}
/// <summary>
/// (アヒルなどの)ガーガー鳴き声クラス
/// </summary>
public class Quack : QuackBehavior
{
public void sound()
{
System.Console.WriteLine("Noisy Sound : !!!!gua-gua!!!!");
}
}
/// <summary>
/// キュッキュッ鳴る鳴き声クラス
/// </summary>
public class Squeak : QuackBehavior
{
public void sound()
{
System.Console.WriteLine("Cute sound : kyu-kyu");
}
}
/// <summary>
/// 無音鳴き声クラス
/// </summary>
public class MuteQuack : QuackBehavior
{
public void sound()
{
System.Console.WriteLine("No sound : <<mute>>");
}
}
/// <summary>
/// アヒルクラス
/// </summary>
public class Duck
{
/// <summary>
/// 鳴き声
/// </summary>
private QuackBehavior quackBehavior;
public Duck(QuackBehavior behavior)
{
quackBehavior = behavior;
}
/// <summary>
/// 鳴き声を発する
/// </summary>
public void quack()
{
quackBehavior.sound();
}
}
/// <summary>
/// アメリカホシハジロ
/// </summary>
public class RedHeadDuck : Duck
{
public RedHeadDuck(QuackBehavior behavior) : base(behavior)
{
}
}
/// <summary>
/// ゴムのアヒル
/// </summary>
public class RubberDuck : Duck
{
public RubberDuck(QuackBehavior behavior) : base(behavior)
{
}
}
/// <summary>
/// 偽物のアヒル
/// </summary>
public class DecoyDuck : Duck
{
public DecoyDuck(QuackBehavior behavior) : base(behavior)
{
}
}
}
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
------------
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
コード例 [C++]
なお、ポリモーフィズムを利用すべく作った基底クラスのデストラクタはvirtualが必要です。
virtualをつけないと、派生クラスのデストラクタが呼ばれずメモリリークが起きます。
[Duck.hpp]
#include <iostream>
#include <assert.h>
// 鳴き声ふるまいインターフェース
class QuackBehavior
{
public:
virtual ~QuackBehavior() = default;
virtual void sound() const = 0;
};
// 静かな(アヒルなどの)ガーガー鳴き声クラス
class SmallQuack : public QuackBehavior
{
public:
// 派生クラス側のオーバーライドメソッドにはoverride指定子を付ける (オーバーライド元の基底クラスを継承していない場合、コンパイルエラーとなる)
void sound() const override
{
std::cout << "Normal sound : kua! kua!" << std::endl;
}
};
// (アヒルなどの)ガーガー鳴き声クラス
class Quack : public QuackBehavior
{
public:
void sound() const override
{
std::cout << "Noisy Sound : !!!!gua-gua!!!!" << std::endl;
}
};
// キュッキュッ鳴る鳴き声クラス
class Squeak : public QuackBehavior
{
public:
void sound() const override
{
std::cout << "Cute sound : kyu-kyu" << std::endl;
}
};
// 無音鳴き声クラス
class MuteQuack : public QuackBehavior
{
public:
void sound() const override
{
std::cout << "No sound : <<mute>>" << std::endl;
}
};
// Duck(基底クラス)
// QuackBehaviorのインスタンスの解放は、基底クラスであるDuckクラスが受け持つ
class Duck
{
private:
const QuackBehavior * quackBehavior = nullptr;
public:
// 引数なしのコンストラクタは使用させない
Duck() = delete;
// インスタンス変数をコンストラクタで初期化する場合は、コンストラクタ初期化子を使用しましょう
// 引数を1個とるコンストラクタの暗黙呼び出しを禁止するには,コンストラクタを“explicit”と宣言しておく. explicit宣言したコンストラクタは,明示的呼び出し(C obj(10);)でしか呼び出せなくなり, 暗黙呼び出しを記述するとコンパイル時エラーになる.
explicit Duck(QuackBehavior* behavior) : quackBehavior(behavior)
{
// コンストラクト時に渡される behaviorがnullptrであると、メンバ関数quack()呼び出すと落ちるため、assert文を追加する
assert(behavior != nullptr);
}
// ポリモーフィズムを利用すべく作った基底クラスのデストラクタはvirtualが必要である
// でないと、派生クラスのデストラクタが呼ばれない
virtual ~Duck()
{
delete quackBehavior;
std::cout << "Duck Destructed" << std::endl;
};
// quack関数は、派生クラスで、overrideさせないことを明示する
// finalを指定した仮想メンバ関数は、派生クラスにてオーバーライドされたとき、文法違反として扱われる
virtual void quack() const final
{
quackBehavior->sound();
}
};
class RedHeadDuck : public Duck
{
public:
RedHeadDuck(QuackBehavior* behavior) : Duck(behavior){}
~RedHeadDuck()
{
std::cout << "RedHeadDuck Destructed" << std::endl;
}
};
class RubberDuck : public Duck
{
public:
RubberDuck(QuackBehavior* behavior) : Duck(behavior){}
~RubberDuck()
{
std::cout << "RubberDuck Destructed" << std::endl;
}
};
class DecoyDuck : public Duck
{
public:
DecoyDuck(QuackBehavior* behavior) : Duck(behavior){}
~DecoyDuck()
{
std::cout << "DecoyDuck Destructed" << std::endl;
}
};
[Main.cpp]
#include "Duck.hpp"
#include <vector>
#include <memory>
#include <iostream>
void use_shared_ptr()
{
std::unique_ptr<Duck> duck(new Duck(new SmallQuack()));
std::unique_ptr<Duck> redHead(new RedHeadDuck(new Quack()));
std::unique_ptr<Duck> rubber(new RubberDuck(new Squeak()));
std::unique_ptr<Duck> decoy(new DecoyDuck(new MuteQuack()));
duck->quack();
redHead->quack();
rubber->quack();
decoy->quack();
}
void use_shared_ptr_list()
{
std::unique_ptr<Duck> duck(new Duck(new SmallQuack()));
std::unique_ptr<Duck> redHead(new RedHeadDuck(new Quack()));
std::unique_ptr<Duck> rubber(new RubberDuck(new Squeak()));
std::unique_ptr<Duck> decoy(new DecoyDuck(new MuteQuack()));
std::vector<std::unique_ptr<Duck>> ducks;
ducks.push_back(std::move(duck));
ducks.push_back(std::move(redHead));
ducks.push_back(std::move(rubber));
ducks.push_back(std::move(decoy));
// なお、for (auto f : v) とすると f は std::unique_ptrのコピーになり、コンパイルエラーとなります
for (const auto& item : ducks)
{
item->quack();
}
}
void use_shared_ptr_list_using_emplace_back()
{
std::vector<std::unique_ptr<Duck>> ducks;
ducks.emplace_back(new Duck(new SmallQuack()));
ducks.emplace_back(new RedHeadDuck(new Quack()));
ducks.emplace_back(new RubberDuck(new Squeak()));
ducks.emplace_back(new DecoyDuck(new MuteQuack()));
// なお、for (auto f : v) とすると f は std::unique_ptrのコピーになり、コンパイルエラーとなります
for (const auto& item : ducks)
{
item->quack();
}
}
int main()
{
Duck* duck = new Duck(new SmallQuack());
Duck* redHead = new RedHeadDuck(new Quack());
Duck* rubber = new RubberDuck(new Squeak());
Duck* decoy = new DecoyDuck(new MuteQuack());
duck->quack();
redHead->quack();
rubber->quack();
decoy->quack();
// newしているので、deleteを忘れずにすること
delete duck;
delete redHead;
delete rubber;
delete decoy;
std::cout << "---- using std::unique_ptr----" << std::endl;
use_shared_ptr();
std::cout << "---- using std::unique_ptr with list ----" << std::endl;
use_shared_ptr_list();
std::cout << "---- using std::unique_ptr with list & vector::emplace_back ----" << std::endl;
use_shared_ptr_list_using_emplace_back();
return 0;
}
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
Duck Destructed
RedHeadDuck Destructed
Duck Destructed
RubberDuck Destructed
Duck Destructed
DecoyDuck Destructed
Duck Destructed
---- using std::unique_ptr----
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
DecoyDuck Destructed
Duck Destructed
RubberDuck Destructed
Duck Destructed
RedHeadDuck Destructed
Duck Destructed
Duck Destructed
---- using std::unique_ptr with list ----
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
Duck Destructed
RedHeadDuck Destructed
Duck Destructed
RubberDuck Destructed
Duck Destructed
DecoyDuck Destructed
Duck Destructed
---- using std::unique_ptr with list & vector::emplace_back ----
Normal sound : kua! kua!
Noisy Sound : !!!!gua-gua!!!!
Cute sound : kyu-kyu
No sound : <<mute>>
Duck Destructed
RedHeadDuck Destructed
Duck Destructed
RubberDuck Destructed
Duck Destructed
DecoyDuck Destructed
Duck Destructed
参考書籍
- Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
- Java言語で学ぶデザインパターン入門