设计模式(七):命令模式

一、概述

  命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

二、解决问题

  假设我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。那么问题来了,这些智能家电来自不同的厂家,我们是不是要对每个厂家的家电都安装一个app呢?毫无疑问,我们肯定不想安装那么多的app,我们希望只要一个app就可以控制全部智能家电。要实现一个app控制所有智能家电的需要,每个智能家电厂家都要提供一个统一的接口给app调用,这个时候命令模式就可以发挥作用了。命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来,在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品。

三、结构类图

  

四、成员角色

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

  调用者(Invoke):持有命令对象的引用,在某个时间点执行命令对象的方法。

  命令接口(Command):定义命令对象的共有的方法,具体命令对象必须实现这些方法。

  具体命令对象(ConcreteCommand):实现命令接口的方法,操纵接收者的执行相关动作。

  接收者(Receiver):请求的执行者,其方法被具体命令对象调用。

五、应用实例

  好了,让我们来简化一下手机app,假如它的界面如下所示,我们可以设置每一行按键对应的电器,我们设置第一行按键为电灯的开关,第二行按键为电视开关。下面就实现这个app的功能。

  

第一步,创建电灯和电视对象,就是接收者

package command.pattern;

//灯对象
public class LightObj {
	public void on(){
		System.out.println("灯打开了");
	}

	public void off(){
		System.out.println("灯关闭了");
	}
}

  

package command.pattern;

//电视对象
public class TVObj {
	public void on(){
		System.out.println("电视打开了");
	}

	public void off(){
		System.out.println("电视关闭了");
	}
}

  第二步,创建命令接口和空命令

package command.pattern;

//定义命令接口
public interface Command {
	//执行动作
	public void execute();
	//撤销动作
	public void undo();
}

  

package command.pattern;

//没有任何命令,用于初始化每个按键,当调用空命令时,对象什么事情都不做,
//有些时候这也是一种设计模式,因为它为我们省去判空的操作
public class NoCommand implements Command{

	public void execute() {

	}

	public void undo() {

	}
}

  第三步、创建电灯开关命令

package command.pattern;

//打开电灯命令
public class LightOnCommand implements Command{
	LightObj light;

	public LightOnCommand(LightObj light) {
		super();
		//设置接收者
		this.light = light;
	}

	//执行请求
	public void execute() {
		//调用接收者的方法
		light.on();
	}

	//撤销请求
	public void undo() {
		light.off();
	}
}

  

package command.pattern;

//电灯的关闭命令
public class LightOffCommand implements Command{
	LightObj light;

	public LightOffCommand(LightObj light) {
		super();
		//设置接收者
		this.light = light;
	}

	//执行请求
	public void execute() {
		light.off();

	}

	//撤销请求
	public void undo() {
		light.on();
	}
}

  第四步、创建电视开关命令

package command.pattern;

//打开电视命令
public class TVOnCommand implements Command{
	TVObj tv;

	public TVOnCommand(TVObj tv){
		this.tv = tv;
	}

	//执行请求
	public void execute() {
		tv.on();
	}

	//撤销请求
	public void undo() {
		tv.off();
	}
}

  

package command.pattern;

//关闭电视命令
public class TVOffCommand implements Command{
	TVObj tv;

	public TVOffCommand(TVObj tv){
		this.tv = tv;
	}

	//执行请求
	public void execute() {
		tv.off();
	}

	//撤销请求
	public void undo() {
		tv.on();
	}
}

  第五步、创建调用者

package command.pattern;

//调用者
public class RemoteControl {
	//开按钮命令数组
	Command[] onCommands;
	//关按钮命令数组
	Command[] offCommands;
	//撤销执行的命令
	Command undoCommand;

	public RemoteControl(){
		onCommands = new Command[5];
		offCommands = new Command[5];

		for(int i = 0;i < 5;i++){
			onCommands[i] = new NoCommand();
			offCommands[i] = new NoCommand();
		}
	}

	public void setCommand(int slot,Command onCommand,Command offCommand){
		onCommands[slot] = onCommand;
		offCommands[slot] = offCommand;
	}

	//按下开按钮
	public void onButtonWasPushed(int slot){
		onCommands[slot].execute();
		//保存最后一次操作,用于撤销操作
		undoCommand = onCommands[slot];
	}

	//按下关按钮
	public void offButtonWasPushed(int slot){
		offCommands[slot].execute();
		//保存最后一次操作,用于撤销操作
		undoCommand = offCommands[slot];
	}

