1、模式说明
策略模式比较好理解,就是将程序中用到的算法整体的拿出来,并有多个不同版本的算法实现,在程序运行阶段,动态的决定使用哪个算法来解决问题。
2、举例
排序算法的问题,假如我们的程序中需要对数据进行排序,我们知道,不同的算法具有不同的时间复杂度和空间复杂度,因此需要在程序运行时,根据可用内存和数据特征,选用不同的算法(排序策略),这就是策略模式的使用场景之一。再举个例子,负载均衡算法:如果某个服务部署了多个冗余的实例,客户端在向服务端发送请求时,根据负载均衡算法策略,请求可能会被转发到不同的服务提供者实例来处理,如何决定某个请求转发给哪个服务实例呢,最简单的做法就是轮询,顺次将请求转发给每个服务实例进行处理。也可以采用随机方式,或者根据实际硬件环境和业务场景设置特定算法。
3、程序示例
在下面的演示策略模式的代码示例中,我们模拟猜拳游戏——剪刀石头布,猜拳的策略有两种:如果这次猜拳赢了,则下次还出同样的手势。另一种策略就是根据以前的猜拳结果,选择胜率最高的一种手势。
定义Hand类
package cn.design.behavior.strategy;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:45
* @Description TODO
*/
public class Hand {
//表示石头
public static final int HANDVALUE_ROCK = 0;
//表示剪刀
public static final int HANDVALUE_SCISSORS = 1;
//表示布
public static final int HANDVALUE_PEPER = 2;
private static final Hand[] hand = {
new Hand(HANDVALUE_ROCK),
new Hand(HANDVALUE_SCISSORS),
new Hand(HANDVALUE_PEPER)
};
private static final String[] name = {
"石头", "剪刀", "布"
};
private final int handValue;
private Hand(int handValue) {
this.handValue = handValue;
}
public static Hand getHand(int handValue) {
return hand[handValue];
}
public boolean isStrongerThan(Hand h) {
return fight(h) == 1;
}
public boolean isWeakerThan(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;
}
}
@Override
public String toString() {
return name[handValue];
}
}
定义Strategy接口
package cn.design.behavior.strategy;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:50
* @Description TODO
*/
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
定义ProbStrategy类
package cn.design.behavior.strategy;
import java.util.Random;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:51
* @Description TODO
*/
public class ProbStrategy implements Strategy{
private Random random;
private int prevHandValue = 0;
private int currentHandValue = 0;
//history[上一局的手势][这一局的手势] 表达式的值越高表示过去的胜率越高
//study方法会根据nextHand方法返回的手势胜负结果更新history字段中的值
private int[][] history = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};
public ProbStrategy(int seed) {
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][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 : history[hv]
) {
sum += i;
}
return sum;
}
@Override
public void study(boolean win){
if(win){
history[prevHandValue][currentHandValue]++;
}else{
history[prevHandValue][(currentHandValue+1)%3]++;
history[prevHandValue][(currentHandValue+2)%3]++;
}
}
}
定义WinningStrategy类
package cn.design.behavior.strategy;
import java.util.Random;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:51
* @Description TODO
*/
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;
}
}
定义Player类
package cn.design.behavior.strategy;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:52
* @Description TODO
*/
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++;
}
@Override
public String toString(){
return "[" + name + ":" + gamecount + " games, " + wincount +
" win, " + losecount + " lose" + "]";
}
}
定义main测试类
package cn.design.behavior.strategy;
import java.util.Random;
/**
* @author lin
* @version 1.0
* @date 2020-07-22 14:52
* @Description TODO
*/
public class Main {
public static void main(String[] args) {
int seed1 = ((new Random()).nextInt(500)) * (1 + (new Random()).nextInt(500));
int seed2 = seed1 * (new Random()).nextInt(500);
Player player1 = new Player("Taro", new WinningStrategy(seed1));
Player player2 = new Player("Hana", new ProbStrategy(seed2));
for (int i = 0; i < 100000; i++) {
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if (nextHand1.isStrongerThan(nextHand2)) {
System.out.println("Winner: " + player1);
player1.win();
player2.lose();
} else if (nextHand2.isStrongerThan(nextHand1)) {
System.out.println("Winner: " + player2);
player2.win();
player1.lose();
} else {
System.out.println("Even...");
player1.even();
player2.even();
}
}
System.out.println("Total result:");
System.out.println(player1.toString());
System.out.println(player2.toString());
}
}
运行结果如下:
。。。
Even...
Winner: [Taro:99994 games, 37658 win, 27060 lose]
Even...
Even...
Winner: [Taro:99997 games, 37659 win, 27060 lose]
Winner: [Hana:99998 games, 27060 win, 37660 lose]
Even...
Total result:
[Taro:100000 games, 37660 win, 27061 lose]
[Hana:100000 games, 27061 win, 37660 lose]
4、Strategy策略模式中的角色
Strategy策略:负责定义实现策略必须的接口方法
ConcreteStrategy具体的策略:实现Strategy角色的接口,如程序中的WinningStrategy和ProbStrategy
Context上下文:负责使用Strategy策略,如示例程序中的player。
5、相关的设计模式
Flyweight享元模式:通过使用享元模式,让多个地方共用ConcreteStrategy角色;
Abstract Factory抽象工厂:策略模式整体替换算法,抽象工厂整体替换具体的工厂,零件和产品;
State状态模式:状态模式和策略模式都可以替换被委托对象,而且类之间的关系也相似,只是两种模式目的不同。strategy策略模式替换被委托对象的类;状态模式中,每次状态发生变化时,被委托的对象必定会被替换。
公众号:发哥讲
这是一个稍偏基础和技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。