Strategy パターン(ストラテジーパターン)は、アルゴリズムを共通のインターフェースを持つ独立したクラスとして定義し、実行時に交換可能にすることで、処理内容の変更を利用側のコードから独立させるデザインパターンである。これにより、複数のアルゴリズムを状況に応じて切り替えながら、利用側の構造を変更せずに拡張や保守を行えるようになる。
Strategy パターンを示す図 500px|thumb|none|UML で表した Strategy パターン
サンプルコード
Java Javaではクラスのメソッドオーバーライドによるポリモーフィズムを使ってStrategyパターンを実現することができる。インターフェイスを用いた例を示す。 package org.wikipedia.patterns.strategy;
// MainApp test application class MainApp { public static void main(String[] args) { Context context;
// 異なるアルゴリズムに従う3つのコンテキスト。 context = new Context(new ConcreteStrategyA()); context.execute();
context = new Context(new ConcreteStrategyB()); context.execute();
context = new Context(new ConcreteStrategyC()); context.execute();
// 具体的な戦略を実装するクラスは、このインターフェイスを実装する。 // コンテキストクラスは、具体的な戦略を呼び出すためにこのインターフェイスを使用する。 interface Strategy { void execute(); }
// Strategy インターフェイスを用いたアルゴリズムの実装。 class ConcreteStrategyA implements Strategy { public void execute() { System.out.println("Called ConcreteStrategyA.execute()");
class ConcreteStrategyB implements Strategy { public void execute() { System.out.println("Called ConcreteStrategyB.execute()");
class ConcreteStrategyC implements Strategy { public void execute() { System.out.println("Called ConcreteStrategyC.execute()");
// ConcreteStrategy を指定して作成され、Strategy オブジェクトへの参照を保持する。 class Context { Strategy strategy;
// Constructor public Context(Strategy strategy) { this.strategy = strategy;
public void execute() { 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同様にクラスやインターフェイスによるポリモーフィズムを用いることもできるが、カスタマイズポイントがひとつのメソッドしかない場合(オブジェクトの他のプロパティやメソッドにアクセスしない場合)は、継承関係を必要としないデリゲートを使うほうが好まれるWhen to Use Delegates Instead of Interfaces (C# Programming Guide) | Microsoft Docs。 using System;
// MainApp テストアプリケーション。 public class MainApp { public static void Main()
Context context;
// 異なるアルゴリズムに従う3つのコンテキスト。 context = new Context(new ConcreteStrategyA().Execute); context.Execute();
context = new Context(new ConcreteStrategyB().Execute); context.Execute();
context = new Context(new ConcreteStrategyC().Execute); context.Execute();
// 具体的な戦略を実装するクラスは、このデリゲートに適合するメソッドを実装する。 // コンテキストクラスは、具体的な戦略を呼び出すためにこのデリゲートを使用する。 public delegate void ExecuteStrategyDelegate();
class ConcreteStrategyA { public void Execute()
Console.WriteLine("Called ConcreteStrategyA.Execute()");
class ConcreteStrategyB { public void Execute()
Console.WriteLine("Called ConcreteStrategyB.Execute()");
class ConcreteStrategyC { public void Execute()
Console.WriteLine("Called ConcreteStrategyC.Execute()");
// ExecuteStrategyDelegate オブジェクトへの参照を保持する。 class Context { ExecuteStrategyDelegate executeStrategy;
// Constructor public Context(ExecuteStrategyDelegate executeStrategy)
this.executeStrategy = executeStrategy;
public void Execute()
this.executeStrategy();
なお、Javaもバージョン8以降であれば、メソッド参照と関数型インターフェイス (functional interface) を用いることで、C#と類似の実装が可能となる。 package org.wikipedia.patterns.strategy;
// MainApp test application class MainApp { public static void main(String[] args) { // Strategy インターフェイスを用いたアルゴリズムの実装。 // 元々はConcreteStrategyとして明示的に定義していたが、必要なくなっている。 Strategy concreteStrategyA = ()-> System.out.println("Called ConcreteStrategyA.execute()"); Strategy concreteStrategyB = ()-> System.out.println("Called ConcreteStrategyB.execute()"); Strategy concreteStrategyC = ()-> System.out.println("Called ConcreteStrategyC.execute()");
Context context;
// 異なるアルゴリズムに従う3つのコンテキスト。 context = new Context(concreteStrategyA); context.execute();
context = new Context(concreteStrategyB); context.execute();
context = new Context(concreteStrategyC); context.execute();
// 具体的な戦略を実装するクラスは、このインターフェイスを実装する。 // コンテキストクラスは、具体的な戦略を呼び出すためにこのインターフェイスを使用する。 interface Strategy { void execute(); }
// ConcreteStrategy を指定して作成され、Strategy オブジェクトへの参照を保持する。 class Context { Strategy strategy;
// Constructor public Context(Strategy strategy) { this.strategy = strategy;
public void execute() { this.strategy.execute();
Strategy パターンと開放/閉鎖原則 Strategy パターンに従うと、クラスの振る舞いは継承されるべきではなく、インターフェイスを用いてカプセル化するべきである。例として Car クラスを考えると、Car の振る舞いにはブレーキとアクセルがある。
アクセルとブレーキの振る舞いは車種により異なる場合があるため、良くあるやり方はこれらの振る舞いをCarのサブクラスとして実装することである。が、このやり方には大きな問題点がある。それはアクセルとブレーキの振る舞いが車種間で同じでも、車種ごとに新たに宣言・定義し直されなければならない事である。これは車種が少ないときには小さな問題で済むが、車種が増えるにつれ、それらの振る舞いを管理する作業とコード重複量が大幅に増えてしまうことになる。さらに、各コードを詳しく分析しなければ各車種の振る舞いの性質を知ることができない。
これに対して Strategy パターンでは、継承ではなく合成 (composition) を用いる。Strategy パターンにおける振る舞いは別々のインターフェイスと、これらのインターフェイスを実装した抽象クラスとして定義される。具体的なクラスは、これらのインターフェイスをカプセル化する。これにより、振る舞いと、それを用いるクラスがうまく分離できる。振る舞いは、それを用いるクラスに変更を加えずに変更することができ、クラスは大きなコード変更を必要とすることなく、使用する実装を切り替えることで振る舞いを切り替えることができる。振る舞いは設計時にも実行時にも変更することができる。例として、Car オブジェクトのブレーキの振る舞いを、メンバー brakeBehavior を BrakeWithABS から Brake に変えることで変更できる:
brakeBehavior = new Brake();
ファイル:Strategy.SVG
これにより設計に優れた柔軟性をもたせることができ、かつ拡張に対して開放的であり変更に対して閉鎖的であるべきとする開放/閉鎖原則 (Open/Closed Principle, OCP) とも調和を保つことができる。
脚注
関連項目 ミックスイン :en:Policy-based design :en:First-class function Template Method パターン Bridge パターン 開放/閉鎖原則 Factory パターン :en:List of object-oriented programming terms (オブジェクト指向の用語一覧)
外部リンク Strategy Pattern for Java article Data & object factory Refactoring: Replace Type Code with State/Strategy Jt J2EE Pattern Oriented Framework Strategy Pattern with a twist!