**可以整体的替换一套算法,这就是策略模式。**这样对于同一个问题,可以有多种解决方案——算法实现的时候,可以通过策略模式来非常方便的进行算法的整体替换,而各种算法是独立封装好的,不用修改其内部逻辑。
具体的实战,下面给出一个经典案例——“猜拳游戏”。该示例来自于【日】结城浩的《图解设计模式》,策略算法做了一些简化调整。
“石头剪刀布”的游戏每轮出什么样的手势,可以遵循一定的策略。比如可以按照下面两种策略来出手势:
- 看上一轮自己出的手势,如果赢了,继续用上一轮出的手势;否则出任意的手势
- 看上一轮对方出的手势
- 如果赢了,就出和上一轮不一样的手势;
- 如果上一轮平了(比如出的剪刀),本轮就出石头;
- 如果上一轮输了,本轮就出上一轮和对方一样的手势
文章目录
- 整体类图设计
- 出手势策略接口
- 手势类
- 玩家类
- 策略实现类
- 测试类
整体类图设计
出手势策略接口
因此抽象出一个出手势的策略接口:
/**
* Java小卷带你轻松高效学编程,一对一辅导加q1372569394
*/
package com.juan.java.designpattern.strategy;
/**
*
* @author Java小卷
* @date 2024-12-04 14:16
* @since 1.0
*/
public interface Strategy {
/**
* 下一回合出手势的方法
* @author Java小卷
* @date 2024/12/5 14:04
* @return 出的手势
* @since 1.0
*/
Hand nextHand();
/**
* 仔细考虑上一轮的结果,作为下一轮出手势的策略的依据
* @author Java小卷
* @date 2024/12/5 14:07
* @param result 上一轮的结果 0-打平 1-胜 -1-负
* @param other 上一轮对方的手势
* @since 1.0
*/
void study(int result, Hand other);
}
手势类
封装了手势具体信息和比手势的方法。
这里会维护3个公开的静态常量来分别维护“石头、剪刀、布”的手势数值。
/** 出的石头 */
public static final int HANDVALUE_STONE = 0;
/** 出的剪刀 */
public static final int HANDVALUE_SCISSORS = 1;
/** 出的布 */
public static final int HANDVALUE_PAPER = 2;
为方便对三种手势对象的获取,这里的手势值和三种手势对象的数组的索引值保持一致。
在Hand
类中维护一个私有的静态数组常量,初始化3个手势对象:
/** 初始化三种手势:石头、剪刀、布 */
private static final Hand[] HANDS = {
new Hand(HANDVALUE_STONE),
new Hand(HANDVALUE_SCISSORS),
new Hand(HANDVALUE_PAPER)
};
同时维护一个对应手势名称的静态数组常量:
/** 描述出的手势的名称数组 */
private static final String[] HAND_NAMES = {"石头", "剪刀", "布"};
提供一个代表所出的手势值的成员变量,并提供相应的构造方法完成其初始化,注意Hand
类不能在外部实例化,只能在内部维护,因此用private
修饰:
/** 出的当前手势值 */
private final int handValue;
/** 私有的带有手势值的构造 */
private Hand(int handValue) {
this.handValue = handValue;
}
提供几个获取手势信息的方法:
/**
* 根据手势值获取对应的手势对象
* @param handValue 手势值
* @return 对应的手势对象
*/
public static Hand getHand(int handValue) {
return HANDS[handValue];
}
/**
* 返回当前实例的手势名称
* @author Java小卷
* @date 2024/12/4 10:57
* @return 手势名称
* @since 1.0
*/
@Override
public String toString() {
return HAND_NAMES[handValue];
}
/**
* 获取手势数值
* @author Java小卷
* @date 2024/12/5 15:14
* @return int 手势数值
* @since 1.0
*/
public int getHandValue() {
return handValue;
}
提供比较手势的方法
/**
* 手势对战方法
* @author Java小卷
* @date 2024/12/4 10:42
* @param hand 对方出的手势
* @return int 0-平局 1-胜 -1-负
* @since 1.0
*/
private int fight(Hand hand) {
// 自身比较无意义,但这里也记为平局
if (this == hand) {
return 0;
} else if ((this.handValue + 1) % 3 == hand.handValue) {
// 游戏规则:石头>剪刀>布>石头
// 因此,只要按照数组的顺序,当前手势的下一个元素与对方手势相等,就认为自己赢了
return 1;
} else {
// 其他情况都是判负
return -1;
}
}
/**
* 对外提供的判断赢了方法
* @author Java小卷
* @date 2024/12/4 10:55
* @param hand 对方手势
* @return boolean
* @since 1.0
*/
public boolean isStrongThan(Hand hand) {
return this.fight(hand) == 1;
}
玩家类
Player
类在策略模式的类设计中,作为Context
,由它负责设置和切换策略并调用策略的行为。
该玩家类除了name
、strategy
属性外,还包含了全局的状态信息(比赛总轮数、胜的轮数、败的轮数)。另外提供了调用策略对象完成出手势的方法以及对一轮比赛结果处理的方法。
策略实现类
正如前面一开始介绍的出手势的两种实现策略,这里提供两种具体的实现:
/**
* Java小卷带你轻松高效学编程,一对一辅导加q1372569394
*/
package com.juan.java.designpattern.strategy.impl;
import ...
/**
*
* @author Java小卷
* @date 2024-12-04 14:21
* @since 1.0
*/
public class WinningStrategy implements Strategy {
private final Random random;
private int prevResult;
private Hand prevHand;
public WinningStrategy() {
random = new Random();
}
@Override
public Hand nextHand() {
// 前一轮不胜,则随意出
if (prevResult != 1) {
prevHand = Hand.getHand(random.nextInt(3));
}
// 否则出上一轮的手势
return prevHand;
}
@Override
public void study(int result, Hand other) {
this.prevResult = result;
}
}
/**
* Java小卷带你轻松高效学编程,一对一辅导加q1372569394
*/
package com.juan.java.designpattern.strategy.impl;
import ...
/**
*
* @author Java小卷
* @date 2024-12-04 15:38
* @since 1.0
*/
public class SmartStrategy implements Strategy {
private final Random random;
private int prevResult = 1;
private Hand prevOtherHand;
public SmartStrategy() {
this.random = new Random();
}
@Override
public Hand nextHand() {
// 如果前一轮平,则出比它大的
if (prevResult == 0) {
return Hand.getHand(Math.floorMod(prevOtherHand.getHandValue() - 1, 3));
} else if (prevResult == -1) {
// 输了则出和对方一样的
return Hand.getHand(prevOtherHand.getHandValue());
} else {
// 赢了,则出不一样的
if (prevOtherHand == null) {
return Hand.getHand(random.nextInt(3));
} else {
Hand prevHand = Hand.getHand(Math.floorMod(prevOtherHand.getHandValue() - 1, 3));
// 出和prevHand不一样的随机手势
return Hand.getHand(Math.floorMod(prevHand.getHandValue() + random.nextInt(2) + 1, 3));
}
}
}
@Override
public void study(int result, Hand other) {
this.prevResult = result;
this.prevOtherHand = other;
}
}
测试类
/**
* Java小卷带你轻松高效学编程,一对一辅导加q1372569394
*/
package com.juan.java.designpattern.strategy;
import ...
/**
*
* @author Java小卷
* @date 2024-12-04 14:46
* @since 1.0
*/
public class Main {
public static void main(String[] args) {
Player player1 = new Player("糖宝", new WinningStrategy());
Player player2 = new Player("小卷", new SmartStrategy());
// 比赛10轮
for (int i = 0; i < 10; i++) {
Hand hand1 = player1.nextHand();
Hand hand2 = player2.nextHand();
if (hand1.isStrongThan(hand2)) {
System.out.println("胜者: " + player1);
player1.win(hand2);
player2.lose(hand1);
} else if (hand2.isStrongThan(hand1)) {
System.out.println("胜者: " + player2);
player2.win(hand1);
player1.lose(hand2);
} else {
System.out.println("打平...");
player1.even(hand2);
player2.even(hand1);
}
}
System.out.println("最终结果:");
System.out.println(player1);
System.out.println(player2);
}
}
程序输出: