コンテンツにスキップ
Wikipedia

Strategy パターン

出典: フリー百科事典『ウィキペディア(Wikipedia)』

Strategy パターン(ストラテジー - )は、コンピュータープログラミングの領域において、アルゴリズムを実行時に選択することができるデザインパターンである。

Strategyパターンはアルゴリズムを記述するサブルーチンへの参照をデータ構造の内部に保持する。このパターンの実現には、関数ポインタ関数オブジェクトデリゲートのほか、オーソドックスなオブジェクト指向言語におけるポリモーフィズム委譲、あるいはリフレクションによる動的ダック・タイピングなどが利用される。

このパターンは、関数が第一級オブジェクトである言語では暗黙のうちに使用されている。例として後述のPythonコード例を参照のこと。

Strategy パターンは、アプリケーションで使用されるアルゴリズムを動的に切り替える必要がある際に有用である。Strategy パターンはアルゴリズムのセットを定義する方法を提供し、これらを交換可能にすることを目的としている。Strategy パターンにより、アルゴリズムを使用者から独立したまま様々に変化させることができるようになる。

Strategy パターンを示す図

[編集 ]
UML で表した Strategy パターン

サンプルコード

[編集 ]

Java

[編集 ]

Javaではクラスメソッド オーバーライドによるポリモーフィズムを使ってStrategyパターンを実現することができる。インターフェイスを用いた例を示す。

packageorg.wikipedia.patterns.strategy;
// MainApp test application
class MainApp{
publicstaticvoidmain(String[]args){
Contextcontext;
// 異なるアルゴリズムに従う3つのコンテキスト。
context=newContext(newConcreteStrategyA());
context.execute();
context=newContext(newConcreteStrategyB());
context.execute();
context=newContext(newConcreteStrategyC());
context.execute();
}
}
// 具体的な戦略を実装するクラスは、このインターフェイスを実装する。
// コンテキストクラスは、具体的な戦略を呼び出すためにこのインターフェイスを使用する。
interface Strategy{
voidexecute();
}
// Strategy インターフェイスを用いたアルゴリズムの実装。
class ConcreteStrategyAimplementsStrategy{
publicvoidexecute(){
System.out.println("Called ConcreteStrategyA.execute()");
}
}
class ConcreteStrategyBimplementsStrategy{
publicvoidexecute(){
System.out.println("Called ConcreteStrategyB.execute()");
}
}
class ConcreteStrategyCimplementsStrategy{
publicvoidexecute(){
System.out.println("Called ConcreteStrategyC.execute()");
}
}
// ConcreteStrategy を指定して作成され、Strategy オブジェクトへの参照を保持する。
class Context{
Strategystrategy;
// Constructor
publicContext(Strategystrategy){
this.strategy=strategy;
}
publicvoidexecute(){
this.strategy.execute();
}
}

Python

[編集 ]

Python では関数が第一級オブジェクトであり、このパターンを明示的に定義する必要はない。下記はコールバック関数を用いる GUI プログラミングで見られる例である。

class Button:
"""A very basic button widget."""
 def __init__(self, submit_func, label):
 self.on_submit = submit_func # strategy 関数を直接生成
 self.label = label
# 異なる戦略を持つ2つのインスタンスを作成
button1 = Button(sum, "Add 'em")
button2 = Button(lambda nums: " ".join(map(str, nums)), "Join 'em")
# ボタンをテストする
numbers = range(1, 10) # A list of numbers 1 through 9
print button1.on_submit(numbers) # displays "45"
print button2.on_submit(numbers) # displays "1 2 3 4 5 6 7 8 9"

C#

[編集 ]

C#はJava同様にクラスやインターフェイスによるポリモーフィズムを用いることもできるが、カスタマイズポイントがひとつのメソッドしかない場合(オブジェクトの他のプロパティやメソッドにアクセスしない場合)は、継承関係を必要としないデリゲートを使うほうが好まれる[1]

