设计模式(十)Strategy模式
Strategy模式,就是用来整体地替换算法,可以轻松地以不同的算法解决同一个问题。
还是根据一个示例程序来理解这种设计模式吧。先看一下示例程序的类图。

然后看示例程序代码。
package bigjunoba.bjtu.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; // 表示布的值
public static final Hand[] hand = { // 表示猜拳中3种手势的实例
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA),
};
private static final String[] name = { // 表示猜拳中手势所对应的字符串
"石头", "剪刀", "布",
};
private int handvalue; // 表示猜拳中出的手势的值
private Hand(int handvalue) {
this.handvalue = handvalue;
}
public static Hand getHand(int handvalue) { // 根据手势的值获取其对应的实例
return hand[handvalue];
}
public boolean isStrongerThan(Hand h) { // 如果this胜了h则返回true
return fight(h) == 1;
}
public boolean isWeakerThan(Hand h) { // 如果this输给了h则返回true
return fight(h) == -1;
}
private int fight(Hand h) { // 计分:平0, 胜1, 负-1
if (this == h) {
return 0;
} else if ((this.handvalue + 1) % 3 == h.handvalue) {
return 1;
} else {
return -1;
}
}
public String toString() { // 转换为手势值所对应的字符串
return name[handvalue];
}
}
Hand类是用来表示猜拳中“手势”的类,首先创建了Hand类的实例,并将它们保存在hand数组中。getHand方法的作用是,将手势的值作为参数传递给getHand方法,它就会将手势的值对应的Hand类的实例返回。判断猜拳结果比较有意思,如果hand1赢了hand2,那么可以用hand1.isStrongerThan(hand2)来表示,反之如果hand1输了hand2,那么可以用hand1.isWeakerThan(hand2)来表示。fight方法是用来比较this和h的,如果this的手势值加1后是h的手势值,那么this获胜。例如this是石头(0),h是剪刀(1);或者this是剪刀(1)而h是布(2);或者this是布(2)而h是石头(0)。这里的取余是因为2加上1后除以3的余数正好是0,也就是石头。这里的Hand类被其他类使用,但是它不是strategy模式的一部分。
package bigjunoba.bjtu.strategy;
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
Strategy接口定义了猜拳策略的抽象方法接口。nextHand方法的作用是“获取下一局要出的手势”,study方法是学习“上一局的手势是否获胜了”。
package bigjunoba.bjtu.strategy;
import java.util.Random;
public class WinningStrategy implements Strategy {
private Random random;
private boolean won = false;
private Hand prevHand;
public WinningStrategy(int seed) {
random = new Random(seed);
}
public Hand nextHand() {
if (!won) {
prevHand = Hand.getHand(random.nextInt(3));
}
return prevHand;
}
public void study(boolean win) {
won = win;
}
}
WinningStrategy类实现了Strategy接口。这种猜拳策略是,如果上一局的手势赢了,则下一局的手势就与上局相同;如果上一局手势输了,那下一局就随机出手势。won字段中保存的是上一局猜拳的输赢结果,如果上一局赢了,那么won值为true,然后nextHand方法直接返回prevHand,study方法调用study(True)。
package bigjunoba.bjtu.strategy;
import java.util.Random;
public class ProbStrategy implements Strategy {
private Random random;
private int prevHandValue = 0;
private int currentHandValue = 0;
private int[][] history = {
{ 1, 1, 1, },
{ 1, 1, 1, },
{ 1, 1, 1, },
};
public ProbStrategy(int seed) {
random = new Random(seed);
}
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;
}
prevHandValue = currentHandValue;
currentHandValue = handvalue;
return Hand.getHand(handvalue);
}
private int getSum(int hv) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += history[hv][i];
}
return sum;
}
public void study(boolean win) {
if (win) {
history[prevHandValue][currentHandValue]++;
} else {
history[prevHandValue][(currentHandValue + 1) % 3]++;
history[prevHandValue][(currentHandValue + 2) % 3]++;
}
}
}
ProbStrategy类是随机出手势,但是每种手势出现的概率会根据以前的猜拳结果而改变。这里的理解就是,假如上一局出的是石头,那么history【0】【0】表示两局分别出石头和石头时胜了的次数,同理history【0】【1】表示两局分别出石头和剪刀时胜了的次数,history【0】【2】表示两局分别出石头和布时胜了的次数。这三个的值假如是3/5/7的情况下,下一局就会以石头、剪刀和布的比率为3:5:7来决定,在0到15之间取一个随机数,如果随机数是0 1 2那么出石头,如果随机数在3 4 5 6 7 那么出剪刀,如果随机数是9 10 11 12 13 14 15那么出布。
package bigjunoba.bjtu.strategy;
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++;
}
public String toString() {
return "[" + name + ":" + gamecount + " games, " + wincount + " win, " + losecount + " lose" + "]";
}
}
Player类表示进猜拳游戏选手的类。nextHand方法的返回值就是策略的nextHand方法的返回值,也就是将自己的工作委托给了strategy。
package bigjunoba.bjtu.strategy;
public class Main {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: java Main randomseed1 randomseed2");
System.out.println("Example: java Main 314 15");
System.exit(0);
}
int seed1 = Integer.parseInt(args[0]);
int seed2 = Integer.parseInt(args[1]);
Player player1 = new Player("Lianjiang", new WinningStrategy(seed1));
Player player2 = new Player("Qiaoye", new ProbStrategy(seed2));
for (int i = 0; i < 20; 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);
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());
}
}
main类负责让电脑进行猜拳游戏。Lianjiang和Qiaoye分别使用不同的策略进行了100局比赛。这里必须输入两个数作为随机数的种子,关于这一方面,目前还是不太理解,等到理解了再来解释。
Even...
Winner:[Qiaoye:1 games, 0 win, 0 lose]
Winner:[Lianjiang:2 games, 0 win, 1 lose]
Even...
Winner:[Qiaoye:4 games, 1 win, 1 lose]
Winner:[Lianjiang:5 games, 1 win, 2 lose]
Even...
Even...
Winner:[Lianjiang:8 games, 2 win, 2 lose]
Winner:[Lianjiang:9 games, 3 win, 2 lose]
Winner:[Lianjiang:10 games, 4 win, 2 lose]
Even...
Winner:[Qiaoye:12 games, 2 win, 5 lose]
Even...
Winner:[Lianjiang:14 games, 5 win, 3 lose]
Winner:[Qiaoye:15 games, 3 win, 6 lose]
Winner:[Qiaoye:16 games, 4 win, 6 lose]
Winner:[Lianjiang:17 games, 6 win, 5 lose]
Winner:[Qiaoye:18 games, 5 win, 7 lose]
Even...
Total result:
[Lianjiang:20 games, 7 win, 6 lose]
[Qiaoye:20 games, 6 win, 7 lose]
输出结果如上,进行了20局的结果。
Strategy模式类图如下:

