OOP设计模式[JAVA]——04命令模式
命令模式
命令模式的意图
命令模式属于对象的行为模式。别名又叫:Action或Transaction。
命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录日志,可以提供命令的撤销和恢复功能。
命令模式的结构
命令模式的参与者
- Command
——声明一个给所有具体命令类的抽象接口。这是一个抽象角色,通常由一个Java接口或Java抽象类实现。 - ConcreteCommand
——将一个接收者对象绑定于一个动作
——调用接收者相应的操作,以实现execute - Client
——创建一个具体命令对象并设定它的接收者 - Invoker
——负责调用命令对象执行请求。 - Receiver
——知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
命令模式的活动序列
- 客户端创建了一个ConcreteCommand对象,并指明了接收者。
- 请求者对象保存了ConcreteCommand对象。
- 请求者对象通过调用action()方法发出请求。如果命令是撤消的,那么ConcreteCommand保存了调用execute()方法之前的状态。
- ConcreteCommand对象调用接收的一方的方法执行请求。
命令模式的使用场景
家电遥控器,该遥控器的原型如下:
- 有7个可编程的插槽(每一个都可以指定到一个不同的家电装置)
- 每个插槽都有对应的开关按钮
- 具备一个整体的撤销按键
下面我们来看看最简单的实现。
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模式有以下效果:
- Command模式将调用操作的对象与知道如何实现该操作的对象解耦
- Command是头等的对象。它们可像其他的对象一样被操作和扩展
- 可将多个命令装配成一个复合命令。
- 增加新的Command很容易,无需改变已有的类
命令模式的实现
实现command模式须考虑的问题有如下:
- 一个命令对象应达到何种智能程度 命令对象的能力可大可小。一个极端是它仅确定一个接收者和执行该请求的动作。另一个极端是它自己实现所有功能,根本不需要接收对象。当需要定义与已有的类无关的命令,当没有合适的接收者,或当一个命令隐式地知道它的接收者时,可以使用后一极端方式。在这两个极端间的情况是命令对象有足够的信息可以动态的找到它们的接收者。
- 支持取消(undo)和重做(redo) 如果Command提供方法逆转它们操作的执行(undo),就可支持取消和重做功能。为达到这个目的,ConcreteCommand类可能需要存储额外的状态信息。这个状态包括:
- 接收者对象,它真正执行处理该请求的各操作。
- 接收者上执行操作的参数
- 如果处理请求的操作会改变接收者对象中的某些值,那么这些值也必须先存储起来。接收者还必须提供一些操作,以使该命令可将接收者恢复到它先前的状态。
若应用只支持一次取消操作,那么只需存储最近一次被执行的命令。而若要支持多级的取消和重做,就需要有一个已被执行命令的历史表列(history list),该表列的最大长度决定了取消和重做的级数。历史表列存储了已被执行的命令序列。向后遍历该表列并逆向执行( r e v e r s e - e x e c u t i n g )命令是取消它们的结果;向前遍历并执行命令是重执行它们。有时可能不得不将一个可撤消的命令在它可以被放入历史列表中之前先拷贝下来。这是因为执行原来的请求的命令对象将在稍后执行其他的请求。如果命令的状态在各次调用之间会发生变化,那就必须进行拷贝以区分相同命令的不同调用。
- 避免取消操作过程中的错误积累
OOP设计模式[JAVA]——04命令模式的更多相关文章
- Java设计模式学习记录-命令模式
前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...
- java 之 命令模式(大话设计模式)
命令模式,笔者一直以为当我们开发的过程中基本上很难用到,直到维护阶段或者重构阶段,我们会发现有些撤销命令和追加命令比较频繁时,自然而然就用到命令模式. 先看下类图 大话设计模式-类图 简单说下类图,最 ...
- JAVA设计模式之:命令模式
*通常情况下:行为请求者与实现者通常呈现一种高度耦合状态.有时要对行为进行变更处理处理.高度耦合方式就显得不合适. * 将行为请求者与行为实现者解耦,将一组行为抽象为对象.实现二者之间的松耦合. 这就 ...
- java设计模式-----23、命令模式
概念: Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数. 命令模式(Command Pattern)是一种 ...
- 《JAVA设计模式》之命令模式(Command)
在阎宏博士的<JAVA与模式>一书中开头是这样描述命令(Command)模式的: 命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. ...
- 重学 Java 设计模式:实战命令模式「模拟高档餐厅八大菜系,小二点单厨师烹饪场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 持之以恒的重要性 初学编程往往都很懵,几乎在学习的过程中会遇到 ...
- Java设计模式系列之命令模式
命令模式(Command)的定义 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作,将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. ...
- JAVA设计模式之【命令模式】
命令模式 为了降低耦合度,将请求的发送者和接收者解耦 发送请求的对象只需要哦知道如何发送请求,而不必知道如何完成请求 对请求排队 记录请求日志 支持撤销操作 核心在于引入命令类 角色 抽象命令类Com ...
- Java设计模式(20):命令模式
本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 智能电脑的品牌越来越多,由此诞生了一款电脑控制的APP,万能遥控器,用户在使用遥控器的时候,可以切换为自家电视的品 ...
随机推荐
- 使用Cobbler批量部署Linux和Windows:CentOS/Ubuntu批量安装(二)
通过前面服务端的部署,已经配置好了 Cobbler Server 端,接下来开始进行 CentOS/Ubuntu 的批量安装,在进行 CentOS/Ubuntu 批量安装时,也需要通过Cobbler来 ...
- python模块分析之hashlib加密(二)
前言 hashlib模块是py3.+用来对字符串进行hash加密的模块,核心算法是md5,明文与密文是一一对应不变的关系:用于注册.登录时用户名.密码等加密使用. 模块分析 hashlib模块有多种加 ...
- 一个简单的Java程序
一个.NET技术还是很菜的水平的猿人现在要去学习Java不知道是坏是好,无从得知啊! 不过在网上看了好多Java方面的简单例子,感觉Java还是蛮不错的么!不管以后怎么样啦,先开始自己的Java菜鸟之 ...
- 谈谈如何查看Android项目方法数
我们都知道,Android App的方法数是有天花板的,在方法数达到65536时,就会出现打包异常,这个时候,我们需要去除一些不需要的三方工具包,或者采用多Dex技术分包,都能达到正常打包的效果. 可 ...
- Java继承关系概述
Java中只支持单继承关系 示例代码: package com.java1995; public class People { private String name; private int age ...
- 《剑指offer》二叉树镜像
剑指offer简单题,但是能一下写对也需要小心考虑细节. 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / 6 10 / / 5 7 9 11 ...
- Archlinux系统配置学习笔记(一)
本文档是有关Archlinux系统配置的学习笔记,参考和学习的是Archlinux官方网站上的相应文档:General Recommendations. 这里的配置主要是针对按照官方网站上的文档刚刚完 ...
- 【AtCoder】CODE FESTIVAL 2017 Final
A - AKIBA 模拟即可 代码 #include <bits/stdc++.h> #define fi first #define se second #define pii pair ...
- java json和对象互相装换
java json和对象互相装换 1.com.alibaba.fastjson.JSON 2.com.fasterxml.jackson.databind.ObjectMapper
- 面向对象设计原则 里氏替换原则(Liskov Substitution Principle)
里氏替换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一. 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现. LSP是继承复用的基石,只 ...