【一起学系列】之命令模式:封装一个简单Jedis
意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式的诞生
【产品】:开发小哥,来活啦,咱们需要设计一款遥控器,核心功能就是几个按键,但是可能要控制很多不同品牌的设备,你们构思构思吧~
【开发】:按键?不存在的,对我来说就是请求罢了,Boss,帮我想一下怎么适配不同的品牌的设备啊?
【BOSS】:适配设备这个事,仅仅靠我们是不行的,这都是配合的结果,你既然也说了什么按钮只不过是请求而已,那可以考虑使用命令模式,把请求封装为对象,由我们主动去绑定不同品牌对应的执行者,懂了吗?
【开发】:哈?哦,懂了懂了(我懂个鬼!)
HeadFirst 核心代码
父级接口
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();
}
命令模式的设计思路:
- Command 声明命令的接口
- ConcreteCommand 具体的动作 | 命令
- Client 客户端请求
- Invoker 绑定命令与接收者
- Receiver 接收者 知道如何实施与执行一个请求相关的操作,任何类都可以是接收者
代码的核心即:把请求抽象为一个命令,把执行命令的接收者和命令本身分离,交由第三方类(Invoker)去管理,达到解耦的目的
试试用命令模式封装简单Jedis
Redis协议Tips
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协议,我在之后的文章会专门出一篇讲解~
封装Get命令
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;
}
}
封装Get接收者
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";
}
封装Invoker
利用栈存储命令,可以很好的控制命令的变化等等
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模式:
- 需要抽象出待执行的动作以参数化某对象
- 在不同的时刻指定,排列和执行请求
- 支持取消操作
Code/生活中的实际应用
在日常生活中都有订单的概念,为什么我们下订单,服务员或者其他工作人员完全明白我们的意图呢?就是因为我们按照他们制定的规则构建起了一个命令,那么在交互过程就不需要层层沟通,方便解耦。
UML图
遵循的设计原则
- 针对接口编程,不针对实现编程
- 为交互对象松耦合设计而努力
- 类应该对拓展开放,对修改关闭
相关代码链接
- 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
- 提供了友好的阅读指导
【一起学系列】之命令模式:封装一个简单Jedis的更多相关文章
- Directx11学习笔记【四】 封装一个简单的Dx11DemoBase
根据前面两个笔记的内容,我们来封装一个简单的基类,方便以后的使用. 代码和前面类似,没有什么新的内容,直接看代码吧(由于代码上次都注释了,这次代码就没怎么写注释o(╯□╰)o) Dx11DemoBas ...
- 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件
先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...
- 代码改变世界 | 如何封装一个简单的 Koa
下面给大家带来:封装一个简单的 Koa Koa 是基于 Node.js 平台的下一代 web 开发框架 Koa 是一个新的 web 框架,可以快速而愉快地编写服务端应用程序,本文将跟大家一起学习:封装 ...
- python+selenium之自定义封装一个简单的Log类
python+selenium之自定义封装一个简单的Log类 一. 问题分析: 我们需要封装一个简单的日志类,主要有以下内容: 1. 生成的日志文件格式是 年月日时分秒.log 2. 生成的xxx.l ...
- Python之自定义封装一个简单的Log类
参考:http://www.jb51.net/article/42626.htm 参考:http://blog.csdn.net/u011541946/article/details/70198676 ...
- Python+Selenium中级篇之8-Python自定义封装一个简单的Log类《转载》
Python+Selenium中级篇之8-Python自定义封装一个简单的Log类: https://blog.csdn.net/u011541946/article/details/70198676
- C#设计模式系列:命令模式(Command)
1.命令模式简介 1.1>.定义 命令模式的目的是解除命令发出者和接收者之间的紧密耦合关系,使二者相对独立,有利于程序的并行开发和代码的维护.命令模式的核心思想是将请求封装为一个对象,将其作为命 ...
- 设计模式总结篇系列:命令模式(Command)
在程序设计中,经常会遇到一个对象需要调用另外一个对象的某个方法以达到某种目的,在此场景中,存在两个角色:请求发出者和请求接收者.发出者发出请求,接收者接收请求并进行相应处理.有时候,当需要对请求发出者 ...
- Java设计模式系列之命令模式
命令模式(Command)的定义 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作,将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. ...
随机推荐
- Spring Cloud Alibaba系列(四)使用gateway作为服务网关
什么是网关 在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计.开发.测试.部署和管理.这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计, ...
- SQL中游标的使用示例
declare @email_source varchar(MAX); --1.原始发件人字段 declare @key_name varchar(50); --2.我方卷号或客户代码 declare ...
- selenium(5)-解读强制等待,隐式等待,显式等待的区别
背景 为什么要设置元素等待 因为,目前大多数Web应用程序都是使用Ajax和Javascript开发的:每次加载一个网页,就会加载各种HTML标签.JS文件 但是,加载肯定有加载顺序,大型网站很难说一 ...
- 从 0 开始机器学习 - 神经网络反向 BP 算法!
最近一个月项目好忙,终于挤出时间把这篇 BP 算法基本思想写完了,公式的推导放到下一篇讲吧. 一.神经网络的代价函数 神经网络可以看做是复杂逻辑回归的组合,因此与其类似,我们训练神经网络也要定义代价函 ...
- 【K8s学习笔记】K8s是如何部署应用的?
本文内容 本文致力于介绍K8s一些基础概念与串联部署应用的主体流程,使用Minikube实操 基础架构概念回顾 温故而知新,上一节[K8S学习笔记]初识K8S 及架构组件 我们学习了K8s的发展历史. ...
- MySQL-数据库和表的基本操作
数据库和表的基本操作 数据库基础知识 创建数据库 CREATE DATABASE 数据库名称 ; 查看数据库(显示数据库名列表) SHOW DATABASES ; 查看某数据库信息(显示创建的信息) ...
- Python三大器之装饰器
Python三大器之装饰器 开放封闭原则 一个良好的项目必定是遵守了开放封闭原则的,就比如一段好的Python代码必定是遵循PEP8规范一样.那么什么是开放封闭原则?具体表现在那些点? 开放封闭原则的 ...
- Python3-datetime模块-日期与时间
官方文档 http://python.usyiyi.cn/translate/python_352/library/datetime.html 代码示例 from datetime import da ...
- Spring Bean各阶段生命周期的介绍
一.xml方式配置bean 二.Aware接口 2.1 BeanNameAware 2.2 BeanFactoryAware 2.3 ApplicationContextAware 2.4 Aware ...
- 用Creator实现一个擀面的效果
先上几张效果图 怎么实现的呢? 节点介绍 1是背景图,可以忽略:2 是准备好的面团:3 是擀好的面饼先隐藏:4 是需要绘制的节点:5 是擀面杖. 制作开始 首先在view上挂一个mask,并且设置为模 ...