usingSystem;
// MainApp テストアプリケーション。
publicclassMainApp
{
publicstaticvoidMain()
{
Contextcontext;
// 異なるアルゴリズムに従う3つのコンテキスト。
context=newContext(newConcreteStrategyA().Execute);
context.Execute();
context=newContext(newConcreteStrategyB().Execute);
context.Execute();
context=newContext(newConcreteStrategyC().Execute);
context.Execute();
}
}
// 具体的な戦略を実装するクラスは、このデリゲートに適合するメソッドを実装する。
// コンテキストクラスは、具体的な戦略を呼び出すためにこのデリゲートを使用する。
publicdelegatevoidExecuteStrategyDelegate();
classConcreteStrategyA
{
publicvoidExecute()
{
Console.WriteLine("Called ConcreteStrategyA.Execute()");
}
}
classConcreteStrategyB
{
publicvoidExecute()
{
Console.WriteLine("Called ConcreteStrategyB.Execute()");
}
}
classConcreteStrategyC
{
publicvoidExecute()
{
Console.WriteLine("Called ConcreteStrategyC.Execute()");
}
}
// ExecuteStrategyDelegate オブジェクトへの参照を保持する。
classContext
{
ExecuteStrategyDelegateexecuteStrategy;
// Constructor
publicContext(ExecuteStrategyDelegateexecuteStrategy)
{
this.executeStrategy=executeStrategy;
}
publicvoidExecute()
{
this.executeStrategy();
}
}

なお、Javaもバージョン8以降であれば、メソッド参照と関数型インターフェイス (functional interface) を用いることで、C#と類似の実装が可能となる。

packageorg.wikipedia.patterns.strategy;
// MainApp test application
class MainApp{
publicstaticvoidmain(String[]args){
// Strategy インターフェイスを用いたアルゴリズムの実装。
// 元々はConcreteStrategyとして明示的に定義していたが、必要なくなっている。
StrategyconcreteStrategyA=()->System.out.println("Called ConcreteStrategyA.execute()");
StrategyconcreteStrategyB=()->System.out.println("Called ConcreteStrategyB.execute()");
StrategyconcreteStrategyC=()->System.out.println("Called ConcreteStrategyC.execute()");

Contextcontext;
// 異なるアルゴリズムに従う3つのコンテキスト。
context=newContext(concreteStrategyA);
context.execute();
context=newContext(concreteStrategyB);
context.execute();
context=newContext(concreteStrategyC);
context.execute();
}
}
// 具体的な戦略を実装するクラスは、このインターフェイスを実装する。
// コンテキストクラスは、具体的な戦略を呼び出すためにこのインターフェイスを使用する。
interface Strategy{
voidexecute();
}
// ConcreteStrategy を指定して作成され、Strategy オブジェクトへの参照を保持する。
class Context{
Strategystrategy;
// Constructor
publicContext(Strategystrategy){
this.strategy=strategy;
}
publicvoidexecute(){
this.strategy.execute();
}
}

Strategy パターンと開放/閉鎖原則

[編集 ]

Strategy パターンに従うと、クラスの振る舞いは継承されるべきではなく、インターフェイスを用いてカプセル化するべきである。例として Car クラスを考えると、Car の振る舞いにはブレーキとアクセルがある。

アクセルとブレーキの振る舞いは車種により異なる場合があるため、良くあるやり方はこれらの振る舞いをCarのサブクラスとして実装することである。が、このやり方には大きな問題点がある。それはアクセルとブレーキの振る舞いが車種間で同じでも、車種ごとに新たに宣言・定義し直されなければならない事である。これは車種が少ないときには小さな問題で済むが、車種が増えるにつれ、それらの振る舞いを管理する作業とコード重複量が大幅に増えてしまうことになる。さらに、各コードを詳しく分析しなければ各車種の振る舞いの性質を知ることができない。

これに対して Strategy パターンでは、継承ではなく合成 (composition) を用いる。Strategy パターンにおける振る舞いは別々のインターフェイスと、これらのインターフェイスを実装した抽象クラスとして定義される。具体的なクラスは、これらのインターフェイスをカプセル化する。これにより、振る舞いと、それを用いるクラスがうまく分離できる。振る舞いは、それを用いるクラスに変更を加えずに変更することができ、クラスは大きなコード変更を必要とすることなく、使用する実装を切り替えることで振る舞いを切り替えることができる。振る舞いは設計時にも実行時にも変更することができる。例として、Car オブジェクトのブレーキの振る舞いを、メンバー brakeBehavior を BrakeWithABS から Brake に変えることで変更できる:

brakeBehavior = new Brake(); 

これにより設計に優れた柔軟性をもたせることができ、かつ拡張に対して開放的であり変更に対して閉鎖的であるべきとする開放/閉鎖原則 (Open/Closed Principle, OCP) とも調和を保つことができる。

脚注

[編集 ]

関連項目

[編集 ]

外部リンク

[編集 ]
GoFによる23種のパターン
生成に関するパターン
構造に関するパターン
振る舞いに関するパターン
並行性に関するパターン
アーキテクチャに関するパターン
その他のパターン
関連する人々
関連項目

AltStyle によって変換されたページ (->オリジナル) /