はじめに
本記事は、GoFデザインパターン解説シリーズの1つです。
今回は、振る舞いに関するパターンの中のStrategy(ストラテジー)パターンを解説します。
実務でも非常によく使われる、シンプルで応用範囲の広いパターインです。
解決したい課題
「同じ処理の中で、アルゴリズム(処理方法)だけを状況に応じて切り替えたい」という場面があります。
例えば、ECサイトの「配送料金計算」を考えます。「通常配送」「速達配送」「送料無料キャンペーン」のように、複数の計算方法があるとします。
これを1つのcalculateShippingFee()メソッドの中でif文を使って分岐すると、新しい配送方法を追加するたびにこのメソッドを修正する必要があり、メソッドがどんどん肥大化していきます。
また、条件分岐の中に複数のアルゴリズムのロジックが混在してしまい、個々のアルゴリズムを独立してテストすることも難しくなります。
Strategyパターンは、アルゴリズムそれぞれを独立したクラス(戦略/Strategy)として切り出し、利用側が実行時にどの戦略を使うかを選んで注入することで、この問題を解決します。
クラス構成
- Strategy(ストラテジー): アルゴリズムの共通インターフェース
- ConcreteStrategy(具体的なストラテジー): 個々のアルゴリズムを実装するクラス
- Context(コンテキスト): Strategyを利用する側。具体的にどのアルゴリズムを使うかは知らず、注入されたStrategyに処理を委譲する
Javaでの実装例
配送料金計算の例で実装してみます。
Strategy:共通インターフェース
public interface ShippingStrategy {
int calculateFee(int itemWeight, int itemPrice);
}
ConcreteStrategy:具体的な計算方法群
public class StandardShipping implements ShippingStrategy {
@Override
public int calculateFee(int itemWeight, int itemPrice) {
int fee = 500 + itemWeight * 10;
System.out.println("[通常配送] 料金計算: " + fee + "円");
return fee;
}
}
public class ExpressShipping implements ShippingStrategy {
@Override
public int calculateFee(int itemWeight, int itemPrice) {
int fee = 1200 + itemWeight * 20;
System.out.println("[速達配送] 料金計算: " + fee + "円");
return fee;
}
}
public class FreeShippingCampaign implements ShippingStrategy {
@Override
public int calculateFee(int itemWeight, int itemPrice) {
// 5000円以上の購入で送料無料、それ未満は通常配送扱い
if (itemPrice >= 5000) {
System.out.println("[送料無料キャンペーン] 対象のため料金: 0円");
return 0;
}
int fee = 500 + itemWeight * 10;
System.out.println("[送料無料キャンペーン] 対象外のため通常料金: " + fee + "円");
return fee;
}
}
Context:利用する側のクラス
public class Order {
private final int itemWeight;
private final int itemPrice;
private ShippingStrategy shippingStrategy; // 具体的なアルゴリズムは知らず、インターフェース経由で保持する
public Order(int itemWeight, int itemPrice) {
this.itemWeight = itemWeight;
this.itemPrice = itemPrice;
}
// 実行時に、使用する戦略(アルゴリズム)を差し替えられる
public void setShippingStrategy(ShippingStrategy shippingStrategy) {
this.shippingStrategy = shippingStrategy;
}
public int getTotalFee() {
return shippingStrategy.calculateFee(itemWeight, itemPrice);
}
}
利用側のコード
public class Main {
public static void main(String[] args) {
Order order1 = new Order(1000, 3000);
order1.setShippingStrategy(new StandardShipping());
System.out.println("合計送料: " + order1.getTotalFee() + "円");
System.out.println();
Order order2 = new Order(1000, 3000);
order2.setShippingStrategy(new ExpressShipping());
System.out.println("合計送料: " + order2.getTotalFee() + "円");
System.out.println();
Order order3 = new Order(1000, 6000); // キャンペーン対象金額
order3.setShippingStrategy(new FreeShippingCampaign());
System.out.println("合計送料: " + order3.getTotalFee() + "円");
}
}
実行結果:
[通常配送] 料金計算: 10500円
合計送料: 10500円
[速達配送] 料金計算: 21200円
合計送料: 21200円
[送料無料キャンペーン] 対象のため料金: 0円
合計送料: 0円
Orderクラス(Context)は、calculateFee()という共通のメソッドを呼んでいるだけで、実際に「通常配送」なのか「速達配送」なのかをif文で判定するロジックを一切持っていません。
どの配送方法(アルゴリズム)を使うかは、setShippingStrategy()によって外部から注入されています。
新しい配送方法(例えば「地域限定配送」)を追加したい場合も、新しいShippingStrategy実装クラスを1つ追加するだけで、Orderクラス自体は変更不要です。
Java標準ライブラリでの実例
Strategyパターンは、Java標準ライブラリでも広く使われています。
代表例が、Comparatorを使ったソート処理です。
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
// 「どうソートするか」というアルゴリズム(Comparator)を、外部から注入している
names.sort(Comparator.naturalOrder()); // 通常の文字列順
names.sort(Comparator.reverseOrder()); // 逆順
names.sort(Comparator.comparingInt(String::length)); // 文字列の長さ順
List#sort()メソッド自体は、実際のソートアルゴリズムは知らず、渡されたComparator(Strategy)に「どちらが先か」の判定を委譲しています。
これはまさに本記事のStrategyパターンと同じ構造です。
使用場面
- 同じ処理の中で、複数のアルゴリズムを実行時に切り替えたい場合(料金計算、ソート順、バリデーションルールなど)
- 巨大な
if/switchによるアルゴリズムの分岐を整理したい場合 - アルゴリズムごとに、独立してユニットテストを書きたい場合
- A/Bテストのように、実行時の条件によって異なるロジックを適用したい場合
まとめ
| 項目 | 内容 |
|---|---|
| 解決する課題 | 複数のアルゴリズムを、巨大なif/switch文ではなく柔軟に切り替えたい |
| 実現方法 | アルゴリズムを独立したStrategyクラスとして切り出し、利用側に外部から注入する |
| メリット | 新しいアルゴリズムの追加が、既存コードを変更せず新クラスの追加だけで済む |
| 実例 | Java標準のComparatorによるソート処理 |
おすすめ書籍
GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。
Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]
※本リンクはアフィリエイトリンクを含みます。