	public void undoButtonWasPushed(){
		undoCommand.undo();
	}
}

  第六步、测试手机app

package command.pattern;

public class RemoteControlTest {
	public static void main(String[] args){
		//创建电灯对象
		LightObj light = new LightObj();
		//创建电视对象
		TVObj tv = new TVObj();
		//创建电灯和电视的开关命令
		Command lingOnCommand = new LightOnCommand(light);
		Command lingOffCommand = new LightOffCommand(light);
		Command tvOnCommand = new TVOnCommand(tv);
		Command tvOffCommand = new TVOffCommand(tv);

		RemoteControl remote = new RemoteControl();
		//把第一行的按钮设置为电灯的开关
		remote.setCommand(0, lingOnCommand, lingOffCommand);

		System.out.println("-----按下灯的开按钮-----");
		remote.onButtonWasPushed(0);
		System.out.println("-----按下灯的关按钮-----");
		remote.offButtonWasPushed(0);
		System.out.println("-----按下撤销键-----");
		remote.undoButtonWasPushed();

		//把第二行的按钮设置为电视的开关
		remote.setCommand(1, lingOnCommand, lingOffCommand);
		System.out.println("-----------------------");
		System.out.println("-----按下电视的开按钮-----");
		remote.onButtonWasPushed(1);
		System.out.println("-----按下电视的关按钮-----");
		remote.offButtonWasPushed(1);
		System.out.println("-----按下撤销键-----");
		remote.undoButtonWasPushed();

	}
}

  运行结果:

六、优点和缺点

  1、优点

  (1)、将发起请求的对象与执行请求的对象解耦。发起请求的对象时调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作。就拿上面的手机app例子来说,不管什么品牌的智能电器,你只要实现统一的命令接口,并告诉手机app你的具体命令什么,手机app就可以控制你的工作,而不必知道你是什么品牌的电器。

  (2)、有很好的扩展性。新增一个具体命令对象不会影响到其他命令对象,所以很容易增加一个具体命令类。

  (3)、很容易地设计一个命令队列。只要把命令对象放到列队,我们就可以多线程的执行命令。

  (4)、可以容易地实现对请求的撤销和重做。

  (5)、可以实现命令日志,当需要系统瘫痪时直接从命令日志恢复系统。

  2、缺点

  (1)、由于一个命令对应一个接收者动作,当接收者动作过多时,会创建很多命令对象,增加了系统的复杂度。

七、使用场合

  (1)、当需要一个对象执行某个动作,但并不知道或者无须知道接收者是什么对象时使用。

  (2)、当需要连续或者同时控制多个对象执行动作时,可以运用命令模式把每个动作封装成一个命令,组成命令队列。只要操纵该队列的命令就可以控制多个对象执行相关动作。

  (3)、当需要考虑系统恢复时,可以使用命令日志。

八、总结

  1、”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。

  2、空命令也可以说是一种设计模式,它为我们省去了判空的操作。在上面的应用实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦,你不判空的话,程序可能会空指针异常,判空又会带来很多重复的操作。

//按下开按钮,判空
	public void onButtonWasPushedWithNull(int slot){
		//判断命令是否为空
		if(onCommands[slot] != null){
			onCommands[slot].execute();
			//保存最后一次操作,用于撤销操作
			undoCommand = onCommands[slot];
		}
	}
时间: 2024-12-27 17:43:29

设计模式(七):命令模式的相关文章

设计模式之命令模式

命令模式的核心是把方法调用封装起来,调用的对象不需要关心是如何执行的. 定义:命令模式将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也可以支持撤销操作. 先看一个例子,设计一个家电遥控器的API,可以通过遥控器发出命令来控制不同生产商生产的家电,比如关灯.开灯. 以下是一个简单的代码示例. 1 package cn.sp.test05; 2 /** 3 * 电灯类 4 * @author 2YSP 5 * 6 */ 7 public class Lig

设计模式之命令模式20170719

