JAVA设计模式之命令模式

一、命令模式简介

Command模式也叫命令模式 ,是行为设计模式的一种。Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。

二、命令模式结构

将请求、命令、动作等封装成对象,这样可以让项目使用这些对象来 参数化其他对象。使得命令的请求者和执行者解耦。
在这里插入图片描述

三、命令模式的角色与职责

Command : Command抽象类。
ConcreteCommand :Command的具体实现类。
Receiver :需要被调用的目标对象。
Invorker : 通过Invorker执行Command对象。

四、命令模式的具体实现

进入了智能家居时代,大米想做一个集合智能家居遥控的控制器。

1、不使用命令模式

设计方案

采用功能绑定的设计方案。在这里插入图片描述

类设计

大米早期的设计还不成型,只能做了粗糙的设计。
首先看大米的智能家电。
大米电灯:

// An highlighted block
package design.command.gys.nocommand;
public class Light {
 private String name;
 public Light(String name) {
  super();
  this.name = name;
 }
     public void on() {
      System.out.println(name+" Light on");
     }
     public void off() {
      System.out.println(name+" Light off");
     }
}

大米电视机:

// An highlighted block
package design.command.gys.nocommand;
public class SmartTV {
 private String name;
 public SmartTV(String name) {
  super();
  this.name = name;
 }
     public void on() {
      System.out.println(name+" SmartTV on");
     }
     public void off() {
      System.out.println(name+" SmartTV off");
     }
}

大米CD机:

// An highlighted block
package design.command.gys.nocommand;
public class CDplayer {
     private String name;
     private  int i= 0;
 public CDplayer(String name) {
  super();
  this.name = name;
 }
     public void on() {
      System.out.println(name+" CD on");
     }
     public void off() {
      System.out.println(name+" CD off");
     }
     public void volumeadd() {
      if(i<=20)
       i++;
      System.out.println(name+" CD volumeadd: "+i);
     }
     public void volumesub() {
      if(i>0)
       i--;
      System.out.println(name+" CD volumesub "+i);
     }
}

再来看看大米的集成遥控器,4*2一共8个按钮。

// An highlighted block
package design.command.gys.nocommand;
public class Control {
 private Light light;
 private SmartTV TV;
 private CDplayer Cdplayer;
  public Control(Light light, SmartTV tV, CDplayer cdplayer) {
  super();
  this.light = light;
  this.TV = tV;
  this.Cdplayer = cdplayer;
 }
 public void pressleftbutton(int button)
  {
   switch(button) {
   case 0: light.on();break;
   case 1: TV.on();break;
   case 2: Cdplayer.on();break;
   case 3: Cdplayer.volumeadd();break;
   default :    break;
   }
  }
  public void pressrightbutton(int button)
  {
   switch(button) {
   case 0: light.off();break;
   case 1: TV.off();break;
   case 2: Cdplayer.off();break;
   case 3: Cdplayer.volumesub();break;
   default :    break;
   }
 }
}

看来大米的控制器采用了最简单的switch语句,判断输入再执行相应的程序。好了,看看我们测试程序。

// An highlighted block
package design.command.gys.nocommand;
public class Test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Control ct=new Control(new Light("bedroom"),new  SmartTV("living room"), new CDplayer("dining room"));
  ct.pressleftbutton(0);
  ct.pressleftbutton(1);
  ct.pressleftbutton(2);
  ct.pressleftbutton(3);
  ct.pressleftbutton(3);
  ct.pressleftbutton(3);
  ct.pressrightbutton(3);
  ct.pressrightbutton(2);
  ct.pressrightbutton(1);
  ct.pressrightbutton(0);
 }
}

测试的结果:

// An highlighted block
bedroom Light on
living room SmartTV on
dining room CD on
dining room CD volumeadd: 1
dining room CD volumeadd: 2
dining room CD volumeadd: 3
dining room CD volumesub 2
dining room CD off
living room SmartTV off
bedroom Light off

效果不错,第一代大米继承遥控器就这样面市了。

2、使用命令模式

随着家里的智能家电越来越多,功能也越来越强大,大米CEO准备升级一代遥控器。

设计方案

每个按钮可以编程动态绑定一个命令对象,而每个命令对象可以使用同一类智能家电进行参数化,具有较强的扩展性。
在这里插入图片描述

类设计

大米公司升级了之前的智能CD播放器:

