デザインパターン入門 | 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(ArrayListprojects) { 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 ArrayListprojects = 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(ストラテジー)」パターンを利用したプログラムを作ってみてください。