博客原文地址 折腾Java设计模式之命令模式

命令模式

wiki上的描述 Encapsulate a request as an object, thereby allowing for the parameterization of clients with different requests, and the queuing or logging of requests. It also allows for the support of undoable operations.

翻译意思,把请求封装成一个对象,从而允许我们可以对客户端的不同请求进行参数化,以及对请求进行排队或记录。还允许支持撤销操作。看起来好像很复杂,很难理解。

通俗简单理解,它就是将请求封装成一个对象,在这里就是这个对象就是命令,而这个命令就是将请求方和执行方分离隔开。从而每一个命令其实就是操作,而这样的流程就是请求方发出请求要求执行某操作,接收方收到请求后并执行对应的操作。这样下来,请求方和接收方就解耦了,使得请求方完全不知道接受的操作方法,从也不会知道接收方是何时接受到请求的,又是何时执行操作的,又是怎么执行操作的。

具体的角色

Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。

ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。

Invoker(请求方):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。

Receiver(接收方):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

Client(客户端):创建具体命令的对象并且设置命令对象的接受者。

再来看看UML图

从上方的时序图中可以看出运行的顺序,Invoker执行execute方法,调用Command1对象,Command1执行action1方法调用Receiver1对象。

干货代码

源码在我的GitHub地址

普通的命令模式

现在结合下上回说到的状态模式一起来实现这个风扇的左转和右转功能,这次把他用命令模式来代替之前风扇的转动,把它当做命令来。

客户端简单的定义请求方和接收方以及对于的左转命令和右转命令,设置命令后对应的执行命令。

public class Client {

    public static void main(String[] args) {
Invoker invoker = new Invoker();
Receiver receiver = new Receiver();
Command leftCommand = new LeftCommand(receiver);
Command rightCommand = new RightCommand(receiver); invoker.setCommand(rightCommand);
invoker.execute();
invoker.execute();
invoker.execute(); invoker.setCommand(leftCommand);
invoker.execute();
invoker.execute();
}
}

请求方

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Invoker { private Command command; public void execute() {
command.execute();
} }

抽象命令

public interface Command {

    void execute();
}

开关左转

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LeftCommand implements Command { private Receiver receiver; @Override
public void execute() {
receiver.left();
}
}

开关右转

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RightCommand implements Command { private Receiver receiver; @Override
public void execute() {
receiver.right();
}
}

接收方

public class Receiver {

    private Context context = new Context(new CloseLevelState());

    public void left() {
context.left();
} public void right() {
context.right();
}
}

通过命令模式把左转和右转封装成命令,以及之前的状态模式变更风扇的状态。本次就是通过状态模式和命令模式实现了一个风扇开关左右转的功能。

宏命令或者叫做组合命令

设计一组命令,简单的处理事情,打印一句话,封装成一组命令。这次我们用了Java8来写,可以使用lambda。

@Slf4j
public class Client { public static void main(String[] args) {
Invoker invoker = new Invoker();
log.info("初始化ABC3个命令");
Command aCommand = () -> log.info("A处理这个请求");
invoker.addCommand(aCommand);
invoker.addCommand(() -> log.info("B处理这个请求"));
invoker.addCommand(() -> log.info("C处理这个请求"));
invoker.execute(); log.info("---------------------------");
log.info("加入新命令D");
invoker.addCommand(() -> log.info("D处理这个请求"));
invoker.execute(); log.info("---------------------------");
log.info("加入新命令E");
invoker.addCommand(() -> log.info("E处理这个请求"));
invoker.execute(); log.info("---------------------------");
log.info("移除命令A");
invoker.removeCommand(aCommand);
invoker.execute();
}
}

打印语句。

抽象命令

@FunctionalInterface
public interface Command { void execute();
}

请求方

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Invoker { private List<Command> commandList = Lists.newArrayList(); public void addCommand(Command command) {
commandList.add(command);
} public void removeCommand(Command command) {
commandList.remove(command);
} public void execute() {
if(CollectionUtils.isEmpty(commandList)) {
return;
}
commandList.stream().forEach(command -> command.execute());
} }

撤销操作

在普通的命令模式的基础上,增加了撤销操作,在这里的撤销操作,其实即为左转时的右转,右转时的左转。

@Slf4j
public class Client { public static void main(String[] args) {
Invoker invoker = new Invoker();
Receiver receiver = new Receiver();
Command leftCommand = new LeftCommand(receiver);
Command rightCommand = new RightCommand(receiver); invoker.setCommand(rightCommand);
invoker.execute();
invoker.execute();
invoker.execute();
invoker.undo();
invoker.undo(); invoker.setCommand(leftCommand);
invoker.execute();
invoker.undo();
}
}

抽象命令增加了撤销操作

public interface Command {

    void execute();

    void undo();
}

具体左转时

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LeftCommand implements Command { private Receiver receiver; @Override
public void execute() {
receiver.left();
} @Override
public void undo() {
receiver.right();
}
}

右转时

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RightCommand implements Command { private Receiver receiver; @Override
public void execute() {
receiver.right();
} @Override
public void undo() {
receiver.left();
}
}

