定义
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
在软件系统中,方法的请求者与方法的实现者总是存在着紧密的耦合关系,这不利于扩展和维护。因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。
在现实生活中,我们想看电视,只要按遥控器的开关即可,而不用知道电视机内部怎么实现的。
角色
命令模式通用类图如下图
Command抽象命令接口:所有要执行的命令会在这里声明。
ConcreteCommand具体命令:它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
Receiver接收者:命令传到这里来会被执行
Invoker调用者:接受命令,并执行命令
举例实现
就拿我们平常对数据库的增删改查举例
数据库类(Receiver)
package factory.command;
public class DataBase {
public void add(){
System.out.println("添加数据。。。");
}
public void del(){
System.out.println("删除数据。。。");
}
public void modify(){
System.out.println("修改数据。。。");
}
public void select(){
System.out.println("查询数据。。。");
}
}
JDBC(Receiver)
package factory.command;
public class JDBC {
public void getConnect(){
System.out.println("获取连接。。。");
}
public void transfer(){
System.out.println("传递指令。。。");
}
public void returnData(){
System.out.println("返回结果。。。");
}
}
Command
package factory.command;
public abstract class Command {
protected DataBase dataBase = new DataBase();
protected JDBC jdbc = new JDBC();
public abstract void execute();
}
ConcreteCommand
//AddCommand
package factory.command;
public class AddCommand extends Command {
@Override
public void execute() {
super.jdbc.getConnect();
super.jdbc.transfer();
super.dataBase.add();
super.jdbc.returnData();
}
}
//DelCommand
package factory.command;
public class DelCommand extends Command {
@Override
public void execute() {
super.jdbc.getConnect();
super.jdbc.transfer();
super.dataBase.del();
super.jdbc.returnData();
}
}
//ModifyCommandi
package factory.command;
public class ModifyCommand extends Command {
@Override
public void execute() {
super.jdbc.getConnect();
super.jdbc.transfer();
super.dataBase.modify();
super.jdbc.returnData();
}
}
//SelectCommand
package factory.command;
public class SelectCommand extends Command {
@Override
public void execute() {
super.jdbc.getConnect();
super.jdbc.transfer();
super.dataBase.select();
super.jdbc.returnData();
}
}
Invoker调用者
package factory.command;
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action(){
this.command.execute();
}
}
测试类
package factory.command;
public class Client {
public static void main(String[] args) {
//定义调用者
Invoker invoker = new Invoker();
//创建命令
Command command = new AddCommand();
//调用者接受命令
invoker.setCommand(command);
//执行命令
invoker.action();
}
}
结果如下
优点
- 降低系统的耦合度:调用者与接收者之间没有依赖关系,调用者只要调用command与抽象类的execute就可以了
- 增加或删除命令非常方便:采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 更动态的控制:命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。
- 复合命令:命令模式中的命令对象能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大
缺点
可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
扩展
参考自:这里
- 宏命令
宏命令又称为组合命令,它是命令模式和组合模式联用的产物。
宏命令也是一个具体命令,不过它包含了对其他命令对象的引用,在调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法,一个宏命令的成员对象可以是简单命令,还可以继续是宏命令。执行一个宏命令将执行多个具体命令,从而实现对命令的批处理。 - 撤销命令
有两种基本的思路来实现可撤销的操作:
逆向操作实现:比如被撤销的操作是添加功能,那撤消的实现就变成删除功能;同理被撤销的操作是删除功能,那么撤销的实现就变成添加功能。
存储恢复实现,意思就是把操作前的状态记录下来,然后要撤销操作的时候就直接恢复回去就可以了,可使用备忘录模式(Memento Pattern)来实现。 - 命令队列
有时候我们需要将多个请求排队,当一个请求发送者发送一个请求时,将不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,我们可以通过命令队列来实现。
命令队列的实现方法有多种形式,其中最常用、灵活性最好的一种方式是增加一个CommandQueue类,由该类来负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者。 - 请求日志
概念:将所有的动作都记录在日志中,并能在系统死机后,重新调用这些动作恢复到之前的状态。当我们执行命令的时候,将历时记录存储在磁盘中。一旦系统死机,我们就可以将命令对象重新加载,并依次调用这些对象的execute()方法。
实现方法:利用对象的序列化把对象保存起来(记录日志),在需要的时候反序列化(恢复事务)。
请求日志文件可以实现很多功能,常用功能如下:
1、什么事情都存在意外,如一旦系统发生故障,日志文件可以为系统提供一种恢复机制,在请求日志文件中可以记录用户对系统的每一步操作,从而让系统能够顺利恢复到某一个特定的状态;
2 、请求日志也可以用于实现批处理,在一个请求日志文件中可以存储一系列命令对象,例如一个命令队列;
3、可以将命令队列中的所有命令对象都存储在一个日志文件中,每执行一个命令则从日志文件中删除一个对应的命令对象,防止因为断电或者系统重启等原因造成请求丢失,而且可以避免重新发送全部请求时造成某些命令的重复执行,只需读取请求日志文件,再继续执行文件中剩余的命令即可。