命令模式

命令模式的意图

命令模式属于对象的行为模式。别名又叫:Action或Transaction。

命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录日志,可以提供命令的撤销和恢复功能。

命令模式的结构

命令模式的参与者

  • Command
    ——声明一个给所有具体命令类的抽象接口。这是一个抽象角色,通常由一个Java接口或Java抽象类实现。
  • ConcreteCommand
    ——将一个接收者对象绑定于一个动作
    ——调用接收者相应的操作,以实现execute
  • Client
    ——创建一个具体命令对象并设定它的接收者
  • Invoker
    ——负责调用命令对象执行请求。
  • Receiver
    ——知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。

命令模式的活动序列

  1. 客户端创建了一个ConcreteCommand对象,并指明了接收者。
  2. 请求者对象保存了ConcreteCommand对象。
  3. 请求者对象通过调用action()方法发出请求。如果命令是撤消的,那么ConcreteCommand保存了调用execute()方法之前的状态。
  4. ConcreteCommand对象调用接收的一方的方法执行请求。

命令模式的使用场景

家电遥控器,该遥控器的原型如下:

  1. 有7个可编程的插槽(每一个都可以指定到一个不同的家电装置)
  2. 每个插槽都有对应的开关按钮
  3. 具备一个整体的撤销按键

下面我们来看看最简单的实现。

Command:抽象命令接口

/**
* 抽象接口 Command
* @author nick
*
*/
public interface Command {
public void execute(); //非常简单,只需要一个方法:execute()
}

ConcreteCommand:具体命令类

/**
* 具体命令类
* @author nick
*
*/
public class LightOnCommand implements Command { //构造器传入了接收对象,以便让这个命令控制,然后记录在实例变量中。
//一旦调用execute()方法,就由这个对象成为接收者,负责接收请求
public LightOnCommand(Light light) {
super();
this.light = light;
} @Override
public void execute() {
// 这个execute()方法调用接收对象的on()方法
light.on();
} Light light;
}

Client:客户端

/**
* 遥控器测试类------>客户端
* @author nick
*
*/
public class RemoteControlTest { /**
* @param args
*/
public static void main(String[] args) {
// 遥控器就是调用者,会传入一个命令对象,可以用来发出请求
SimpleRemoteControl remote = new SimpleRemoteControl();
//创建一个电灯对象,此对象就是请求的接收者
Light light = new Light();
//创建一个命令,然后将接收者传递给它
LightOnCommand lightOn = new LightOnCommand(light); remote.setCommand(lightOn); //把命令传给调用者
remote.buttonWasPressed(); //模拟遥控器的按钮按下动作
} }

invoker:调用者

/**
* 遥控器类------>调用者
*
* @author nick
*/
public class SimpleRemoteControl {
Command slot; //有一个插槽持有命令,而这个命令控制着一个装置 public SimpleRemoteControl() {
} public void setCommand(Command command) { //用来设置插槽控制的命令
slot = command;
} public void buttonWasPressed() { //遥控器按钮按下时,使得当前命令衔接插槽
slot.execute();
}
}

Receiver:接收者

/**
* 电灯对象------>接收者
* @author nick
*
*/
public class Light {
int level; public Light() {} public void on() {
level = 100;
System.out.println("Light is on");
} public void off() {
level = 0;
System.out.println("Light is off");
} public void dim(int level) {
this.level = level;
if (level == 0) {
off();
}
else {
System.out.println("Light is dimmed to " + level + "%");
}
} public int getLevel() {
return level;
}
}

模拟结果输出如下:

Light is on

以上只是单个命令的操作,如果我们需要添加一个LightOff的命令呢,那该怎么办?看下面:

/**
* 具体命令类---->关闭电灯
* @author nick
*
*/
public class LightOffCommand implements Command { //构造器传入了接收对象,以便让这个命令控制,然后记录在实例变量中。
//一旦调用execute()方法,就由这个对象成为接收者,负责接收请求
public LightOffCommand(Light light) {
super();
this.light = light;
} @Override
public void execute() {
// 这个execute()方法调用接收对象的off()方法
light.off();
} Light light;
}
/**
* 遥控器测试类------>客户端
* @author nick
*
*/
public class RemoteControlTest {
/**
* @param args
*/
public static void main(String[] args) {
// 遥控器就是调用者,会传入一个命令对象,可以用来发出请求
SimpleRemoteControl remote = new SimpleRemoteControl();
//创建一个电灯对象,此对象就是请求的接收者
Light light = new Light();
//创建一个命令,然后将接收者传递给它
LightOnCommand lightOn = new LightOnCommand(light);
LightOffCommand lightOff = new LightOffCommand(light); //添加LightOff命令 remote.setCommand(lightOn); //把命令传给调用者
remote.setCommand(lightOff);
remote.buttonWasPressed(); //模拟遥控器的按钮按下动作
}
}

好了,其他几个控制命令,可以按这个去添加即可。但是我们别忘了,Command模式不是说还可以提供撤消和恢复功能吗,那这个功能我们又该如何实现呢?

因为每个控制按钮都需要实现撤消功能,所以我们要在抽象命令的接口中定义该撤消功能的方法:

/**
* 抽象接口 Command
* @author nick
*
*/
public interface Command {
public void execute(); // 非常简单,只需要一个方法:execute()
public void undo(); // 添加一个撤消方法
}

具体命令类的实现:

/**
* 具体命令类
* @author nick
*
*/
public class LightOnCommand implements Command { //构造器传入了接收对象,以便让这个命令控制,然后记录在实例变量中。
//一旦调用execute()方法,就由这个对象成为接收者,负责接收请求
public LightOnCommand(Light light) {
super();
this.light = light;
} @Override
public void execute() {
// 这个execute()方法调用接收对象的on()方法
light.on();
} @Override
public void undo() {
        // 实现抽象命令中的撤消功能,该撤消动作就是电灯对象的上一个动作(light off)
        System.out.println("Ready to undo---->");
        light.off();
    } Light light;
}
/**
* 具体命令类---->关闭电灯
* @author nick
*
*/
public class LightOffCommand implements Command { //构造器传入了接收对象,以便让这个命令控制,然后记录在实例变量中。
//一旦调用execute()方法,就由这个对象成为接收者,负责接收请求
public LightOffCommand(Light light) {
super();
this.light = light;
} @Override
public void execute() {
// 这个execute()方法调用接收对象的off()方法
light.off();
} public void undo() {
        // 实现抽象命令中的撤消功能,该撤消动作就是电灯对象的上一个动作(light on)
        System.out.println("Ready to undo---->");
        light.on();
    } Light light;
}

调用者类

/**
* 遥控器类------>调用者
*
* @author nick
*/
public class SimpleRemoteControl {
Command onCommand;
Command offCommand;
Command undoCommand; public SimpleRemoteControl() {
} public void setCommand(Command onCom, Command offCom) {//用来设置插槽控制的命令
onCommand = onCom;
offCommand = offCom;
} public void onButtonWasPressed() { //开电灯按钮按下,执行打开电灯命令
onCommand.execute();
undoCommand = onCommand;
} public void offButtonWasPressed() { //关电灯按钮按下,执行关闭电灯命令
offCommand.execute();
undoCommand = offCommand;
} public void undoButtonWasPressed() { //撤消按钮按下,执行撤消动作
undoCommand.undo();
}
}

客户端

/**
* 遥控器测试类------>客户端
* @author nick
*
*/
public class RemoteControlTest {
/**
* @param args
*/
public static void main(String[] args) {
// 遥控器就是调用者,会传入一个命令对象,可以用来发出请求
SimpleRemoteControl remote = new SimpleRemoteControl();
//创建一个电灯对象,此对象就是请求的接收者
Light light = new Light();
//创建一个命令,然后将接收者传递给它
LightOnCommand lightOn = new LightOnCommand(light);
LightOffCommand lightOff = new LightOffCommand(light); //添加LightOff命令 remote.setCommand(lightOn, lightOff); remote.onButtonWasPressed(); //模拟遥控器的按钮按下动作
remote.undoButtonWasPressed();
System.out.println("++++++++++++++++++++");
remote.offButtonWasPressed();
remote.undoButtonWasPressed();
}
}

测试结果如下:

Light is on
Ready to undo---->
Light is off
++++++++++++++++++++
Light is off
Ready to undo---->
Light is on

命令模式的效果

Command模式有以下效果:

  1. Command模式将调用操作的对象与知道如何实现该操作的对象解耦
  2. Command是头等的对象。它们可像其他的对象一样被操作和扩展
  3. 可将多个命令装配成一个复合命令。
  4. 增加新的Command很容易,无需改变已有的类

命令模式的实现

实现command模式须考虑的问题有如下:

  1. 一个命令对象应达到何种智能程度     命令对象的能力可大可小。一个极端是它仅确定一个接收者和执行该请求的动作。另一个极端是它自己实现所有功能,根本不需要接收对象。当需要定义与已有的类无关的命令,当没有合适的接收者,或当一个命令隐式地知道它的接收者时,可以使用后一极端方式。在这两个极端间的情况是命令对象有足够的信息可以动态的找到它们的接收者。
  2. 支持取消(undo)和重做(redo)    如果Command提供方法逆转它们操作的执行(undo),就可支持取消和重做功能。为达到这个目的,ConcreteCommand类可能需要存储额外的状态信息。这个状态包括:
    • 接收者对象,它真正执行处理该请求的各操作。
    • 接收者上执行操作的参数
    • 如果处理请求的操作会改变接收者对象中的某些值,那么这些值也必须先存储起来。接收者还必须提供一些操作,以使该命令可将接收者恢复到它先前的状态。
      若应用只支持一次取消操作,那么只需存储最近一次被执行的命令。而若要支持多级的取消和重做,就需要有一个已被执行命令的历史表列(history list),该表列的最大长度决定了取消和重做的级数。历史表列存储了已被执行的命令序列。向后遍历该表列并逆向执行( r e v e r s e - e x e c u t i n g )命令是取消它们的结果;向前遍历并执行命令是重执行它们。有时可能不得不将一个可撤消的命令在它可以被放入历史列表中之前先拷贝下来。这是因为执行原来的请求的命令对象将在稍后执行其他的请求。如果命令的状态在各次调用之间会发生变化,那就必须进行拷贝以区分相同命令的不同调用。
  3. 避免取消操作过程中的错误积累