行为型设计模式之命令模式: 一.含义 将请求(命令)封装成一个对象(内部有接收者对象,以及按照具体命令执行接收者操作的方法),传递给调用者,由调用者执行具体命令. 也就是把一个动作的执行分为执行对象(接收者角色).执行行为(命令角色),让两者相互独立而不相互影响,实现对动作解耦 二.代码说明 1.主要有三个角色 1)接收者角色 该角色就是干活的角色,被命令角色调用,其操作按具体命令的要求执行 2)命令角色 需要执行的所有命令都在这里声明,同时接收者所有的对象都在这里(接收者封装在这里,防止高层模

跟我学设计模式视频教程——命令模式vs策略模式,唠嗑

课程视频 命令模式vs策略模式 唠嗑 课程笔记 课程笔记 课程代码 课程代码 新课程火热报名中 课程介绍 跟我学设计模式视频教程--命令模式vs策略模式,唠嗑,布布扣,bubuko.com

用Java 8 Lambda表达式实现设计模式:命令模式

链接:http://www.importnew.com/16789.html 在这篇博客里,我将说明如何在使用Java 8 Lambda表达式的函数式编程方式时实现命令设计模式.命令模式的目标是将请求封装成一个对象,从对客户端的不同类型请求,例如队列或日志请求参数化,并提供相应的操作.命令模式是一种通用编程方式,该方式基于运行时决策顺序来执行方法.模式的参与者如下: 命令 :声明用于执行操作的接口. 实体命令 :定义接收者对象和动作的绑定. 客户端 :创建实体命令实例并设置它的接收者. 调用者:

设计模式 ( 十三 ) 命令模式Command(对象行为型)

设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活. 例子1:电视机遥控器 : 遥控器是请求的发送者,电视机是请求的接收者,遥控器上有一些按钮如开,关,换频道等按钮就是具体命令,不同的按钮对应电视机的不同操作. 2.问题

设计模式(6)--命令模式

关键词 :空对象 有人称为设计模式 三层调用 1. 封装调用  , 把封装带到一个全新的境界: 把方法调用(method invocation) 封装起来. 2. 命令模式可将"动作的请求者" 从"动作的执行者" 对象中解耦. 3. 当需要将发出的请求和执行请求的对象解耦的时候,使用命令模式. OO原则: (1)封装变化 (2) 多用组合,少用继承 (3)针对接口编程,不针对实现编程 (4)为交互对象之间松耦合设计而努力 (5) 类应该对扩展开放,对修改关闭 (6)

设计模式 7 —— 命令模式

设计模式目录: 设计模式 1 ——观察者模式 设计模式 2 —— 装饰者模式 设计模式 3 —— 迭代器和组合模式(迭代器) 设计模式 4 —— 迭代器和组合模式(组合) 设计模式 5 —— 工厂模式 设计模式 6 —— 单件模式 设计模式 7 —— 命令模式 概述 第1部分 问题引入 第2部分 定义和实现 第3部分 使用宏命令 第1部分 问题引入 首先看下,下面的要求: 实现命令接口 首先,让说有的命令对象实现相同的包含一个方法的接口. 1 /** 2 * 命令接口 3 * @ClassNam

C#设计模式(15)——命令模式(Command Pattern)

原文:C#设计模式(15)--命令模式(Command Pattern) 一.前言 之前一直在忙于工作上的事情,关于设计模式系列一直没更新,最近项目中发现,对于设计模式的了解是必不可少的,当然对于设计模式的应用那更是重要,可以说是否懂得应用设计模式在项目中是衡量一个程序员的技术水平,因为对于一个功能的实现,高级工程师和初级工程师一样都会实现,但是区别在于它们实现功能的可扩展和可维护性,也就是代码的是否“优美”.可读.但是,要更好地应用,首先就必须了解各种设计模式和其应用场景,所以我还是希望继续完

每天一个设计模式之命令模式

作者按:<每天一个设计模式>旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现.诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :) 原文地址是:<每天一个设计模式之命令模式> 欢迎关注个人技术博客:godbmw.com.每周 1 篇原创技术分享!开源教程(webpack.设计模式).面试刷题(偏前端).知识整理(每周零碎),欢迎长期关注! 如果您也想进行知识整理 + 搭建功能完善/设计简约/快速启动的个人博客,请直接戳the

Java设计模式菜鸟系列(七)命令模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39804057 命令模式(Command):将"请求"(命令/口令)封装成一个对象,以便使用不同的请求.队列或者日志来参数化其对象.命令模式也支持撤销操作.命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开. 一.uml建模 二.代码实现: /** * 示例:以咱去餐馆吃饭为例,分为3步 * * 1.和小二说,来个宫保鸡丁 --> 顾客发出口令 * *