设计模式学习笔记——策略(Strategy)模式

@(设计模式)[设计模式, 策略模式, Stategy]

  • 设计模式学习笔记策略Strategy模式
    • 基本介绍
    • 策略案例
      • 类图
      • 实现代码
        • Hand类
        • Strategy接口
        • WinningStrategy类
        • ProbStrategy类
        • Player类
        • 测试类
        • 运行结果
    • 策略模式中的角色
      • Strategy策略
      • ConcreteStrategy具体的策略
      • Context上下文
      • 类图

基本介绍

策略模式主要用于整体切换解决方案,算法。一般使用委托来实现。

策略案例

类图

实现代码

Hand类

package com.pc.strategy.example;

/**
 * 猜拳手势类
 * Created by Switch on 2017-02-20.
 */
public class Hand {
    private static final int HANDVALUE_ST = 0;  // 石头
    private static final int HANDVALUE_JD = 1;  // 剪刀
    private static final int HANDVALUE_BU = 2;  // 布

    private int handValue;  // 手势值

    // 手势值对应的手势名
    private static final String[] name = {
            "石头", "剪刀", "布"
    };

    // 手势实例
    public static final Hand[] hand = {
            new Hand(HANDVALUE_ST),
            new Hand(HANDVALUE_JD),
            new Hand(HANDVALUE_BU)
    };

    /**
     * 构造方法,私有化,不能new出实例,注册表单例模式
     *
     * @param handValue 手势值
     */
    private Hand(int handValue) {
        this.handValue = handValue;
    }

    /**
     * 根据手势值,获取手势实例
     *
     * @param handValue 手势值
     * @return 手势实例
     */
    public static Hand getHand(int handValue) {
        return hand[handValue];
    }

    /**
     * 赢了返回true
     *
     * @param hand 手势实例
     * @return
     */
    public boolean isStrongerThan(Hand hand) {
        return fight(hand) == 1;
    }

    /**
     * 输了返回true
     *
     * @param hand 手势实例
     * @return
     */
    public boolean isWeakerThan(Hand hand) {
        return fight(hand) == -1;
    }

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

    @Override
    public String toString() {
        // 手势值对应的手势名
        return name[this.handValue];
    }
}

Strategy接口

package com.pc.strategy.example;

/**
 * 猜拳策略接口
 * Created by Switch on 2017-02-20.
 */
public interface Strategy {
    /**
     * 下一个出拳手势
     *
     * @return 手势实例
     */
    Hand nextHand();

    /**
     * 根据上一次猜拳是否获胜,决定下一次的猜拳手势
     *
     * @param win 是否获胜
     */
    void study(boolean win);
}

WinningStrategy类

package com.pc.strategy.example;

import java.util.Random;

/**
 * Winning策略类
 * Created by Switch on 2017-02-20.
 */
public class WinningStrategy implements Strategy {
    private Random random; // 随机
    private boolean won = false;    // 上一把是否赢了
    private Hand prevHand;  // 上一把的手势对象

    /**
     * 构造方法,传入初始化随机seek
     *
     * @param seed 随机种子
     */
    public WinningStrategy(int seed) {
        this.random = new Random(seed);
    }

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

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

ProbStrategy类

package com.pc.strategy.example;

import java.util.Random;

/**
 * Prob策略类
 * Created by Switch on 2017-02-20.
 */
public class ProbStrategy implements Strategy {
    private Random random;  // 随机
    private int prevHandValue;  // 上一把的手势值
    private int currentHandValue; // 这一把的手势值

    // 历史出拳概率数组
    // history[上一把的手势值][这一把的手势值] 表示赢的次数
    private int[][] history = {
            {1, 1, 1},
            {1, 1, 1},
            {1, 1, 1}
    };

    /**
     * 构造方法,传入初始化随机seek
     *
     * @param seed 随机种子
     */
    public ProbStrategy(int seed) {
        this.random = new Random(seed);
    }


    @Override
    public Hand nextHand() {
        int randomValue = this.random.nextInt(this.getSum(this.currentHandValue));
        int handValue = 0;
        if (randomValue < this.history[this.currentHandValue][0]) {
            // 当随机数在0到剪刀赢的次数时,赋值为0
            handValue = 0;
        } else if (randomValue < this.history[this.currentHandValue][0] + this.history[this.currentHandValue][1]) {
            // 当随机数在剪刀赢的次数到石头赢得次数时,赋值为1
            handValue = 1;
        } else {
            // 其他情况,赋值为2
            handValue = 2;
        }
        // 当前值赋值为之前值
        this.prevHandValue = this.currentHandValue;
        // 计算出来的值赋值为当前值
        this.currentHandValue = handValue;
        return Hand.getHand(handValue);
    }

