デザインパターン入門 | Strategy(ストラテジー)パターン
「Strategy(ストラテジー)」パターンは、ある対象に対して「Strategy(戦略)」を作り、「戦略の入れ替え」ができるデザインパターンです。
「ある対象」には、主に「勝ち負け」があるような「勝負事」を想定しますが、どの戦略が有効なのかを調べるための「シミュレーション」などでも使われます。
今回は「プロジェクト」をテーマに「Strategy(ストラテジー)」パターンについて見ていきたいと思います。
「Strategy」インターフェースの役割と「戦略」の作り方
「ゲーム」や「シミュレーション」など、「競争・対戦」に必要なものが「戦略」です。
「戦略」は状況によって変わることもありますし、プレイヤーによって変わることもあります。
ということは「戦略」は「交換・変化が可能な仕組み」を持っていることが求められる部分でもあります。
今回作成するプログラムでは、「Strategy」インターフェースとして「戦略のひな型」を作っています。
「プロジェクトの成功可否を競う」ことを想定すると、「Strategy」インターフェースは下記のようになります。(プログラム言語:Java)
public interface Strategy {
public boolean judgeProject(Project pj); // プロジェクトの実行可否判断
}
このインターフェースには「judgeProject」抽象メソッドが定義されていますが、それぞれの「戦略」を表すクラスはこのインターフェースを実装しています。
「judgeProject」抽象メソッドは、「プロジェクトの実行可否」を判断する役割を持っていますが、どのような基準で判断するのかは「戦略」によって異なるため、それぞれの「戦略」を表すクラスによって定義していきます。
今回は「仕事のプロジェクト」をテーマにしていきたいと思いますが、「プロジェクトの実行可否」を決定する「経営者(社長)」を表すクラスも必要となってきます。
このそれぞれのクラスの関係を表すと下図のようになります。

「President」クラスは「経営者・社長」を表すクラスです。
この「President」クラスでは、「Strategy」インターフェース型のフィールドを持っているため、「has a」の関係となっています。
「戦略」にはさまざまなものがありますが、今回は「強気な戦略(Strong Strategy)」と「慎重な戦略(Careful Strategy)」の2つの戦略を作ってみたいと思います。
この2つの戦略は「Strategy」インターフェースを実装しています。

