Strategyパターンとは
GoFのデザインパターンのうちの1つで、振る舞いに関するデザインパターンです。プログラムの実行中に処理を変更したい場合に、Startegyパターンを用いればクライアント側で簡単にアルゴリズムを切り替えることができます。
なぜStrategyパターンを使うのか
Strategyパターンを使用することで、クライアント側が簡単にアルゴリズムを簡単に切り替えられるため、クライアント側の負担が軽くなります。また、コード自体がすっきりするため可読性が高まり、メンテナンスがしやすくなるメリットがあります。
普通にプログラミングをしていると、処理を切り替える際はif文での条件分岐を使用しがちです。
しかし、処理内容が複雑になり条件分岐の数が増えてくると、改修作業時に修正箇所の把握や影響範囲などの判定が難しくなり、バグに繋がりやすくなってしまいます。
どういう時に使用するのか
アルゴリズムの切り替えが必要な時に使用します。例えば、ゲームの難易度をEasyからHardに変更したい場合や、性別によって処理を変えたいときなどに有効です。
サンプルコード
Strategyパターンを用いて簡単な処理を一つ実装してみます。処理内容
大学の入試試験での点数から、各大学の入学評価を判定する処理を考えます。入試試験を「鈴本賢太さん、吉村彩さん、福永尚也さん、増岡麻衣さん」の4人が受けました。
それぞれの点数は以下の表のとおりです。
名前 | 点数(100点満点) |
---|---|
鈴本賢太 | 90点 |
吉村彩 | 80点 |
福永尚也 | 70点 |
増岡麻衣 | 60点 |
この4人が、難易度が高い大学、平均の大学、低い大学の3つの評価を受けるとします。
それぞれどのような評価を受けるのか、Strategyパターンで切り替えながら見てみましょう。
コード詳細
まずは大学で点数を評価するクラスを作成します。 このクラスでは評価方法を直接記載せずに、Strategyクラスを呼び出して評価する処理を記載しています。
public class SampleClass {
BaseStrategy currentStrategy;
/*
* SampleClass コンストラクタ
* @param firstStrategy 設定するStrategy
*/
public SampleClass(BaseStrategy firstStrategy) {
this.currentStrategy = firstStrategy;
}
/*
* changeStrategy Strategyを変更する
* @param newStrategy 変更するStrategy
*/
public void changeStrategy(BaseStrategy newStrategy) {
this.currentStrategy = newStrategy;
}
/*
* getOpinion 評価を取得する
* @param pointMap テストの点数
*/
public void getOpinion(Map<String, Integer> pointMap) {
// クラスの人数分ループ
for (String name : pointMap.keySet()) {
// 評価結果を計算
int result = currentStrategy.scoringTest(pointMap.get(name));
// 評価結果を出力
if (result > 0) {
System.out.println(name + "さん:良い評価です");
} else if (result == 0) {
System.out.println(name + "さん:普通の評価です");
} else {
System.out.println(name + "さん:悪い評価です");
}
}
}
}
次にStrategyクラスのインターフェスを作成します。
各Strategyクラスでメソッド名を統一するためです。
今回は、評価を計算する処理を担うscoringTestメソッドのみ実装しています。
戻り値の評価結果は、良い評価なら1、普通の評価なら0、悪い評価なら-1を返します。
public interface BaseStrategy {
/*
* scoringTest 評価を計算する
* @parame point テストの点数
* @return 良い評価:1, 普通の評価:0, 悪い評価:-1
*/
int scoringTest(int point);
}
次に、各大学での評価方法をBaseStrategy.javaを継承して作成していきます。
EasyUniversityStrategyの評価方法は、
80点以上が良い評価、60点以上が普通の評価、59点以下が悪い評価です。
public class EasyUniversityStrategy implements BaseStrategy {
public int scoringTest(int point) {
// 80点以上の場合、良い評価
if (point >= 80) {
return 1;
// 60点以上の場合、普通の評価
} else if (point >= 60) {
return 0;
// 59点以下の場合、悪い評価
} else {
return -1;
}
}
}
UsualUniverSityStrategyの評価方法は、
85点以上が良い評価、70点以上が普通の評価、69点以下が悪い評価です。
public class UsualUniverSityStrategy implements BaseStrategy {
public int scoringTest(int point) {
// 85点以上の場合、良い評価
if (point >= 85) {
return 1;
// 70点以上の場合、普通の評価
} else if (point >= 70) {
return 0;
// 69点以下の場合、悪い評価
} else {
return -1;
}
}
}
HardUniverSityStrategyの評価方法は、
95点以上が良い評価、80点以上が普通の評価、79点以下が悪い評価です。
public class HardUniverSityStrategy implements BaseStrategy {
public int scoringTest(int point) {
// 95点以上の場合、良い評価
if (point >= 95) {
return 1;
// 80点以上の場合、普通の評価
} else if (point >= 80) {
return 0;
// 79点以下の場合、悪い評価
} else {
return -1;
}
}
}
最後にメインクラスで、各大学の評価方法で4人の点数を評価する処理を実装します。
public class SampleMain {
public static void main(String args[]) {
Map<String, Integer> pointMap = new HashMap<String, Integer>() {
{
put("鈴本賢太", 90);
put("吉村彩", 80);
put("福永尚也", 70);
put("増岡麻衣", 60);
}
};
// EasyUniversityStrategyで評価
SampleClass sampleClass = new SampleClass(new EasyUniversityStrategy());
System.out.println("EasyUniversityStrategyの評価結果");
sampleClass.getOpinion(pointMap);
// UsualUniverSityStrategyで評価
sampleClass.changeStrategy(new UsualUniverSityStrategy());
System.out.println("UsualUniverSityStrategyで評価");
sampleClass.getOpinion(pointMap);
// HardUniverSityStrategyで評価
sampleClass.changeStrategy(new HardUniverSityStrategy());
System.out.println("HardUniverSityStrategyで評価");
sampleClass.getOpinion(pointMap);
}
}
実行結果
EasyUniversityStrategyの評価結果 増岡麻衣さん:普通の評価です 鈴本賢太さん:良い評価です 福永尚也さん:普通の評価です 吉村彩さん:良い評価ですUsualUniverSityStrategyで評価
増岡麻衣さん:悪い評価です
鈴本賢太さん:良い評価です
福永尚也さん:普通の評価です
吉村彩さん:普通の評価です
HardUniverSityStrategyで評価
増岡麻衣さん:悪い評価です
鈴本賢太さん:普通の評価です
福永尚也さん:悪い評価です
吉村彩さん:普通の評価です
最後に
Strategyパターンについて解説しました。サンプルコードを見ても、if文での条件分岐に比べて分かりやすいコードになっていることがわかると思います。
(かくいう自分も前まではif文でせっせと条件分岐を羅列していたのですが。。。)
クラス設計などを考えると少し面倒かもしれませんが、のちのメンテナンス性などを考えるとStrategyパターンを用いたほうがコードがすっきりするので、もしアルゴリズムの分岐があればぜひStrategyパターンを使用してみてください!