    /**
     * 获取history[上一把的手势值][三种手势]赢的总次数
     *
     * @param hv 上一把的手势值
     * @return history[上一把的手势值][三种手势]赢的总次数
     */
    private int getSum(int hv) {
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum += history[hv][i];
        }
        return sum;
    }

    @Override
    public void study(boolean win) {
        if (win) {
            // history[上一把的手势值][这一把的手势]赢的次数加一
            this.history[this.prevHandValue][this.currentHandValue]++;
        } else {
            // history[上一把的手势值][其他手势]赢的次数加一
            this.history[this.prevHandValue][(this.currentHandValue + 1) % 3]++;
            this.history[this.prevHandValue][(this.currentHandValue + 2) % 3]++;
        }
    }
}

Player类

package com.pc.strategy.example;

/**
 * 玩家类
 * Created by Switch on 2017-02-20.
 */
public class Player {
    private String name; // 玩家名
    private Strategy strategy; // 策略对象
    private int wincount;   // 赢的次数
    private int losecount;  // 输出次数
    private int gamecount;  // 游戏总次数

    /**
     * 构造方法
     *
     * @param name     玩家名
     * @param strategy 策略对象
     */
    public Player(String name, Strategy strategy) {
        this.name = name;
        this.strategy = strategy;
    }

    /**
     * 策略决定下次要出的手势
     *
     * @return 手势对象
     */
    public Hand nextHand() {
        return this.strategy.nextHand();
    }

    /**
     * 赢
     */
    public void win() {
        this.strategy.study(true);
        this.wincount++;
        this.gamecount++;
    }

    /**
     * 输
     */
    public void lose() {
        this.strategy.study(false);
        this.losecount++;
        this.gamecount++;
    }

    /**
     * 平
     */
    public void even() {
        this.gamecount++;
    }

    @Override
    public String toString() {
        return "[" + this.name + ":" + this.gamecount + " games, " + this.wincount + " win, " + this.losecount + " lose]";
    }
}

测试类

package com.pc.strategy.example.test;

import com.pc.strategy.example.Hand;
import com.pc.strategy.example.Player;
import com.pc.strategy.example.ProbStrategy;
import com.pc.strategy.example.WinningStrategy;
import org.junit.Test;

/**
 * Strategy Tester.
 *
 * @author Switch
 * @version 1.0
 */
public class StrategyTest {
    /**
     * 测试策略类
     */
    @Test
    public void testStrategy() {
        Player switchvov = new Player("switch", new WinningStrategy(54));
        Player kity = new Player("kity", new ProbStrategy(32));

        for (int i = 0; i < 10000; i++) {
            Hand nextValue1 = switchvov.nextHand();
            Hand nextValue2 = kity.nextHand();
            if (nextValue1.isStrongerThan(nextValue2)) {
                switchvov.win();
                kity.lose();
                System.out.println("Winner:" + switchvov);
            } else if (nextValue1.isWeakerThan(nextValue2)) {
                switchvov.lose();
                kity.win();
                System.out.println("Winner:" + kity);
            } else {
                System.out.println("Even...");
                switchvov.even();
                kity.even();
            }
        }
        System.out.println("Total Result:");
        System.out.println(switchvov);
        System.out.println(kity);

    }
}

运行结果

Winner:[switch:1 games, 1 win, 0 lose]
Winner:[switch:2 games, 2 win, 0 lose]
Even...
Winner:[kity:4 games, 1 win, 2 lose]
Winner:[kity:5 games, 2 win, 2 lose]

省略N行。。。

Winner:[switch:9998 games, 3136 win, 3642 lose]
Winner:[switch:9999 games, 3137 win, 3642 lose]
Winner:[kity:10000 games, 3643 win, 3137 lose]
Total Result:
[switch:10000 games, 3137 win, 3643 lose]
[kity:10000 games, 3643 win, 3137 lose]

策略模式中的角色

Strategy(策略)

Strategy角色负责决定实现策略所必需的接口(API)。在案例中,由Strategy接口扮演此角色。

ConcreteStrategy(具体的策略)

ConcreteStrategy角色负责实现Strategy角色的接口(API) ,即负责实现具体的策略(战略、方向、方法和算法)。在案例中,由WinningStrategy类和ProbStrategy类扮演此角色。

Context(上下文)

负责使用Strategy角色。Context角色保存了ConcreteStrategy 角色的实例,并使用ConcreteStrategy角色去实现需求(总之,还是要调用Strategy角色的接口(API))。在案例中,由Player类扮演此角色。

类图

GitHub:DesignPatternStudy

——————参考《图解设计模式》

更多推荐

设计模式学习笔记——策略(Strategy)模式