策略模式

143 阅读3分钟

实现背景

策略(strategy)模式,无论什么程序,其目的都是解决问题。而面对不同的问题,可能就会有不同的解决方法,strategy模式可以整体地替换算法的实现部分。能够整体地替换算法,能让我们轻松地以不同的方法去解决同一个问题

登场角色

Strategy(策略)

Strategy 角色负责决定实现策略所必须的接口(API)

ConcreteStrategy(具体的策略)

角色负责实现Strategy的接口,即负责实现具体的策略

Context(上下文)

负责使用 Strategy 角色

类图

实例代码

以石头剪刀布作为例子,玩家在每次出石头剪刀布时都有不同的策略

手势类

public class Hand {
    public static final int HANDVALUE_GUU = 0; // 石头的值

    public static final int HANDVALUE_CHO = 1; // 剪刀的值

    public static final int HANDVALUE_PAA = 2; // 布的值

    private int handValue;

    public Hand(int handValue) {
        this.handValue = handValue;
    }

    /**
     * 使用 单例模式 获取这三个中的值
     */
    public static final Hand[] hands = {
            new Hand(HANDVALUE_GUU),
            new Hand(HANDVALUE_CHO),
            new Hand(HANDVALUE_PAA)
    };

    private static final String[] handNames = {"石头", "剪刀", "布"};

    /**
     * 获取 hand的值
     *
     * @param handValue
     * @return
     */
    public static Hand getHand(int handValue) {
        return hands[handValue];
    }

    /**
     * 获取hand的名称
     *
     * @param handValue
     * @return
     */
    public String getHandName(int handValue) {
        return handNames[handValue];
    }

    /**
     * 比赢了
     * @param hand
     * @return
     */
    public boolean isStrongThanOther(Hand hand) {
        return fight(hand) == 1;
    }

    /**
     * 比输了
     * @param hand
     * @return
     */
    public boolean isWorseThanOther(Hand hand) {
        return fight(hand) == -1;
    }

    /**
     * 比较
     * @param hand
     * @return
     */
    private int fight(Hand hand) {
        if (this == hand) {
            // 平手
            return 0;
        } else if ((this.handValue + 1) % 3 == hand.handValue) {
            return 1;
        } else {
            return -1;
        }
    }
}

Strategy接口

/**
 * 定义策略的接口
 */
public interface Strategy {
    // 获取下一局所要出的手势
    Hand nextHand();

    // 学习上一局是否赢了
    void study(boolean win);
}

WinningStrategy类:若赢了,下次出的手势与上次一样

import java.util.Random;

public class WinningStrategy implements Strategy{

    private Random random;

    // 上一局是否赢了
    private boolean won = false;

    // 上一局的结果
    private Hand preHand;

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

    @Override
    public Hand nextHand() {
        if (!won) {
            preHand =  Hand.getHand(random.nextInt(3));
        }
        return preHand;
    }

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

ProbStrategy类:根据以往的出拳的概率判断下一次所出的手势

public class ProbStrategy implements Strategy {
    private Random random;

    private int currentHandValue = 0;

    private int preHandValue = 0;

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

    /**
     * history[上一局所出的手势][这一局所出的手势]
     */
    private int[][] history = {
            {1, 1, 1},
            {1, 1, 1},
            {1, 1, 1}
    };

    @Override
    public Hand nextHand() {
        int handValue = 0;
        int bet = random.nextInt(getSum(currentHandValue));

        if (bet < history[currentHandValue][0]) {
            handValue = 0;
        } else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
            handValue = 1;
        } else {
            handValue = 2;
        }

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

    @Override
    public void study(boolean win) {
        if (win) {
            history[preHandValue][currentHandValue]++;
        } else {
            history[preHandValue][(currentHandValue + 1) % 3]++;
            history[preHandValue][(currentHandValue + 2) % 3]++;
        }
    }

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

}

Player类:相当于 Context

public class Player {

    private String name;

    private Strategy strategy;

    private int winTimes;

    private int loseTimes;

    private int gameTimes;

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

    public Hand nextHand() {
        return strategy.nextHand();
    }

    public void win() {  // 胜
        strategy.study(true);
        winTimes++;
        gameTimes++;
    }

    public void lose() {  // 负
        strategy.study(false);
        loseTimes++;
        gameTimes++;
    }

    public void even() { // 平
        gameTimes++;
    }


    @Override
    public String toString() {
        return "Player{" +
                "name='" + name + ''' +
                ", strategy=" + strategy +
                ", winTimes=" + winTimes +
                ", loseTimes=" + loseTimes +
                ", gameTimes=" + gameTimes +
                '}';
    }
}

Main类

public class Main {

    public static void main(String[] args) {
        int seed1 = 2;
        int seed2 = 3;

        Player player1 = new Player("Taro", new WinningStrategy(seed1));
        Player player2 = new Player("Hana", new ProbStrategy(seed2));

        for (int i = 0; i < 10000; i++) {
            Hand hand1 = player1.nextHand();
            Hand hand2 = player2.nextHand();
            if (hand1.isStrongThanOther(hand2)) {
                System.out.println("Winner is " + player1);
                player1.win();
                player2.lose();
            } else if (hand2.isStrongThanOther(hand1)) {
                System.out.println("Winner is " + player2);
                player1.lose();
                player2.win();
            } else {
                System.out.println("Even...");
                player1.even();
                player2.even();
            }
        }

        System.out.println("Total result;");
        System.out.println(player1.toString());
        System.out.println(player2.toString());
    }
}