请求方

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Invoker { private Command command; public void execute() {
command.execute();
} public void undo() {
command.undo();
} }

接收方

public class Receiver {

    private Context context = new Context(new CloseLevelState());

    public void left() {
context.left();
} public void right() {
context.right();
}
}

命令模式总结

优点

(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。

(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。

(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)

(4) 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案

缺点

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用场景

(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。

(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。

(3) 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

(4) 系统需要将一组操作组合在一起形成宏命令。

(5)线程池有一个addTash方法,将任务添加到待完成的队列中,队列中的元素就是命令对象,通常的就是一个公共接口,像我们常用的java.lang.Runnable接口。

(6)java8之后,最好在Command接口中@FunctionalInterface修饰,这样具体的命令就可以使用lambda表达式啦。

Java中的使用

将操作封装到对象内,以便存储,传递和返回。

java.lang.Runnable

javax.swing.Action

文章参考

java设计模式之命令模式

细数JDK里的设计模式

wiki的命令模式

折腾Java设计模式之命令模式的更多相关文章

  1. 折腾Java设计模式之状态模式

    原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...

  2. 折腾Java设计模式之建造者模式

    博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...

  3. 折腾Java设计模式之备忘录模式

    原文地址:折腾Java设计模式之备忘录模式 备忘录模式 Without violating encapsulation, capture and externalize an object's int ...

  4. 折腾Java设计模式之模板方法模式

    博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...

  5. 折腾Java设计模式之访问者模式

    博客原文地址:折腾Java设计模式之访问者模式 访问者模式 Represent an operation to be performed on the elements of an object st ...

  6. Java设计模式 之 命令模式

    1      从属模式分类 行为性模式 2      命令模式意图 命令模式可将动作的请求者和动作的执行者对象中解耦. 该模式将一个行为操作发起者的请求封装到对象中,该请求由另外一个对象执行. 将动作 ...

  7. 折腾Java设计模式之迭代器模式

    迭代器模式 Provide a way to access the elements of an aggregate object sequentially without exposing its ...

  8. JAVA设计模式之 命令模式【Command Pattern】

    一.概述 命令模式能够将请求发送者和接收者全然解耦.发送者与接收者之间没有直接引用关系,发送请求的对象仅仅须要知道怎样发送请求,而不必知道怎样完毕请求.核心在于引入了命令类,通过命令类来减少发送者和接 ...

  9. 14.java设计模式之命令模式

    基本需求: 一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装app就可以控制对这些家电工作 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App分别控制,我们希望只要一个 ...

随机推荐

  1. 十八、泛型 l 注解 l Servlet3.0 l 动态代理 l 类加载器基础加强

    l 泛型 l 注解 l Servlet3.0 l 动态代理 l 类加载器 泛型 1 回顾泛型类 泛型类:具有一个或多个泛型变量的类被称之为泛型类. public class A<T> { ...

  2. Three.js学习笔记01

    1.四大组件: 场景:场景是所有物体的容器 var scene = new THREE.Scene(); 相机: 正投影相机:远处的和近处的是一样大 THREE.OrthographicCamera ...

  3. request,reponse对象中的方法

    1.request对象 客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应.它是HttpServletRequest类的实例. 序号 方 法 说 明 1   obj ...

  4. SDL 开发实战(六): 使用 SDL 实现 YUV 播放器

    前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器. 下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分: ...

  5. [Swift]LeetCode384. 打乱数组 | Shuffle an Array

    Shuffle a set of numbers without duplicates. Example: // Init an array with set 1, 2, and 3. int[] n ...

  6. Java数据结构和算法 - TreeMap源码理解红黑树

    前言 本篇将结合JDK1.6的TreeMap源码,来一起探索红-黑树的奥秘.红黑树是解决二叉搜索树的非平衡问题. 当插入(或者删除)一个新节点时,为了使树保持平衡,必须遵循一定的规则,这个规则就是红- ...

  7. 用Flutter开发的跨平台项目,完美运行在Android和IOS上,Material简洁风格,包括启动页、引导页、注册、登录、首页、体系、公众号、导航、项目,还有漂亮的妹子图库,运行极度流畅,结构清晰,代码规范,值得拥有

    Flutter学习资源汇总持续更新中...... Flutter官方网站 Flutter中文网 wendux的Flutter实战 Flutter官方exampleflutter_gallery 阿里巴 ...

  8. 针对 Ocelot 网关的性能测试

    一.背景 目前我们项目是采用的 Ocelot 作为 API 网关,并且在其基础上结合 IdentityServer4 开发了一套 API 开放平台.由于部分项目是基于 ABP 框架进行开发的,接口的平 ...

  9. JDK1.8源码(三)——java.util.HashMap

      什么是哈希表? 在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 数组:采用一段连续的存储单元来存储数据.对于指定下标的查找,时间复杂度为O(1):通过给定值进行查找, ...

  10. 《HelloGitHub》第 12 期

    <HelloGitHub>第 12 期 兴趣是最好的老师,<HelloGitHub>就是帮你找到兴趣! 简介 最开始我只是想把自己在浏览 GitHub 过程中,发现的有意思.高 ...