目录
一、什么是策略模式
策略模式(Strategy Method)是一种比较简单的模式,也叫做政策模式,其定义如下:定义一组算法,将每个算法都封装起来,并且使他们之间相互切换。
二、示例程序
1、Hand类
package com.as.module.stratege;
/**
* 手势类
* @author Andy
* @date 2021/5/9 22:48
*/
public class Hand {
public static final int HANDVALUE_GUU = 0;//表示石头的值
public static final int HANDVALUE_CHO = 1;//表示剪刀的值
public static final int HANDVALUE_PAA = 2;//表示布的值
public static final Hand[] hand = {
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA),
};
public static final String[] name = {
"石头","剪刀","布"
};
private int handvalue;
public Hand(int handvalue) {
this.handvalue = handvalue;
}
public static Hand getHand(int handvalue){
return hand[handvalue];
}
public boolean isStrongThan(Hand h){
return fight(h) == 1;
}
private int fight(Hand h){
if(this == h){
return 0;
}else if((this.handvalue+1)%3==h.handvalue){
return 1;
}else{
return -1;
}
}
public String toString(){
return name[handvalue];
}
}
2、Strategy接口
package com.as.module.stratege;
/**
* 策略接口
* @author Andy
* @date 2021/5/9 22:59
*/
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
3、WinningStrategy接口
package com.as.module.stratege;
import java.util.Random;
/**
* TODO
*
* @author Andy
* @date 2021/5/9 23:00
*/
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;
}
}
4、ProbStrategy接口
package com.as.module.stratege;
import java.util.Random;
/**
* TODO
* @author Andy
* @date 2021/5/10 21:28
*/
public class ProbStrategy implements Strategy{
private Random random;
private int preHandValue;
private int currentHandValue;
private int[][] history = {
{1,1,1,},
{1,1,1,},
{1,1,1,},
};
public ProbStrategy(int seed) {
this.random = new Random(seed);
}
@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;
}
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;
}
}
5、Player类
package com.as.module.stratege;
/**
* 玩家
* @author Andy
* @date 2021/5/10 21:40
*/
public class Player {
private String name;
private Strategy strategy;
private int wincount;
private int losecount;
private int gamecount;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public int getWincount() {
return wincount;
}
public void setWincount(int wincount) {
this.wincount = wincount;
}
public int getLosecount() {
return losecount;
}
public void setLosecount(int losecount) {
this.losecount = losecount;
}
public int getGamecount() {
return gamecount;
}
public void setGamecount(int gamecount) {
this.gamecount = 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++;
}
@Override
public String toString() {
return "Player{" +
"name='" + name + '\'' +
", strategy=" + strategy +
", wincount=" + wincount +
", losecount=" + losecount +
", gamecount=" + gamecount +
'}';
}
}
6、TestStrategy类
package com.as.module.stratege;
/**
* 策略模式测试类
* @author Andy
* @date 2021/5/10 21:46
*/
public class TestStrategy {
public static void main(String[] args) {
int seed1 = 5;
int seed2 = 6;
Player player1 = new Player("Andy",new WinningStrategy(seed1));
Player player2 = new Player("Lucy",new ProbStrategy(seed2));
for(int i=0;i<10000;i++){
Hand nextHandle1 = player1.nextHand();
Hand nextHandle2 = player2.nextHand();
if(nextHandle1.isStrongThan(nextHandle2)){
System.out.println("winner:"+player1.getName());
player1.win();
player2.lose();
}else if(nextHandle2.isStrongThan(nextHandle1)){
System.out.println("winner:"+player2.getName());
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());
}
}
三、UML
在Strategy模式中有以下登场角色。
1、Strategy(策略)
Strategy角色负责决定实现策略所必须的接口。在示例程序中,由Strategy接口扮演此角色。
2、ConcreteStrategy(具体的策略)
ConcreteStrategy角色负责实现Strategy角色的接口,即负责实现具体的策略(战略、方向、方法和算法)。在示例程序中,由WinningStrategy和ProbStrategy类扮演此角色。
3、Context(ontext(上下文)
负责使用Strategy角色。Context角色保存了ConcreteStrategy角色的实例,并使用ConcreteStrategy角色去实现需求。在示例程序中,由Player类扮演此角色
四、拓展思路与注意事项
1、为什么需要特意编写Strategy角色
通常在编程时算法会被写在具体方法中。Strategy模式却特意将算法与其他部分分离开来,只是定义了与算法相关的接口,然后在程序中以委托的方式来使用算法。
这样看起来程序好像变复杂了,其实不然。例如,当我们想要通过改善算法来提高算法的处理速度时,如果使用了Strategy模式,就不必修改Strategy角色的接口了,仅仅修改ConcreteStrategy角色即可。而且,使用委托这种弱关联关系可以很方便地整体替换算法。
2、程序运行中也可以切换策略
如果是Strategy模式,在程序运行中也可以切换ConcreteStrategy角色。例如,在内存容量少的运行环境中可以使用SlowButLessMemory(速度慢但是省内存的策略),而在内存容量比较多的环境中则可以使用FastButMoreMemoryStrategy(速度快但耗内存的策略)
3、策略模式的优点
(1) 算法可以自由切换
(2) 避免使用多重条件判断
(3) 扩展性良好
4、策略模式的缺点
(1)策略类数量众多
(2)所有的策略类都需要对外暴露
上层模块必须知道有哪些策略,然后才能决定使用哪个策略,这与迪米特法则是想违背的,但是,可以通过其他模式来修补这个缺陷,如工厂方法模式,代理模式,享元模式
5、策略模式的使用场景
(1)算法需要自由切换的场景
(2)需要屏蔽算法规则的场景