打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
命令模式.

一、概念

  • 命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
  • 角色:
     1、命令(Command):为所有命令声明了一个接口。调用命令对象的 execute()方法,就可以让接收者进行相关的操作。这个接口也具备一个 undo() 方法。
     2、具体命令(ConcreteCommand):实现命令接口,定义了动作和接收者之间的绑定关系。调用者只要调用 execute() 就可以发出请求,然后由 ConcreteCommand 调用接收者的一个或多个动作。
     3、请求者(Invoker):持有一个命令对象,有一个行动方法,在某个时间点调用命令对象的 execute() 方法,将请求付诸实行。
     4、接收者(Receiver):接收者知道如何进行必要的动作,实现这个请求。任何类都可以当接收者。
     5、客户端(Client):创建一个具体命令(ConcreteCommand)对象并确定其接收者,包括把其他角色串连在一起。

二、Demo 实现

Topic:我们要制作一个简易的遥控器,有两个控制灯开关的按钮,并有一个操作回退按钮。

1、接收者

 首先,我们先来定义一个接收者的角色,也就是最后执行动作的那个对象 —— Light.java,控制着灯的开启和关闭。

public class Light {    public void on() {        System.out.println("灯亮了...");    }    public void off() {        System.out.println("灯暗了...");    }}

2、命令

 现在,我们要定义一个命令角色。一般是一个接口,为所有的命令对象声明一个接口,规范将要进行的命令操作。

public interface Command {    /**     * 执行命令     */    void execute();    /**     * 撤销命令     */    void undo();}

3、具体命令

 有了命名角色后,我们要构建具体命令角色。具体命令实现了命令接口,定义了动作和接收者之间的绑定关系。这里,我们有两个具体命令对象—— LightOnCommand.java(开灯命令)、LightOffCommand.java(关灯命令)

public class LightOnCommand implements Command {    private Light light;    public LightOnCommand(Light light) {        this.light = light;    }    @Override    public void execute() {        light.on();    }    @Override    public void undo() {        light.off();    }}
public class LightOffCommand implements Command {    private Light light;    public LightOffCommand(Light light) {        this.light = light;    }    @Override    public void execute() {        light.off();    }    @Override    public void undo() {        light.on();    }}

4、请求者

 前面,我们定义了动作的接收方和联系中介 —— 命名对象。现在,我们要着手构建请求者角色了。请求者持有一个命令对象,有一个行动方法。它会在某个时间点执行行动方法,但不关心是谁具体执行了这个动作。

public class RemoteInvoker {    /**     * 开关命令数组,模拟有很多对开关数组     */    private Command[] onCommands;    private Command[] offCommands;    /**     * 撤销(回退)命令     */    private Command undoCommand;    public RemoteInvoker(int length) {        // 有几组开关,就设置多少数组        onCommands = new Command[length];        offCommands = new Command[length];        // 把每个命令初始化成空命令,避免空指针异常        Command noCommand = new NoCommand();        undoCommand = noCommand;        for (int i = 0; i < length; i++) {            onCommands[i] = noCommand;            offCommands[i] = noCommand;        }    }    /**     * @Description 设置命令对象     * @date 2018/11/29 09:15     * @param slot 遥控器的位置     * @param onCommand 开的命令     * @param offCommand 关的命令     * @return void     */    public void setCommond(int slot, Command onCommand, Command offCommand) {        onCommands[slot] = onCommand;        offCommands[slot] = offCommand;    }    public void onButton(int slot) {        onCommands[slot].execute();        //为撤销(回退)按钮记录动作        undoCommand = onCommands[slot];    }    public void offButton(int slot) {        offCommands[slot].execute();        //为撤销(回退)按钮记录动作        undoCommand = offCommands[slot];    }    public void undoButton() {        undoCommand.undo();    }}

5、客户端

 前面,我们定义好了请求者、接收者已经两者之间的联系中介 —— 命令对象。但是这几个角色对象之间都是松耦合的,还没有一个具体动作的流程,现在我们利用客户端角色把整个动作流程串联在一起。

public class RemoteClient {    public static void main(String[] args) {        // 1、创建接收者        Light light = new Light();        // 2、创建命令对象        LightOnCommand lightOnCommand = new LightOnCommand(light);        LightOffCommand lightOffCommand = new LightOffCommand(light);        // 3、创建一组开关并用命令对象装载它        RemoteInvoker invoker = new RemoteInvoker(1);        invoker.setCommond(0, lightOnCommand, lightOffCommand);        // 4、测试        invoker.onButton(0);        invoker.offButton(0);        invoker.undoButton();    }}

三、总结

  • 命令模式将发出请求的对象和执行请求的对象解耦,在被解耦的两者之间是通过命令对象进行沟通的。
  • 一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将接收者和动作封装进对象中,这个对象只暴露出一个 execute() 方法,当此方法被调用时,接收者就会进行这些动作。从外面来看,其他对象不知道究竟哪个接收者进行了哪些操作,只知道如果调用 execute() 方法,请求的目的就可以达到。
  • 当你不想返回一个有意义的对象时,空对象就很有用。这样,我们就可以把处理 null 的责任转移给空对象,甚至有些时候,空对象本身也被视为一种设计模式。
  • 我们还可以把一堆命令组装起来拼成一个命令,称为宏命令。宏命令是命令的一种延伸,允许调用一系列的命令。包括一系列的执行和撤销动作。
  • 适用场景:
     1、命令的发送者和命令执行者有不同的生命周期,命令发送了并不是立即执行。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在序列化之后传送到另外一台机器上去。 2、命令需要进行各种管理逻辑,比如:对多个命令的统一控制。
     3、需要支持撤消/重试操作。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用 undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供 redo()方法, 以供客户端在需要时再重新实施命令效果。 4、使用命令模式作为 "回调(callBack)" 在面向对象系统中的替代。"callBack" 讲的便是先将一个函数登记上,然后在以后调用此函数。 5、如果要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志读回所有的数据更新命令,重新调用 execute() 方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)
图解Java设计模式之命令模式
命令模式(Command)解析例子
命令模式
研磨设计模式之 命令模式-1
命令模式——行为型模式(2)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服