// An highlighted block
package design.command.gys.appliances;
public class CDplayer {
 private String name;
    private  int volume= 0;
 public CDplayer(String name) {
  super();
  this.name = name;
 }
    public void on() {
     System.out.println(name+" CDplayer on");
    }
    public void off() {
     System.out.println(name+" CDplayer off");
    }
    public void start() {
     System.out.println(name+" CDplayer plays music");
    }
    public void stop() {
     System.out.println(name+" CDplayer stops music");
    }
    public void volumeadd() {
     if(volume<=20)
     volume++;
     System.out.println(name+" CD volumeadd: "+volume);
    }
    public void volumesub() {
     if(volume>0)
     volume--;
     System.out.println(name+" CD volumesub "+volume);
    }
}

并且又推出了智能冰箱:

// An highlighted block
package design.command.gys.appliances;
public class Bridge {
 private String name;
 public Bridge(String name) {
  super();
  this.name = name;
 }
     public void on() {
      System.out.println(name+" Bridge on");
     }
     public void off() {
      System.out.println(name+" Bridge off");
     }
}

推出这么多智能家电,原来的集成遥控器就不能用了。于是,负责人决定做一款能够让使用者自己编程使用的遥控器。
首先定义了一个抽象的命令的类:

// An highlighted block
package design.command.gys.command;
public abstract class Command {
 public abstract void execute() ;
 public abstract void undo() ;
}

这个类里面定义了两个抽象的方法一个执行,一个撤销用来执行相反的动作,在每个继承类中都实现。
接下来我们定义控制类。
灯控制类:

// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.Light;
public class LightOnCommand extends Command{
 private Light light;
 public LightOnCommand(Light light) {
  super();
  this.light = light;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  light.on();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  light.off();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.Light;
public class LightOffCommand extends Command{
 private Light light;
 public LightOffCommand(Light light) {
  super();
  this.light = light;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  light.off();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  light.on();
 }
}

智能电视控制类:

// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.SmartTV;
public class SmartTVOnCommand extends Command{
 private SmartTV TV;
 public SmartTVOnCommand(SmartTV TV) {
  super();
  this.TV = TV;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  TV.on();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  TV.off();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.SmartTV;
public class SmartTVOffCommand extends Command{
 private SmartTV TV;
 public SmartTVOffCommand(SmartTV TV) {
  super();
  this.TV = TV;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  TV.off();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  TV.off();
 }
}

冰箱控制类:

// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.Bridge;
public class BridgeOnCommand extends Command{
 private Bridge bridge;
 public BridgeOnCommand(Bridge bridge) {
  super();
  this.bridge = bridge;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  bridge.on();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  bridge.off();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.Bridge;
public class BridgeOffCommand extends Command{
 private Bridge bridge;
 public BridgeOffCommand(Bridge bridge) {
  super();
  this.bridge = bridge;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  bridge.off();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  bridge.on();
 }
}

新智能CD控制类:
在打开的同时播放音乐,在关闭之前停止播放音乐

// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.CDplayer;
public class CDplayerOnCommand extends Command{
 private CDplayer player;
 public CDplayerOnCommand(CDplayer player) {
  super();
  this.player = player;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  player.on();
  player.start();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  player.stop();
  player.off();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.CDplayer;
public class CDplayerOffCommand extends Command{
 private CDplayer player;
 public CDplayerOffCommand(CDplayer player) {
  super();
  this.player = player;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  player.stop();
  player.off(); 
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  player.on();
  player.start();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.CDplayer;
public class CDplayerVolumeAdd extends Command{
 private CDplayer player;
 public CDplayerVolumeAdd(CDplayer player) {
  super();
  this.player = player;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  player.volumeadd();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  player.volumesub();
 }
}
// An highlighted block
package design.command.gys.command;
import design.command.gys.appliances.CDplayer;
public class CDplayerVolumeSub extends Command{
 private CDplayer player;
 public CDplayerVolumeSub(CDplayer player) {
  super();
  this.player = player;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
  player.volumesub();
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  player.volumeadd();
 }
}

另外我们定义了无操作的命令类,用在没有实现命令编程的按钮上:

// An highlighted block
package design.command.gys.command;
public class NoCommand extends Command{
 public void execute() {};
 public void undo() {};
}

下面是这个集成遥控器的设计。定义两个数组,左边五个右边五个,但是可以自由组合,不必与固定功能绑定。

// An highlighted block
package design.command.gys.control;
import java.util.Stack;
import design.command.gys.command.Command;
import design.command.gys.command.NoCommand;
public class Controller {
 private Command[] leftcommands =new Command[5];
 private Command[] rightcommands=new Command[5];
 public Controller() {
  super();
 }
 public void setLeftCommand(Command[] command) {
  for(int i=0;i<command.length;i++)
   leftcommands[i]=command[i];
  for(int i=command.length;i<5;i++)
    leftcommands[i]=new NoCommand();
 }
 public void setRightCommand(Command[] command) {
  for(int i=0;i<command.length;i++)
   rightcommands[i]=command[i];
  for(int i=command.length;i<5;i++)
   rightcommands[i]=new NoCommand();
 }
 public void pressLeft(int botton)
 {
  leftcommands[botton].execute();
 }
 
 public void pressRight(int botton)
 {
  rightcommands[botton].execute();
 }
}

接下来我们看看二代遥控器能否实现我们需要的功能。给每个按钮绑定上我们需要的智能家电的命令类,并参数化我们已有的相应的家电。下面是代码:

// An highlighted block
package design.command.gys.control;
import design.command.gys.appliances.*;
import design.command.gys.command.*;
public class Test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Light L1=new Light("bedroom");
  SmartTV TV=new SmartTV("living room");
  CDplayer player=new CDplayer("dining room");
  Bridge bridge =new Bridge("dining room");
  Controller ct=new Controller();
  Command[] leftcommands=new Command[]{new LightOnCommand(L1),
    new BridgeOnCommand(bridge),new CDplayerOnCommand(player),
    new CDplayerVolumeAdd(player),new SmartTVOnCommand(TV)};
  Command[] rightcommands=new Command[]{new LightOffCommand(L1),
    new BridgeOffCommand(bridge),new CDplayerOffCommand(player),
    new CDplayerVolumeSub(player),new SmartTVOffCommand(TV)};
  ct.setLeftCommand(leftcommands);
  ct.setRightCommand(rightcommands);
  ct.pressLeft(0);ct.pressLeft(1);ct.pressLeft(2);ct.pressLeft(3);ct.pressLeft(4);
  ct.pressRight(0);ct.pressRight(1);ct.pressRight(2);ct.pressRight(3);ct.pressRight(4);
  }
}

看看测试结果吧:

// An highlighted block
bedroom Light on
dining room Bridge on
dining room CDplayer on
dining room CDplayer plays music
dining room CD volumeadd: 1
living room SmartTV on
bedroom Light off
dining room Bridge off
dining room CDplayer stops music
dining room CDplayer off
dining room CD volumesub 0
living room SmartTV off

完美运行。
这时候,设计者又想一个按钮能够控制所有的家电。于是,有涉及了下面的AllCommand类:

// An highlighted block
package design.command.gys.command;
public class AllCommand extends Command{
 private Command[] commands ;
 public AllCommand(Command[] commands) {
  super();
  this.commands = commands;
 }
 @Override
 public void execute() {
  // TODO Auto-generated method stub
   for(Command c:commands)
    c.execute(); 
 }
 @Override
 public void undo() {
  // TODO Auto-generated method stub
  for(Command c:commands)
   c.undo();
 }
}

但是这样也不需要重新设计遥控器,我们来看测试程序:

// An highlighted block
package design.command.gys.control;
import design.command.gys.appliances.*;
import design.command.gys.command.*;
public class Test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Light L1=new Light("bedroom");
  SmartTV TV=new SmartTV("living room");
  CDplayer player=new CDplayer("dining room");
  Bridge bridge =new Bridge("dining room");
  Controller ct=new Controller();
  Command[] leftcommands=new Command[]{new LightOnCommand(L1),
    new BridgeOnCommand(bridge),new CDplayerOnCommand(player),
    new CDplayerVolumeAdd(player),new SmartTVOnCommand(TV)};
  Command[] rightcommands=new Command[]{new LightOffCommand(L1),
    new BridgeOffCommand(bridge),new CDplayerOffCommand(player),
    new CDplayerVolumeSub(player),new SmartTVOffCommand(TV)};
  ct.setLeftCommand(leftcommands);
  ct.setRightCommand(rightcommands);
  ct.pressLeft(0);ct.pressLeft(1);ct.pressLeft(2);ct.pressLeft(3);ct.pressLeft(4);
  ct.pressRight(0);ct.pressRight(1);ct.pressRight(2);ct.pressRight(3);ct.pressRight(4);
  
  System.out.println("---------------");
  
  Controller cta=new Controller();
  AllCommand leftcommand=new AllCommand(leftcommands);
  AllCommand rightcommand=new AllCommand(rightcommands);
  cta.setLeftCommand(new Command[] {leftcommand});
  cta.setRightCommand(new Command[] {rightcommand});
  cta.pressLeft(0);
  cta.pressRight(0);
    }
  }

看一下运行结果:

// An highlighted block
bedroom Light on
dining room Bridge on
dining room CDplayer on
dining room CDplayer plays music
dining room CD volumeadd: 1
living room SmartTV on
bedroom Light off
dining room Bridge off
dining room CDplayer stops music
dining room CDplayer off
dining room CD volumesub 0
living room SmartTV off
---------------
bedroom Light on
dining room Bridge on
dining room CDplayer on
dining room CDplayer plays music
dining room CD volumeadd: 1
living room SmartTV on
bedroom Light off
dining room Bridge off
dining room CDplayer stops music
dining room CDplayer off
dining room CD volumesub 0
living room SmartTV off

设计者脑袋一拍,又想出一个注意,想要来个撤销按钮。
没办法,程序猿又要加班加点了,不过也不是太难,在类中添加一个栈,用于撤回操作,每次执行一个操作时,都会将操作进行压栈,在需要撤回时进行弹栈操作,并执行undo()。当然,栈的大小我们就需要顶起清空,不能无限制的扩大,在这个程序里,暂时不实现这个功能:

// An highlighted block
package design.command.gys.control;
import java.util.Stack;
import design.command.gys.command.Command;
import design.command.gys.command.NoCommand;
  public class Controller {
  private Command[] leftcommands =new Command[5];
  private Command[] rightcommands=new Command[5];
  private Stack<Command> stack=new Stack<>();
  public Controller() {
   super();
  }
  public void setLeftCommand(Command[] command) {
   for(int i=0;i<command.length;i++)
    leftcommands[i]=command[i];
   for(int i=command.length;i<5;i++)
     leftcommands[i]=new NoCommand();
  }
  public void setRightCommand(Command[] command) {
   for(int i=0;i<command.length;i++)
    rightcommands[i]=command[i];
   for(int i=command.length;i<5;i++)
    rightcommands[i]=new NoCommand();
  }
  public void pressLeft(int botton)
  {
   leftcommands[botton].execute();
   stack.push(leftcommands[botton]);
  }
  public void pressRight(int botton)
  {
   rightcommands[botton].execute();
   stack.push(leftcommands[botton]);
  }
  public void returnPrevious() {
   stack.pop().undo();
  }
  }

在每个遥控器中,我们只需要对每个按钮进行命令类的绑定,在命令类里参数化具体的智能家电就可以了。
下面接着来测试这个类:

// An highlighted block
package design.command.gys.control;
import design.command.gys.appliances.*;
import design.command.gys.command.*;
public class Test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Light L1=new Light("bedroom");
  SmartTV TV=new SmartTV("living room");
  CDplayer player=new CDplayer("dining room");
  Bridge bridge =new Bridge("dining room");
  Controller ct=new Controller();
  Command[] leftcommands=new Command[]{new LightOnCommand(L1),
    new BridgeOnCommand(bridge),new CDplayerOnCommand(player),
    new CDplayerVolumeAdd(player),new SmartTVOnCommand(TV)};
  Command[] rightcommands=new Command[]{new LightOffCommand(L1),
    new BridgeOffCommand(bridge),new CDplayerOffCommand(player),
    new CDplayerVolumeSub(player),new SmartTVOffCommand(TV)};
  ct.setLeftCommand(leftcommands);
  ct.setRightCommand(rightcommands);
  ct.pressLeft(0);ct.pressLeft(1);ct.pressLeft(2);ct.pressLeft(3);ct.pressLeft(4);
  ct.pressRight(0);ct.pressRight(1);ct.pressRight(2);ct.pressRight(3);ct.pressRight(4);
  System.out.println("---------------");
  Controller cta=new Controller();
  AllCommand leftcommand=new AllCommand(leftcommands);
  AllCommand rightcommand=new AllCommand(rightcommands);
  cta.setLeftCommand(new Command[] {leftcommand});
  cta.setRightCommand(new Command[] {rightcommand});
  cta.pressLeft(0);
  cta.pressRight(0);
  System.out.println("---------------");
  ct.pressLeft(0);ct.returnPrevious();
  ct.pressLeft(1);ct.returnPrevious();
 }
}

看一下运行结果:

// An highlighted block
bedroom Light on
dining room Bridge on
dining room CDplayer on
dining room CDplayer plays music
dining room CD volumeadd: 1
living room SmartTV on
bedroom Light off
dining room Bridge off
dining room CDplayer stops music
dining room CDplayer off
dining room CD volumesub 0
living room SmartTV off
---------------
bedroom Light on
dining room Bridge on
dining room CDplayer on
dining room CDplayer plays music
dining room CD volumeadd: 1
living room SmartTV on
bedroom Light off
dining room Bridge off
dining room CDplayer stops music
dining room CDplayer off
dining room CD volumesub 0
living room SmartTV off
---------------
bedroom Light on
bedroom Light off
dining room Bridge on
dining room Bridge off

到此,二代集成遥控器也面市了。

五、命令模式的应用场景

在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。
但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。

  • 整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
  • 调用前后需要对调用参数进行某些处理。
  • 调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。

要抱抱才会开心啊~~~~~~~~~~~~

在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值