LoginSignup
0

More than 3 years have passed since last update.

Strategyパターン

Last updated at Posted at 2020-09-08

Strategyパターンとは

どんなプログラムも問題を解くために書かれている。
そして問題を解くために特定のアルゴリズムが実装されている。
Strategyパターンでは、そのアルゴリズムを実装した部分が交換できるようになっている。
アルゴリズムを切り替え、同じ問題を別の方法で解くのを容易にするパターン。

Strategy(戦略)の役

戦略を利用するためのインターフェースを定める役。

package strategy;

public interface Strategy {
    // 次回出す手を決定する
    public abstract Hand nexHand();
    // 前回出した手で勝ったかどうかを学習する
    public abstract void study(boolean win);
}

ConcreteStrategy(具体的戦略)の役

Strategy役のインターフェースを実際に実装する役。
ここで具体的に戦略(作戦、方法、方策、アルゴリズム)を実際にプログラミングする。

package strategy;

import java.util.Random;

public class WinningStrategy implements Strategy{
    private Random random;
    private boolean won = false;
    private Hand prevHand;

    public WinningStrategy(int seed) {
        random = new Random(seed);
    }

    /**
     * 前回勝っていれば同じ手を出し、負けていればランダムで手を決定する
     */
    @Override
    public Hand nextHand() {
        if (!won) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        return prevHand;
    }

    @Override
    public void study(boolean win) {
        won = win;
    }
}
package strategy;

import java.util.Random;

public class ProbStrategy implements Strategy {
    private Random random;
    private int prevHandValue = 0;
    private int currentHandValue = 0;
    // 過去の勝敗を反映した確率計算のための表
    //[前回に出した手][今回出す手]
    private int[][] history = {
            {1,1,1,},
            {1,1,1,},
            {1,1,1,},
    };

    public ProbStrategy(int seed) {
        random = new Random(seed);
    }

    /**
     * historyの値から確率で計算を行う
     * 例えば、history[0][0]の値が3、history[0][1]の値が5、history[0][2]の値が7の場合
     * グー、チョキ、パーを出す割合を3:5:7として次の手を決定する。
     * 0以上15未満(15は3+5+7の合計値)の乱数値を得て、
     * 0以上3未満ならグー
     * 3以上8未満ならチョキ
     * 8以上15未満ならパー
     * とする
     */
    @Override
    public Hand nextHand() {
        int bet = random.nextInt(getSum(currentHandValue));
        int handValue = 0;
        if (bet < history[currentHandValue][0]) {
            handValue = 0;
        } else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
            handValue = 1;
        } else {
            handValue = 2;
        }

        prevHandValue = currentHandValue;
        currentHandValue = handValue;
        return Hand.getHand(handValue);
    }

    private int getSum(int hv) {
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum += history[hv][i];
        }
        return sum;
    }

    /**
     * nextHandメソッドで返した手の勝敗を元に、historyフィールドの内容を更新する
     *
     */
    @Override
    public void study(boolean win) {
        if (win) {
            history[prevHandValue][currentHandValue]++;
        } else {
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}

Context(文脈)の役

Strategy役を利用する役。
ConcreateStrategy役のインスタンスを持っていて、必要に応じてそれを利用する。あくまで呼び出すのはStrategy役のインターフェース。

package strategy;

public class Player {
    private String name;
    private Strategy strategy;
    private int winCount;
    private int loseCount;
    private int gameCount;

    public Player(String name, Strategy strategy) {
        this.name = name;
        this.strategy = strategy;
    }

    public Hand nextHand() {
        // 実際に次の手を決定するのは自分の「戦略」
        return strategy.nextHand();
    }

    public void win() {
        // 戦略の内部状態を変化させる
        strategy.study(true);
        winCount++;
        gameCount++;
    }

    public void lose() {
        // 戦略の内部状態を変化させる
        strategy.study(false);
        loseCount++;
        gameCount++;
    }

    public void even() {
        gameCount++;
    }

    public String toString() {
        return "[" + name + ":" + gameCount + " games, " + winCount + " win, " +
    loseCount + " lose" + "]";
    }
}

呼び出し元

package strategy;

public class Main {
    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Usage: java Main randmeseed1 randomeseed2");
            System.out.println("Example: java Main 314 15");
            System.exit(0);
        }

        int seed1 = Integer.parseInt(args[0]);
        int seed2 = Integer.parseInt(args[1]);
        Player p1 = new Player("Jiro", new WinningStrategy(seed1));
        Player p2 = new Player("Taro", new ProbStrategy(seed2));
        for (int i = 0; i < 10000; i++) {
            Hand nexHand1 = p1.nextHand();
            Hand nexHand2 = p2.nextHand();
            if (nexHand1.isStrongerThan(nexHand2)) {
                System.out.println("Winner:" + p1);
                p1.win();
                p2.lose();
            } else if (nexHand2.isStrongerThan(nexHand1)) {
                System.out.println("Winner:" + p2);
                p1.lose();
                p2.win();
            } else {
                System.out.println("Even…");
                p1.even();
                p2.even();
            }
        }

        System.out.println("Total result:");
        System.out.println(p1);
        System.out.println(p2);

    }
}

実行結果

スクリーンショット 2020-09-08 13.43.45.png

こちらを参考にさせていただきました。
増補改訂版Java言語で学ぶデザインパターン入門

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0