Strategy模式主要思想就是将算法与其他部分分离开,只定义了与算法相关的接口,然后在程序中以委托的方式来使用算法。使用委托这种弱关联关系可以很方便地整体替换算法,或者选择更好的算法在不同的环境下运行。
设计模式(十)Strategy模式的更多相关文章
- 设计模式之Strategy模式
策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具体的共同接口的独立类中,从而使得他们可以互相替换. 策略模式使得算法可以在不影响客户端的情况下发生变化. all in one “ ...
- 设计模式十: 生成器模式(Builder Pattern)
简介 生成器模式属于创建型模式的一种, 又叫建造者模式. 生成器模式涉及4个关键角色:产品(Product),抽象生成器(builder),具体生成器(ConcreteBuilder),指挥者(Dir ...
- 设计模式:strategy模式
思想:将算法进行抽象,然后使用桥接的模式使用算法的抽象接口,达到算法整体替换的目的 理解:和桥接模式相同,只是桥接的两边分开的思想不同 例子: class Algrithm //算法的抽象 { pub ...
- 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...
- 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...
- 设计模式 ( 十九 ) 模板方法模式Template method(类行为型)
设计模式 ( 十九 ) 模板方法模式Template method(类行为型) 1.概述 在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行 ...
- 【转】设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)
设计模式 ( 十五 ) 中介者模式Mediator(对象行为型) 1.概述 在面向对象的软件设计与开发过程中,根据"单一职责原则",我们应该尽量将对象细化,使其只负责或呈现单一的职 ...
- 设计模式:Strategy 策略模式 -- 行为型
设计模式 策略模式Strategy(对象行为型) 这是几年前写的文字(转载做的笔记更准确些),发觉还是废话多了点. 其实,核心就是5.结构中的UML图 5.1 和 5.2(新增).现在看这张图就觉得一 ...
- 设计模式 ( 十八 ):State状态模式 -- 行为型
1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ellse语句来做状态判断来进行不同情况的处理.但是对 ...
随机推荐
- Spring MVC-从零开始-@RequestMapping 注解headers 属性
package com.jt; import org.springframework.stereotype.Controller; import org.springframework.web.bin ...
- Unity - 存读档机制简析
本文旨在于简要分析Unity中的两种存档机制,即:PlayerPrefs数据持久化方法及Serialization数据序列化方法 较比与源项目,我另加了JSON方法.XML方法等及一些Unity设置, ...
- SpringCloudEureka入门
说明 SpringBoot版本 2.1.7.RELEASE SpringCloud版本 Greenwich.SR2 创建eureka server工程 加入pom依赖 <dependencies ...
- Laravel Entrust 权限管理扩展包的使用笔记
简介 Entrust 是一个简洁而灵活的基于角色进行权限管理的 Laravel 扩展包.针对 Laravel 5,官方推荐的安装版本是 5.2.x-dev.它的详细使用方法请查看 Entrust Gi ...
- 【实战】 elasticsearch 写入速度提升的案例分享
文章首发投稿至InfoQ,[侠梦的开发笔记]公众号,欢迎关注 https://www.infoq.cn/article/t7b52mbzxqkwrrdpVqD2 基本配置 基本配置,5台配置为 24C ...
- LitePal的聚合函数
传统的聚合函数用法 虽说是聚合函数,但它的用法其实和传统的查询还是差不多的,即仍然使用的是select语句.但是在select语句当中我们通常不会再去指定列名,而是将需要统计的列名传入到聚合函数当 ...
- 【线性表基础】顺序表和单链表的插入、删除等基本操作【Java版】
本文表述了线性表及其基本操作的代码[Java实现] 参考书籍 :<数据结构 --Java语言描述>/刘小晶 ,杜选主编 线性表需要的基本功能有:动态地增长或收缩:对线性表的任何数据元素进行 ...
- Rust入坑指南:常规套路
搭建好了开发环境之后,就算是正式跳进Rust的坑了,今天我就要开始继续向下挖了. 由于我们初来乍到 ,对Rust还不熟悉,所以我决定先走一遍常规套路. 变不变的变量 学习一门语言第一个要了解的当然就是 ...
- Java的动手动脑
动手动脑及课后实 仔细阅读示例: EnumTest.java,运行它,分析运行结果? public class EnumTest { public static void main(String[] ...
- e课表项目第二次冲刺周期第三天
昨天干了什么? 昨天和我们组的组员商量,确定了第二个界面的框架内容,即内容的输入和完成按钮,然后通过在网上搜索图片资源,然后我们利用ps软件,将图片通通设置了大小,进行了裁剪,最后为我们所利用,实现第 ...