一、什么叫做命令模式
定义:将请求封装成对象,这可以让你使用不同的队列,请求,或者日志请求来参数化其他对象。命令模式也支持撤销操作。
二、使用场景
当需要将发出请求的对象和执行请求的对象解耦的时候使用命令模式。
三、命令模式的构成:
命令模式包括:(1)命令接口或者命令抽象类(Command):定义命令类所需要的方法
(2)具体命令类(***Command):抽象命令的具体实现,里面包含了命令的接收者,用于执行真正的命令
(3)命令请求者(invoker):请求命令,让具体接受者(receiver)执行
(4)命令接受者(receiver):具体命令的执行者
(5)客户端类(Client):用于分配命令以及命令的执行者
四、具体场景:
有一个厂家生产了一个控制器,上面有七个开关,用来控制七种不同的电器。如何实现控制器控制开关的行为更加方便,并且有利于以后的扩展呢?
分析:控制器可以当作命令请求者,开关的开与关需要被赋予不同的命令功能,这些命令都实现command接口,为每一个命令赋予一个命令的执行者。当以后有新的电器加入的时候,只需要再写一个具体命令实现命令接口然后在具体命令中放入新的电器即可。
具体代码:
/**
* 命令接口
*
*/
interface Command{
public void execute();
}
/**
* 命令的执行者
*
*/
class TV{
public void openTV(){
System.out.println("打开电视");
}
public void closeTV(){
System.out.println("关闭电视");
}
}
class Light{
public void openLight(){
System.out.println("开灯");
}
public void closeLight(){
System.out.println("关灯");
}
}
//具体命令,里面包含对命令执行者的引用
class TVOpenCommand implements Command{
private TV tv;
public TVOpenCommand(TV tv) {
// TODO Auto-generated constructor stub
this.tv=tv;
}
@Override
public void execute() {
tv.openTV();
}
}
class TVCloseCommand implements Command{
private TV tv;
public TVCloseCommand(TV tv) {
this.tv=tv;
}
@Override
public void execute() {
tv.closeTV();
}
}
class LightOpenCommand implements Command{
private Light light;
public LightOpenCommand(Light light) {
this.light=light;
}
@Override
public void execute() {
light.openLight();
}
}
class LightOffCommand implements Command{
private Light light;
public LightOffCommand(Light light) {
this.light=light;
}
@Override
public void execute() {
light.closeLight();
}
}
/**
* 当没有命令的时候,设置成空命令类,这样便于管理
*
*/
class NOCommand implements Command{
@Override
public void execute() {
System.out.println("没有命令放在这");
}
}
/**
* 命令的执行者
*
*/
class ElectricController{
private Command[] onCommands=new Command[7];
private Command[] offCommands=new Command[7];
private Command undoCommand;
public ElectricController(){
for (int i=0;i<7;i++){
onCommands[i]=new NOCommand();
offCommands[i]=new NOCommand();
}
undoCommand=new NOCommand();
}
public void setCommand(int pos,Command onCommand,Command offCommand){
onCommands[pos]=onCommand;
offCommands[pos]=offCommand;
}
public void removeCommand(int pos){
offCommands[pos]=new NOCommand();
onCommands[pos]=new NOCommand();
}
public void ONCommandButton(int pos){
onCommands[pos].execute();
undoCommand=onCommands[pos];
}
public void OFFCommandButton(int pos){
offCommands[pos].execute();
undoCommand=offCommands[pos];
}
public void undoButton(){//这个在接下来说明作用
undoCommand.execute();
}
}
/**
* 客户端类,用来分配命令以及命令的执行者
*
*/
public class ControllerClient {
public static void main(String args[]){
ElectricController electricController=new ElectricController();
TV tv=new TV();
Command tvOpenCommand=new TVOpenCommand(tv);
Command tvCloseCommand=new TVCloseCommand(tv);
electricController.setCommand(0,tvOpenCommand, tvCloseCommand);
electricController.ONCommandButton(0);
electricController.OFFCommandButton(0);
Light light=new Light();
Command lightOnCommand=new LightOpenCommand(light);
Command lightOffCommand=new LightOffCommand(light);
electricController.setCommand(1, lightOnCommand, lightOffCommand);
electricController.ONCommandButton(1);
electricController.undoButton();
}
}
/**
* 输出
打开电视
关闭电视
开灯
关灯
*/
ps:关于undoButton方法
执行命令后,可能需要执行命令的撤销,这时候可以使用undo方法,用来记录上一次的命令请求,然后撤销即可。
五、扩展
执行之前操作的全部撤销?
这时候可以用一个栈保存全部的操作,每执行一个操作就放入栈中,撤销时候,每次从栈中取出一个元素执行撤销操作,并移除直到栈为空。
日志请求:
如果需要将操作记录到磁盘中,命令模式可以做到这一点,通过在执行者中添加一个load和store方法,把每一次执行的命令,通过load存储到本地磁盘,当服务器出错后,可以从磁盘中从检查点开始(也就是store开始记录的时候),调用load执行操作。
队列请求:
可以设置一个请求命令工作队列,一个命令请求者对应一个请求队列,从中按顺序取出命令执行。