「President」クラスは下記のようになります。
public class President {
private String name; // 氏名
private int money = 1000000; // 保有金額
private Strategy strategy;
// コンストラクタ
public President(String name, Strategy strategy) {
this.name = name;
this.strategy = strategy;
}
// プロジェクトを実行するかどうかを判定
public boolean judgeProject(Project pj) {
return this.strategy.judgeProject(pj);
}
// プロジェクトリストを実行
public void execProjectList(ArrayList projects) {
for ( Project project : projects ) {
System.out.print(project);
if ( this.judgeProject(project) ) {
System.out.print("プロジェクト実行 プロジェクト実行結果:");
if( this.execProject(project) ) {
System.out.println("成功");
} else {
System.out.println("失敗");
}
} else {
System.out.println("プロジェクト未実行");
}
}
}
// プロジェクトを実行
public boolean execProject(Project pj) {
// コスト減算
this.money -= ( pj.getPrice() / 2 );
if( pj.execProject() == true ) {
this.money += pj.getPrice();
return true;
}
return false;
}
public String getName() {
return name;
}
public int getMoney() {
return money;
}
@Override
public String toString() {
return "経営者:" + this.getName() + " 保有金額:" + NumberFormat.getNumberInstance().format(this.getMoney());
}
}
「StrongStrategy」クラスは下記のようになります。
public class StrongStrategy implements Strategy {
// プロジェクトの実行可否を判断
@Override
public boolean judgeProject(Project pj) {
pj.getProbabilityOfSuccess();
if ( pj.getProbabilityOfSuccess() > 30 ) {
return true;
}
return false;
}
}
「CarefulStrategy」クラスは下記のようになります。
public class CarefulStrategy implements Strategy {
// プロジェクトの実行可否を判断
@Override
public boolean judgeProject(Project pj) {
pj.getProbabilityOfSuccess();
if ( pj.getProbabilityOfSuccess() > 50 && pj.getPrice() > 5000000) {
return true;
}
return false;
}
}
この2つの「戦略クラス」の違いは「プロジェクトの実行可否の基準」なので、それぞれの「戦略クラス」で実行可否を判断するための「judgeProject」メソッドが定義されています。
といっても判断基準は「プロジェクトの成功確率」と「プロジェクトの成功報酬金額」です。
「強気な戦略」では、「プロジェクトの成功確率」が30%を上回っていれば、プロジェクトを実行します。
一方、「慎重な戦略」では、「プロジェクトの成功確率」が50%を上回っていて、「プロジェクトの成功報酬金額」が「5,000,000円」を上回るプロジェクトのみを実行します。
「Project」クラスの概要
「Project」クラスは文字通り「プロジェクト」自体を表すクラスです。
public class Project {
private int probabilityOfSuccess; // 成功確率
private int price; // 成功報酬金額
// コンストラクタ
public Project ( int probabilityOfSuccess, int price) {
this.probabilityOfSuccess = probabilityOfSuccess;
this.price = price;
}
// プロジェクトを実行
public boolean execProject() {
int rand = new Random().nextInt(100) + 1;
if ( rand <= this.getProbabilityOfSuccess()) {
return true;
}
return false;
}
public int getProbabilityOfSuccess() {
return probabilityOfSuccess;
}
public int getPrice() {
return price;
}
@Override
public String toString() {
return "成功確率:" + String.format("%02d", this.getProbabilityOfSuccess()) + "% 成功報酬金額:" + NumberFormat.getNumberInstance().format(this.getPrice()) + "円";
}
}
「プロジェクトの成功確率」「プロジェクトの成功報酬金額」を表すフィールドや「プロジェクトの実行」を行うメソッドが定義されていますね。
「プロジェクトの実行」といっても、今回は「乱数値」を元にプロジェクトの成否を決定しているだけです。
プロジェクトを管理するための「ProjectManager」クラスは下記のようになります。
public class ProjectManager {
private ArrayList projects = new ArrayList(); // プロジェクトリスト
private final int UPPER_LIMIT = 9000000; // プロジェクト金額の上限設定値
private final int LOWER_LIMIT = 1000000; // プロジェクト金額の下限設定値
// プロジェクトリストを作成
public void createProjectList(int numOfProjects) {
for ( int i = 0; i < numOfProjects; i++) {
projects.add(createProject());
}
}
// プロジェクトを作成
private Project createProject() {
Random rand = new Random();
int probabilityOfSuccess = rand.nextInt(100) + 1;
int price= (rand.nextInt(((UPPER_LIMIT/1000)+1)) * 1000) + LOWER_LIMIT;
return new Project(probabilityOfSuccess, price);
}
// プロジェクトリストを表示
public void displayProjectsList() {
for ( Project pj : projects) {
System.out.println(pj);
}
System.out.println();
}
// 作成したプロジェクトの取得
public ArrayList getProjects() {
return this.projects;
}
}
1つ1つのプロジェクトは「ProjectManager」クラスによって「ArrayList」で管理されています。
このクラスには「プロジェクトリストの取得・表示・作成」などのメソッドが定義されています。
これで必要なクラスは全て揃いましたので、「プログラムの実行例」について見ていきたいと思います。
プログラムの実行
プログラムを実行するための「main」」メソッドは下記のようになります。
public class Main {
public static void main(String[] args) {
// プロジェクト管理者を作成
ProjectManager pjMan = new ProjectManager();
// 経営者の作成
President presidentA = new President("佐藤" , new StrongStrategy());
President presidentB = new President("西山" , new CarefulStrategy());
// プロジェクトの作成
pjMan.createProjectList(10);
// PresidentA
System.out.println("■プロジェクト実行前 " + presidentA);
presidentA.execProjectList(pjMan.getProjects());
System.out.println("■プロジェクト実行後 " + presidentA);
System.out.println();
System.out.println("===================================================================================");
System.out.println();
// PresidentB
System.out.println("■プロジェクト実行前 " + presidentB);
presidentB.execProjectList(pjMan.getProjects());
System.out.println("■プロジェクト実行後 " + presidentB);
}
}
「佐藤」さんと「西山」さんという2人の経営者が登場していますが、「佐藤社長」は「30%」という低い成功率であっても「プロジェクトを実行」しているため「強気戦略」を推進しています。
一方、「西山社長」は「慎重」派なので、「50%を超える成功率」そして、「プロジェクト報酬金額」が「5,000,000円」を超えるプロジェクトしか実行しません。
プログラムを実行すると「コスト」としてプロジェクト報酬金額の50%が保有金額から差し引かれますので、「プロジェクトの実行」に失敗すると「保有金額が減少していく仕組み」となっています。
実行結果は下記のようになりました。
■プロジェクト実行前 経営者:佐藤 保有金額:1,000,000 成功確率:31% 成功報酬金額:6,728,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:32% 成功報酬金額:9,917,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:13% 成功報酬金額:4,843,000円プロジェクト未実行 成功確率:89% 成功報酬金額:5,710,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:32% 成功報酬金額:9,575,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:67% 成功報酬金額:1,169,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:81% 成功報酬金額:2,144,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:12% 成功報酬金額:2,752,000円プロジェクト未実行 成功確率:57% 成功報酬金額:8,070,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:80% 成功報酬金額:3,397,000円プロジェクト実行 プロジェクト実行結果:成功 ■プロジェクト実行後 経営者:佐藤 保有金額:-18,000 =================================================================================== ■プロジェクト実行前 経営者:西山 保有金額:1,000,000 成功確率:31% 成功報酬金額:6,728,000円プロジェクト未実行 成功確率:32% 成功報酬金額:9,917,000円プロジェクト未実行 成功確率:13% 成功報酬金額:4,843,000円プロジェクト未実行 成功確率:89% 成功報酬金額:5,710,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:32% 成功報酬金額:9,575,000円プロジェクト未実行 成功確率:67% 成功報酬金額:1,169,000円プロジェクト未実行 成功確率:81% 成功報酬金額:2,144,000円プロジェクト未実行 成功確率:12% 成功報酬金額:2,752,000円プロジェクト未実行 成功確率:57% 成功報酬金額:8,070,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:80% 成功報酬金額:3,397,000円プロジェクト未実行 ■プロジェクト実行後 経営者:西山 保有金額:7,890,000
「強気な戦略」の「佐藤」さんのプロジェクトの実行結果は「-18.000円」となりましたが、「慎重な戦略」の「西山」さんのプロジェクトの実行結果は「7,890,000円」となりました。
しかし、この結果は「乱数値」を元にしているため、毎回実行結果は異なります。
試しにもう一回実行してみると、
■プロジェクト実行前 経営者:佐藤 保有金額:1,000,000 成功確率:53% 成功報酬金額:6,766,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:55% 成功報酬金額:8,325,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:75% 成功報酬金額:7,019,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:96% 成功報酬金額:9,505,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:13% 成功報酬金額:9,334,000円プロジェクト未実行 成功確率:11% 成功報酬金額:5,381,000円プロジェクト未実行 成功確率:52% 成功報酬金額:7,658,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:12% 成功報酬金額:2,283,000円プロジェクト未実行 成功確率:85% 成功報酬金額:9,137,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:36% 成功報酬金額:5,147,000円プロジェクト実行 プロジェクト実行結果:成功 ■プロジェクト実行後 経営者:佐藤 保有金額:27,778,500 =================================================================================== ■プロジェクト実行前 経営者:西山 保有金額:1,000,000 成功確率:53% 成功報酬金額:6,766,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:55% 成功報酬金額:8,325,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:75% 成功報酬金額:7,019,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:96% 成功報酬金額:9,505,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:13% 成功報酬金額:9,334,000円プロジェクト未実行 成功確率:11% 成功報酬金額:5,381,000円プロジェクト未実行 成功確率:52% 成功報酬金額:7,658,000円プロジェクト実行 プロジェクト実行結果:成功 成功確率:12% 成功報酬金額:2,283,000円プロジェクト未実行 成功確率:85% 成功報酬金額:9,137,000円プロジェクト実行 プロジェクト実行結果:失敗 成功確率:36% 成功報酬金額:5,147,000円プロジェクト未実行 ■プロジェクト実行後 経営者:西山 保有金額:2,283,000
「強気な戦略」の「佐藤」さんのプロジェクトの実行結果は「27,778,500円」となりましたが、「慎重な戦略」の「西山」さんのプロジェクトの実行結果は「2,283,000円」となりました。
このように「乱数値」を元にシミュレーションをすることを「モンテカルロ法」と言いますが、統計的に結果を導き出すことができるようになります。
「Strategy(ストラテジー)」パターンは、さまざまな戦略を変えることができるため、いろいろな戦略を作り、どの戦略が効果的なのかを「シミュレーション」することができます。
ぜひ何か身近な「シミュレーション対象」を見つけて、「Strategy(ストラテジー)」パターンを利用したプログラムを作ってみてください。