本文最后更新于:3 years ago
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于 行为型模式 。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
如果想了解命令模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-命令模式
结构图
优缺点
优点:
1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景
认为是命令的地方都可以使用命令模式,比如:
1、GUI 中每一个按钮都是一条命令。
2、模拟 CMD。
实现代码
Command模式可以说在我们日常生活中用得比较多的了,命令模式主要解决我们在开发过程中,请求者与实现者的解耦
举个栗子,比如空调遥控器,我们在使用空调遥控器的时候,只需要按下相应的键就可以完成温度的调控,这就是命令模式
在这里温度调控请求和温度调控处理完全解耦了,空调遥控器(命令发送者)通过按钮(具体命令)来遥控空调(命令接收者)
其实写到现在会发现很多模式其原理都是很类似的,只是每个模式的作用不同罢了
既然提到遥控器这个栗子,那就用这个例子来介绍一下吧
首先定义一个Command的接口,接口中有一个execute的抽象类方法
再创建一个RemoteControl的遥控器类,来实现Command接口,并且在RemoteControl中定义一个AirConditioner类型的变量,用于实现温度调控
接着我们再创建一个Invoker类来模拟用户,其中定义一个Command,无参构造用于接收传入的Command,并且定义call方法用于调用传入Command中的execute方法(这里就实现了请求者(Invoker)与实现者(AirConditioner)的解耦)
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Main { public static void main(String[] args) { Command cmd = new RemoteControl(); Invoker iv = new Invoker(cmd); System.out.println("已按下按键..."); iv.call(); } }
public interface Command { public abstract void execute(); }
public class RemoteControl implements Command{ private AirConditioner airCon;
public RemoteControl (){ airCon = new AirConditioner(); }
@Override public void execute() { airCon.action(); } }
public class AirConditioner { public void action(){ System.out.println("温度已改变..."); } }
public class Invoker { private Command command;
public Invoker (Command command){ this.command = command; }
public void call(){ System.out.println("温度调控命令已执行..."); command.execute(); } }
|
控制台输出如下:
| 已按下按键... 温度调控命令已执行... 温度已改变...
|
以上就是命令模式的代码,很简单
接着我在补充一个常用到的一个例子,Undo的实现
拿记事本来讲吧,我们在往记事本中编写东西的时候,有时候会撤销一些之前我们做过的操作(这里可以理解为Ctrl+z,但是实际上Ctrl+z是备忘录模式的实现),这就是我们要实现的Undo操作
首先我们创建一个Command接口,含有操作(doIt)和撤销(undo)两个方法
接着继续创建三个类:CopyCommand、DeleteCommand和InsertCommand三个方法分别实现Command接口,
最后创建一个我们需要处理的类Content
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public class Main { public static void main(String[] args) { Content content = new Content();
Command command = new InsertCommand(content); command.doIt("Hello"); command.undo();
Command command1 = new DeleteCommand(content); command1.doIt(); command1.undo();
Command command2 = new CopyCommand(content); command2.doIt(); command2.undo(); } }
public interface Command { public abstract void doIt(); public abstract void undo(); }
public class CopyCommand implements Command{ Content c; String strToCopy; public CopyCommand(Content c){ this.c = c; }
@Override public void doIt() { c.msg = c.msg + c.msg; System.out.println(c.msg); }
@Override public void undo() { c.msg = c.msg.substring(0, c.msg.length()/2); System.out.println(c.msg); } }
public class DeleteCommand implements Command{ Content c; String strToDelete; public DeleteCommand(Content c){ this.c = c; }
@Override public void doIt() { strToDelete = c.msg.substring(0,5); c.msg = c.msg.substring(5, c.msg.length()); System.out.println(c.msg); }
@Override public void undo() { c.msg = strToDelete + c.msg; System.out.println(c.msg); } }
public class InsertCommand implements Command{ Content c; String strToInsert; public InsertCommand(Content c){ this.c = c; }
@Override public void doIt(String str) { strToInsert = str; c.msg = c.msg + str; System.out.println(c.msg); }
@Override public void undo() { c.msg = c.msg.substring(0,c.msg.length()-strToInsert.length()); System.out.println(c.msg); } }
public class Content { String msg = "Welcome "; }
|
CopyCommand中,我们实现复制的命令,其中doIt方法用于复制传入的内容,而undo则实现撤销复制的操作;
DeleteCommand中,我们实现删除的命令,其中doIt方法用于删除前五个字符,并且保留前五个字符用于undo的撤销操作;
InsertCommand中,我们实现插入的命令,其中doIt方法用于插入传入的字符串,undo用于撤销刚刚的插入
输出结果:
| Welcome Hello Welcome me Welcome Welcome Welcome Welcome
|
这里就简单实现了一下一些操作的撤销功能,而真实的情况是,我们需要记录多条undo,因为我们可能需要多次撤销,此时我们这里的操作就不满足需求了
解决方式就需要将命令模式和责任链模式结合使用,将用过的Command都放到一个容器中,接着就可以实现多个undo了(代码就不写了,提供思路)