10、Strategy 策略模式 整体地替换算法 行为型模式
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策略模式替换被委托对象的类;状态模式中,每次状态发生变化时,被委托的对象必定会被替换。
公众号:发哥讲
这是一个稍偏基础和技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
10、Strategy 策略模式 整体地替换算法 行为型模式的更多相关文章
- 设计模式学习之命令模式(Command,行为型模式)(12)
一.命令模式的定义 命令模式属于对象的行为型模式.命令模式是把一个操作或者行为抽象为一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开.命令模式的实现可以提供命令的撤销和恢复功能 ...
- 设计模式学习之备忘录模式(Memento,行为型模式)(19)
假如我们已经记录一个人的个人信息,但是发现信息写错了,然后我先备份下再去修改,结果发现原来的信息是正确的,于是我就看备份的个人信息还原到初始的状态,下面我们用代码去实现 class Program { ...
- 设计模式学习之迭代器模式(Iterator,行为型模式)(17)
参考地址:http://www.cnblogs.com/zhili/p/IteratorPattern.html 一.介绍迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作 ...
- ANDROID 中设计模式的採用--创建型模式
所谓模式就是在某一情景下解决某个问题的固定解决方式. 全部的创建型模式都是用作对象的创建或实例化的解决方式. 1 简单工厂模式 创建对象的最简单方法是使用new来创建一个对象,假设仅仅创建一种固 ...
- 设计模式学习之访问者模式(Visitor,行为型模式)(21)
参考:https://www.cnblogs.com/edisonchou/p/7247990.html 在患者就医时,医生会根据病情开具处方单,很多医院都会存在以下这个流程:划价人员拿到处方单之后根 ...
- 设计模式学习之中介者模式(Mediator,行为型模式)(18)
转载地址:http://www.cnblogs.com/zhili/p/MediatorPattern.html 一.引言 在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室.QQ群和短信 ...
- 责任链模式/chain of responsibility/行为型模式
职责链模式 chain of responsibility 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处 ...
- 6、单例模式 Singleton模式 只有一个实例 创建型模式
1.了解Singleton模式 程序在运行时,通常都会生成很多实例.例如,表示字符串的java . lang . string类的实例与字符串是- -对- -的关系,所以当有1000个字符串的时候,会 ...
- OOAD-设计模式(二)之GRASP模式与GOF设计模式概述
一.GRASP模式(通用责任分配软件模式)概述 1.1.理解责任 1)什么是责任 责任是类间的一种合约或义务,也可以理解成一个业务功能,包括行为.数据.对象的创建等 知道责任——表示知道什么 行为责任 ...
随机推荐
- 数据可视化之分析篇(二)Power BI 数据分析:客户购买频次分布
https://zhuanlan.zhihu.com/p/100070260 商业数据分析通常都可以简化为对数据进行筛选.分组.汇总的过程,本文通过一个实例来看看PowerBI是如何快速完成整个过程的 ...
- Integer和Long部分源码分析
Integer和Long的java中使用特别广泛,本人主要一下Integer.toString(int i)和Long.toString(long i)方法,其他方法都比较容易理解. Integer. ...
- python处理json总结
一.首先,了解下什么是JSON? JSON:JavaScript Object Notation [JavaScript 对象表示法] JSON 是一种轻量级的数据交换格式,完全独立于任何程序语言的文 ...
- C#根据反射动态创建ShowDoc接口文本信息
我目前每天主要工作以开发api为主,这都离不开接口文档.如果远程对接的话前端总说Swagger不清晰,只能重新找一下新的接口文档.ShowDoc就是一个不错的选择,简洁.大方.灵活部署. 但是话说回来 ...
- freeMarker随手笔记
freemarker官网:http://docs.freemarker.cn/ 注意: 1.如果标签没有嵌套内容(在开始标签和结束标签之间的内容),那么可以只使用开始标签 (详情:http://fre ...
- express中是如何处理IP的?
express获取client_ip req.ip // 获取客户端ip req.ips // 获取请求经过的客户端与代理服务器的Ip列表 查看源码 定义获取ip的入口, // 源码 request. ...
- EF Code 如何输出sql语句
首先写拷贝下面类 public class EFLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string catego ...
- 毫不留情地揭开 ArrayList 和 LinkedList 之间的神秘面纱
先看再点赞,给自己一点思考的时间,思考过后请毫不犹豫微信搜索[沉默王二],关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有技术大佬整理的面试题, ...
- Python操作adb命令脚本
一.脚本代码(有错误,抓紧改进中) import os import datetime date1=datetime.datetime.now().strftime('%Y%m%d') date2=d ...
- PHP操作Redis步骤详解
一.Redis连接与认证 $redis = new Redis(); //连接参数:ip.端口.连接超时时间,连接成功返回true,否则返回false $ret = $redis->connec ...