设计模式笔记:命令模式

命令模式将请求封装成对象,用于减少请求发送者与接收者的耦合。它支持请求的队列处理、日志记录和撤销操作。在系统中,请求者(如遥控器或服务员)通过命令对象(如灯、风扇或菜品命令)调用执行者(如电灯、风扇或厨师)的方法。命令模式降低了系统复杂度,易于扩展,但也可能导致大量具体命令类的产生。适用于需要解耦调用者和接收者、处理请求队列或实现撤销/重做功能的场景。
摘要由CSDN通过智能技术生成

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

所谓参数化,我的理解是实际执行的对象,比如light(电灯)、strereo(音响),他们存在执行动作的方法,并且作为命令对象的成员变量

队列、日志这两样是命令模式的应用直接扩展

为什么需要命令模式:

在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。

假如有这样一种情景,我现在需要打开风扇,自然我需要一个遥控器,很明显这里的关系是:我——遥控器—>命令——风扇,而遥控器发出命令去控制风扇,其中,遥控器是请求者,风扇是执行者,遥控器不知道怎么让风扇转起来,他只知道发出命令,即上面的动机。

也可以这样子想象:我在餐厅里点菜,服务员他帮我转达请求,厨师帮我炒菜,而服务员是不知道怎么去炒,他仅仅转达请求而已——完全解耦了

先看看类图:


其中,我们可以把invoker看作遥控器,receiver看作动作的执行对象,即light之类,我们现在直接看一个支持撤销命令的代码并不是很难,所以直接上这个,来自《Head First 设计模式》:

这里给出一个接口和两个代表性的实体类(其实实体类有light、fan、cellingfan(这个是实现除开关两种可能性以外的多种可能性的撤销方法)),类里面有并没有罗列出来的类,但其实是差不多的,应该不会造成阅读障碍

先看实体类:他们各自有自己的行为方法,开关、换高低速(更好地表现撤销功能)

public class Light {
	public void On(){
		System.out.println("Light-----On");
	}
	public void Off(){
		System.out.println("Light-----Off");
	}
}

public class Fan {
	public void On(){
		System.out.println("Fan-----On");
	}
	public void Off(){
		System.out.println("Fan-----Off");
	}
}

public class CellingFan{
	public static final int High = 3;
	public static final int MEDIUM = 2;
	public static final int LOW = 1;
	public static final int OFF = 0;
	
	String location;
	int speed;
	
	public CellingFan(String location) {
		this.location = location;
		speed = OFF;
	}
	
	public void high(){
		System.out.println("Speed-----High");
		speed = High;
	}
	
	public void medium(){
		System.out.println("Speed-----Medium");
		speed = MEDIUM;
	}
	
	public void Low(){
		System.out.println("Speed-----Low");
		speed = LOW;
	}
	
	public void Off(){
		System.out.println("Speed-----Off");
		speed = OFF;
	}

	public int getSpeed() {
		return speed;
	}
}

实体类并没有什么特别指出,看上去好轻松,接下来是封装好的命令抽象,这里的命令抽象也很简单,只支持两种情况:开、关,开了撤销就是关,关了撤销就是开

public interface Command {
	public void execute();
	public void undo();
}

public class LightOnCommand implements Command {
	Light light;
	public void setLight(Light light) {this.light = light;}

	@Override
	public void execute() {light.On();}

	@Override
	public void undo() {light.Off();}
}

public class LightOffCommand implements Command {
	Light light;
	public void setLight(Light light) {this.light = light;}

	@Override
	public void execute() {light.Off();}

	@Override
	public void undo() {light.On();}
}

//这里就不列举fan的命令对象了

这里的另外一个命令是支持多种情况的:这里只列举一个高速的命令,其他大同小异

public class CellingFanHighCommand implements Command {
	CellingFan cellingFan;
	int prevSpeed;
	
	public void setCellingFan(CellingFan cellingFan) {
		this.cellingFan = cellingFan;
	}

	@Override
	public void execute() {
		prevSpeed = cellingFan.getSpeed();
		cellingFan.high();
	}

	@Override
	public void undo() {
		switch(prevSpeed){
		case CellingFan.High:cellingFan.high();break;
		case CellingFan.LOW:cellingFan.Low();break;
		case CellingFan.MEDIUM:cellingFan.medium();break;
		case CellingFan.OFF:cellingFan.Off();break;
			default:break;
		}
	}
}
可以看到这里有一个判断:之前的速度是什么,便让执行者执行之前的命令,我们也提到过,执行者就是那堆实体类,他们知道如何执行任务

我们还需要一个空对象来填充那些null的位置

public class noCommand implements Command {
	@Override
	public void execute() {}

	@Override
	public void undo() {}

}

到了这里,我们定义了实体类,把命令抽象了出来,还需要一个遥控器

我们的遥控器根据不同的设备设置开关按钮,还有一个撤销按钮,当按下开关、转速按钮的同时把执行这一动作的命令保存到撤销命令里面,这样想撤销的时候就可以调用undoButtonPushed()方法来执行上一次的命令的undo方法,不过要用之前得设置按钮

public class RemoteControl {
	Command[] onCommands;
	Command[] offCommands;
	Command undoCommand;
	
	//开关灯
	//开关风扇
	//转速高速、关
	//转速中速、关
	//转速低速、关 共10个按钮
	public RemoteControl(){
		onCommands = new Command[5];//负责开的
		offCommands = new Command[5];//负责关的
		
		//引入空对象这一设计理念,关于空对象会在java中的类型转换博客中提及
		Command noCommand = new noCommand();
		for(int i = 0;i<5;i++){
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
		undoCommand = 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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值