1. 什么是命令模式?
《Head First设计模式》书中定义,命令模式将请求封装成对象,从而可以用不同的请求对客户端进行参数化;还可以实现请求队列、请求日志记录、宏命令和请求撤销等操作。
个人理解,命令模式中将请求操作单独封装成对象,可以称之为命令对象,每一个命令包含一个请求接收者和一系列的动作,这样请求者和接收者之间就没有直接耦合,实现了解耦。请求者不用关心请求什么时候执行、怎么执行,也不用知道具体的请求接收者的内部细节,只需要调用具体的命令对象。
《Head First设计模式》:在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。
宏命令是命令的一种简单延伸,允许调用多个命令,即一个命令中包含了多个其他命令,是一组操作命令的集合。
2. 角色
(图片来源于网络)
客户端角色(Client):负责创建调用者对象、命令对象和接收者对象,并确定他们之间的调用关系
请求者角色(Invoker):负责调用命令对象执行请求,相关的方法叫行动方法(例如图中call方法)。
接收者角色(Receiver):负责执行和具体实施一个请求。
抽象命令角色(Command):声明了一个所有具体命令共同的抽象接口。具体命令必须实现其中的execute方法。
具体命令角色(ConcreteCommand):拥有共同的接口。其中封装了接收者和一个或一组动作。在请求者和接收者之间起沟通作用,使他们两者解耦。
3. 优点
(1)将请求执行大动作进行单独封装,使请求动作与执行动作解耦,调用关系更加灵活。
(2)请求单独封装成命令过后,能够进行复用,更可以实现请求队列、请求日志记录、请求撤销和宏命令的操作。
(3)能够更好的扩展系统功能。当有新的调用关系产生时,只需要增加命令和相关的接收者对象,不会对原有代码结构进行破坏。
4. 缺点
(1)将请求执行操作单独封装过后,势必造成类数量的庞大。想要完成某个操作,就必须要了解相应的命令,增加开发者的学习成本。
(2)有些简单的命令也必须进行单独的封装。
5. 使用场景
(1)系统需要将请求调用者和请求执行者进行解耦,使两者不直接交互。
(2)系统需要将请求排队执行、请求日志记录和请求撤销/恢复 的情况。
(3)系统需要支持宏命令操作,将多个操作命令组合成一个操作命令。
6. 实例代码
模拟收音机功能,实现播放(play)、暂停(suspend)、倒退(back off)功能。
(1)接收者角色类
/**
* 收音机对象,相当于Receiver角色
*/
public class RadioMachine {
/*播放*/
public void play(){
System.out.println("Radio has played...");
}
/*暂停*/
public void suspend(){
System.out.println("Radio has suspended...");
}
/*倒退*/
public void backOff(){
System.out.println("Radio has Back off...");
}
}
(2)抽象命令角色类
/**
* 公共抽象命令接口
*/
public interface Command {
public void execute();
}
(3)具体命令角色类
/**
* 播放命令,相当于ConcreteCommand角色
*/
public class PlayCommand implements Command {
RadioMachine radio;
public PlayCommand() {
radio=new RadioMachine();
}
@Override
public void execute() {
radio.play();
}
}
/**
* 暂停操作,相当于ConcreteCommand角色
*/
public class SuspendCommand implements Command {
RadioMachine radio;
public SuspendCommand() {
radio=new RadioMachine();
}
@Override
public void execute() {
radio.suspend();
}
}
/**
* 倒退操作,相当于ConcreteCommand操作
*/
public class BackOffCommand implements Command {
RadioMachine radio;
public BackOffCommand() {
radio=new RadioMachine();
}
@Override
public void execute() {
radio.backOff();
}
}
(4)调用者角色类
/**
* 命令请求者
*/
public class Invoker {
Command command;
public Invoker() {
}
/*构造器注入*/
public Invoker(Command command) {
this.command=command;
}
/*set方法注入*/
public void setCommand(Command command){
this.command=command;
}
/*执行命令*/
public void call(){
command.execute();
}
}
(5)客户端
/**
* 客户端角色
*/
public class Client {
public static void main(String[] args) {
Invoker invoker=new Invoker();
Command playCommand=new PlayCommand();
Command suspendCommand=new SuspendCommand();
Command backOffCommand=new BackOffCommand();
invoker.setCommand(playCommand);
invoker.call();
invoker.setCommand(suspendCommand);
invoker.call();
invoker.setCommand(backOffCommand);
invoker.call();
}
}
(6)测试结果
Radio has played...
Radio has suspended...
Radio has Back off...
【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】