将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
【产品】:开发小哥,来活啦,咱们需要设计一款遥控器,核心功能就是几个按键,但是可能要控制很多不同品牌的设备,你们构思构思吧~
【开发】:按键?不存在的,对我来说就是请求罢了,Boss,帮我想一下怎么适配不同的品牌的设备啊?
【BOSS】:适配设备这个事,仅仅靠我们是不行的,这都是配合的结果,你既然也说了什么按钮只不过是请求而已,那可以考虑使用命令模式,把请求封装为对象,由我们主动去绑定不同品牌对应的执行者,懂了吗?
【开发】:哈?哦,懂了懂了(我懂个鬼!)
「父级接口」
public interface Command { void execute(); } 复制代码
「封装请求为一个对象」
public class LightOnCommand implements Command { Light light; public LightOnCommand(Light light) { this.light = light; } @Override public void execute() { light.on(); } } 复制代码
「请求响应的Api」
public class Light { /*** * on方法 */ public void on() { System.out.println("On..."); } /*** * off方法 */ public void off() { System.out.println("Off..."); } } 复制代码
「调用方代码」
public class SimpleRemoteControl { Command slot; public SimpleRemoteControl() {} public void setCommand(Command command) { slot = command; } public void buttonWasPressed() { slot.execute(); } } //****************************************** public static void main(String[] args) { SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); LightOnCommand lightOn = new LightOnCommand(light); remote.setCommand(lightOn); remote.buttonWasPressed(); LightOffCommand lightOff = new LightOffCommand(light); remote.setCommand(lightOff); remote.buttonWasPressed(); } 复制代码
命令模式的设计思路:
代码的核心即:把请求抽象为一个命令,把执行命令的接收者和命令本身分离,交由第三方类(Invoker)去管理,达到解耦的目的
Redis 即 REmote Dictionary Server (远程字典服务);
而Redis的协议规范是 Redis Serialization Protocol (Redis序列化协议)
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好
协议如下:
客户端以规定格式的形式发送命令给服务器
/*** * set key value 协议翻译如下: * * * 3 -> 表示以下有几组命令 * * $ 3 -> 表示命令长度是3 * SET * * $6 -> 表示长度是6 * keykey * * $5 -> 表示长度是5 * value * * 完整即: * * 3 * $ 3 * SET * $6 * keykey * $5 * value */ 复制代码
❝关于Redis相关的RESP协议,我在之后的文章会专门出一篇讲解~
❞
public class GetCommand implements Command { private GetReceiver receiver; private String arg; @Override public void execute() { receiver.doCommand(this.arg); } public GetCommand(GetReceiver receiver, String arg) { this.receiver = receiver; this.arg = arg; } } 复制代码
public class GetReceiver { OutputStream write; InputStream read; public void doCommand (String arg) { String[] strings = arg.split(" "); String key = strings[0]; byte[] bytes; try { String sb = "*2" + SPILT + "$3" + SPILT + "GET" + SPILT + "$" + key.getBytes().length + SPILT + key + SPILT; write.write(sb.getBytes()); bytes = new byte[1024]; read.read(bytes); System.out.println("Result: " + new String(bytes)); } catch (IOException e) { e.printStackTrace(); } } public GetReceiver(OutputStream write, InputStream read) { this.write = write; this.read = read; } final String SPILT = "\r\n"; } 复制代码
利用栈存储命令,可以很好的控制命令的变化等等
public class Invoker { private final Stack<Command> commands; public Invoker() { commands = new Stack<>(); } public void addCommand(Command command) { commands.push(command); } public void undoCommand() { if (!commands.empty()) { commands.pop(); } } public void execute() { while (!commands.empty()) { Command command = commands.pop(); command.execute(); } } } 复制代码
/*** * 简易Jedis代码, 利用栈存储命令(可根据需求更改数据结构) * * 推荐阅读顺序: * @see Command * @see GetCommand | SetCommand * @see GetReceiver | SetReceiver * @see Invoker */ public static void main(String[] args) throws IOException { // 初始化Socket流 Socket socket = new Socket("127.0.0.1", 6379); OutputStream write = socket.getOutputStream(); InputStream read = socket.getInputStream(); Invoker invoker = new Invoker(); // 初始化Get | Set任务执行者 GetReceiver getReceiver = new GetReceiver(write, read); SetReceiver setReceiver = new SetReceiver(write, read); // 测试get命令 invoker.addCommand(new GetCommand(getReceiver, "key")); // 测试set命令 invoker.addCommand(new SetCommand(setReceiver, "key xixixi")); // 测试get命令 invoker.addCommand(new GetCommand(getReceiver, "key")); // 测试get命令 invoker.addCommand(new GetCommand(getReceiver, "key")); // 测试撤销上一个命令 -> 输出四次则测试失败,三次则成功 invoker.undoCommand(); invoker.execute(); } 复制代码
输出结果:
/*** * Result: $4 * test * * Result: +OK * * Result: $6 * xixixi */ // 测试成功~ 复制代码
❝代码量有点小多,需要看详情的话,请跳转到最下面的相关代码链接吧~
❞
在下列情况下可以使用 Command Method模式:
在日常生活中都有订单的概念,为什么我们下订单,服务员或者其他工作人员完全明白我们的意图呢?就是因为我们按照他们制定的规则构建起了一个命令,那么在交互过程就不需要层层沟通,方便解耦。
GitHub地址