当前位置 博文首页 > 韩超的博客 (hanchao5272):设计模式-命令模式-以游戏快捷键为例
超级链接: Java常用设计模式的实例学习系列-绪论
参考:《HeadFirst设计模式》
命令模式是一种行为型
模式。
命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
本文以游戏快捷键
为场景来学习命令模式
:
空格
键,则角色跳跃;按下W
键,则角色前进。角色跳跃
的快捷键是空格
,用户可以自定义为回车
键。Q
,则依次进行:角色前进、释放技能:潜行术、释放技能:火球术。无论如何实现三个需求,我们先来实现按键本身。
游戏按键:枚举:KeyEnum
/**
* <p>按键(部分)</P>
*
* @author hanchao
*/
public enum KeyEnum {
/**
* 空格键
*/
KEY_SPACE,
/**
* 按键:回车
*/
KEY_ENTER,
/**
* 按键:A
*/
KEY_A,
/**
* 按键:B
*/
KEY_B,
/**
* 按键:V
*/
KEY_V,
/**
* 按键:W
*/
KEY_W,
/**
* 按键:1
*/
KEY_1,
/**
* 按键:2
*/
KEY_2,
/**
* 按键:无
*/
KEY_NULL
}
无论如何实现三个需求,我们先来实现游戏操作本身。
游戏操作:角色相关:Role
/**
* <p>游戏角色</P>
*
* @author hanchao
*/
@Slf4j
public class Role {
/**
* 前进
*/
public static void forward() {
log.info("角色前进");
}
/**
* 跳跃
*/
public static void jump() {
log.info("角色跳跃");
}
}
游戏操作:技能相关:SkillHandler
/**
* <p>技能管理器</P>
*
* @author hanchao
*/
@Slf4j
public class SkillHandler {
/**
* 释放技能-五火球神术
*/
public static void fireBall() {
log.info("释放技能:火球术...酝酿1秒钟...砰! 砰!! 砰!!!");
}
/**
* 释放技能-潜行
*/
public static void sneak() {
log.info("释放技能:潜行术...蒙面...抛出烟雾弹...不见了!");
}
}
游戏操作:界面相关:UIHandler
/**
* <p>游戏界面</P>
*
* @author hanchao
*/
@Slf4j
public class UiHandler {
/**
* 打开背包界面
*/
public static void showPack() {
log.info("打开背包界面,背包中现在有:2个面包,3棵草药,5块矿石...");
}
/**
* 打开技能界面
*/
public static void showSkill() {
log.info("打开技能界面,已掌握的技能有:暗影之舞,剑刃风暴,神圣之光...");
}
}
实现思路:
实现命令模式的关键在于:将命令/请求封装为一个对象
。
在我们的例子中,控制角色向前
、释放技能:火球术
、打开背包界面
等等,都是一种命令/请求
。
这些命令可以抽象为一个抽象类:命令Command
。
/**
* <p>命令:执行、撤销</P>
*
* @author hanchao
*/
public interface Command {
/**
* 执行命令
*/
void execute();
}
命令实现:角色跳跃:RoleJumpCommand
角色前进:RoleForwardCommand实现方式类似。
/**
* <p>命令:跳跃</P>
*
* @author hanchao
*/
public class RoleJumpCommand implements Command {
/**
* 执行命令
*/
@Override
public void execute() {
Role.jump();
}
}
命令实现:释放技能:火球术:ReleaseFireBallCommand
释放技能:潜行术:ReleaseSneakCommand实现方式类似。
/**
* <p>命令:释放技能:火球术</P>
*
* @author hanchao
*/
public class ReleaseFireBallCommand implements Command {
/**
* 执行命令
*/
@Override
public void execute() {
SkillHandler.fireBall();
}
}
命令实现:打开背包界面:ShowPackCommand
打开技能界面:ShowSkillCommand实现方式类似。
/**
* <p>命令:打开背包界面</P>
*
* @author hanchao
*/
public class ShowPackCommand implements Command {
/**
* 执行命令
*/
@Override
public void execute() {
UiHandler.showPack();
}
}
命令实现:默认命令:DefaultCommand
可能存在这种情况:某个按键并未关联任何命令,如果按下此键,应该不会产生任何效果。
为了统一处理上述情况,定义一种默认命令,这样就不必专门去进行非空判断。
/**
* <p>默认命令</P>
*
* @author hanchao
*/
@Slf4j
public class DefaultCommand implements Command {
/**
* 执行命令
*/
@Override
public void execute() {
log.info("什么也没有发生");
}
}
命令实现:宏命令:MacroCommand
所谓宏命令,就是一次按键,产生多个游戏操作。其实,宏命令本身也是一种命令。
为了实现多个游戏操作
的需求,我们可以通过集合
来实现。
/**
* <p>宏命令</P>
* <p>
* 需求三:可以自定义宏命令,一个宏命令可以进行多个操作。
*
* @author hanchao
*/
@AllArgsConstructor
public class MacroCommand implements Command {
/**
* 宏命令列表
*/
private List<Command> commandList;
/**
* 执行命令
*/
@Override
public void execute() {
//依次执行命令
for (Command command : commandList) {
command.execute();
}
}
}
下面编写客户代码。
首先,定义按键管理器``KeyManager`,主要关注点:按键初始化:设置默认按键。
/**
* <p>调用者:按键管理器</P>
*
* @author hanchao
*/
@Slf4j
public class KeyManager {
/**
* 假设共计32种游戏操作
*/
private static final int MAX_SIZE = 32;
/**
* 快捷键列表
*/
private static Map<KeyEnum, Command> commandMap = new HashMap<>(MAX_SIZE);
static {
//按键初始化:设置默认按键
commandMap.put(KeyEnum.KEY_W, new RoleForwardCommand());
commandMap.put(KeyEnum.KEY_SPACE, new RoleJumpCommand());
commandMap.put(KeyEnum.KEY_B, new ShowPackCommand());
commandMap.put(KeyEnum.KEY_V, new ShowSkillCommand());
commandMap.put(KeyEnum.KEY_1, new ReleaseFireBallCommand());
commandMap.put(KeyEnum.KEY_2, new ReleaseSneakCommand());
commandMap.put(KeyEnum.KEY_NULL, new DefaultCommand());
}
}
需求一:通过按键进行快捷操作。例如:按下空格
键,则角色跳跃;按下W
键,则角色前进。
在按键管理器``KeyManager中定义按键
press()`方法,实现需求一。
/**
* 需求一:通过按键进行快捷操作。
*/
public static void press(KeyEnum key) {
//如果旧的按键为空,则置为空按键
if (Objects.isNull(key)) {
key = KeyEnum.KEY_NULL;
}
log.info("按下了「{}」", key.name());
//执行此按键
Command command = commandMap.get(key);
if (Objects.isNull(command)){
command = new DefaultCommand();
}
command.execute();
log.info("----------");
}
需求二:可以替换快捷键。例如:默认角色跳跃
的快捷键是空格
,用户可以自定义为回车
键。
在按键管理器``KeyManager中定义设置自定义按键
setCustomKey()`方法,实现需求二。
/**
* 需求二:可以替换快捷键。
*/
public static void setCustomKey(Command command, KeyEnum oldKey, KeyEnum newKey) {
//如果旧的按键为空,则置为空按键
if (Objects.isNull(oldKey)) {
oldKey = KeyEnum.KEY_NULL;
}
//如果新的按键为空,则置为空按键
if (Objects.isNull(newKey)) {
newKey = KeyEnum.KEY_NULL;
}
log.info("将「{}」操作的快捷键进行替换:{} --> {}", command.getClass().getSimpleName(), oldKey.name(), newKey.name());
//将旧按键绑定到默认操作
if (!Objects.equals(oldKey, KeyEnum.KEY_NULL)) {
commandMap.put(oldKey, new DefaultCommand());
}
//将新按键绑定到当前操作
commandMap.put(newKey, command);