OOP设计模式[JAVA]——04命令模式的更多相关文章

  1. Java设计模式学习记录-命令模式

    前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...

  2. java 之 命令模式(大话设计模式)

    命令模式,笔者一直以为当我们开发的过程中基本上很难用到,直到维护阶段或者重构阶段,我们会发现有些撤销命令和追加命令比较频繁时,自然而然就用到命令模式. 先看下类图 大话设计模式-类图 简单说下类图,最 ...

  3. JAVA设计模式之:命令模式

    *通常情况下:行为请求者与实现者通常呈现一种高度耦合状态.有时要对行为进行变更处理处理.高度耦合方式就显得不合适. * 将行为请求者与行为实现者解耦,将一组行为抽象为对象.实现二者之间的松耦合. 这就 ...

  4. java设计模式-----23、命令模式

    概念: Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数. 命令模式(Command Pattern)是一种 ...

  5. 《JAVA设计模式》之命令模式(Command)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述命令(Command)模式的: 命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. ...

  6. 重学 Java 设计模式:实战命令模式「模拟高档餐厅八大菜系,小二点单厨师烹饪场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 持之以恒的重要性 初学编程往往都很懵,几乎在学习的过程中会遇到 ...

  7. Java设计模式系列之命令模式

    命令模式(Command)的定义 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作,将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. ...

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

    命令模式 为了降低耦合度,将请求的发送者和接收者解耦 发送请求的对象只需要哦知道如何发送请求,而不必知道如何完成请求 对请求排队 记录请求日志 支持撤销操作 核心在于引入命令类 角色 抽象命令类Com ...

  9. Java设计模式(20):命令模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 智能电脑的品牌越来越多,由此诞生了一款电脑控制的APP,万能遥控器,用户在使用遥控器的时候,可以切换为自家电视的品 ...

随机推荐

  1. 解决MySQL新增用户无法登陆问题

    1. 新增用户 grant all on *.* to '库名'@'%' identified by '库名'; 2. 刷新授权表 flush privileges; 3. 删除空用户 use mys ...

  2. 使用badblocks命令检测、修复硬盘坏道(待验证)

    今天我的新硬盘到了.暂时没想好怎么用它.可以把它装入光驱位硬盘架给G430用,也可以把它当成移动硬盘来用. 想起以前记录过一个badblocks的用法,用来检查坏道,这里再贴一遍备用. ####### ...

  3. JS模块化编程(一):CommonJS,AMD/CMD

    前言 模块化是什么? 为什么采用模块化? 场景: 一个html,因不同的业务需求开发,会不断的引入js文件.另外,a.js和b.js中的变量或函数必须是全局的,才能暴露给使用方. <script ...

  4. Oracle学习笔记:with as子查询用法

    With as短语,也叫做子查询部分(subquery factoring),可以定义一个SQL片断,该SQL片断会被整个SQL语句用到.该语句会在真正的查询之前预先构造一个临时表,之后可以多次使用做 ...

  5. Java编程的逻辑 (34) - 随机

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  6. transition动画

    http://rainleaves.com/demo/transition/transition.html

  7. Solr7.4.0的API(Solrj)操作

    一.SolrJ的概念 solr单机版服务搭建:https://www.cnblogs.com/frankdeng/p/9615253.html solr集群版服务搭建:https://www.cnbl ...

  8. 打FFT时中发现的卡常技巧

    题目:洛谷P1919 A*B Problem 加强版 我的代码完全借鉴boshi,然而他380ms我880ms...于是我通过彻底的卡(chao)常(dai)数(ma)成功优化到了380ms,都是改了 ...

  9. ubuntu14.0安装ITK的步骤

    (1) sudo apt-get install cmake (2) sudo apt-get install cmake-curses-gui (3)下载安装包InsightToolkit-4.11 ...

  10. CSS------如何让ul中的li分为两列甚至多列

    转载: http://blog.sina.com.cn/s/blog_7f13f92a0100rkfg.html 只需要复制ul和li中的style样式即可 如果需要自定义多少列,只需要修改li中的w ...