命令模式
一 概述
什么是命令模式
- 概念:将一个请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销操作
- 举个例子:我们老早以前的电视和电灯都需要亲自过去开或者关,但是到了现在,我们所有的灯与电视都可以通过一个遥控器进行控制。这样我们只需要知道遥控器上的哪些按钮控制什么,点下发出相关的命令即可,而不需要直接去操作那个电器。
二 模式的结构与实现
结构
- 接收者:接收者是一个类的实例,该实例负责执行与请求相关的操作
- 命令接口:命令是一个接口,规定了用来封装“请求”的若干个方法
- 具体命令:实现命令接口的类的实例
- 请求者:包含了命令接口的实例,可以通过调用具体的命令去执行封装了请求的方法
实现
我们就按遥控器的例子来设计,假设我们有一个10个按钮的遥控器,两个控制灯的开关,两个控制电视的开关,剩下的留作未来产品的按钮,现在点上去没有效果,除了这四个按钮,还有一个特殊的撤销按钮,用来撤销上一步的操作。
具体实现:
- 接收者
1.电视
public class TV {
public void setOn(){
System.out.println("电视开");
}
public void setOff(){
System.out.println("电视关");
}
}
2.电灯
public class Light {
public void setOn(){
System.out.println("灯开了");
}
public void setOff(){
System.out.println("灯关了");
}
}
- 命令接口
public interface ICommand {
//分装请求的方法
public void execute();
//撤销操作
public void undo();
}
- 具体命令
1.电灯开的命令
public class LightOnCommand implements ICommand{
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.setOn();
}
@Override
public void undo() {
light.setOff();
}
}
2.电灯关的命令
public class LightOffCommand implements ICommand{
private Light light;
public LightOffCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.setOff();
}
@Override
public void undo() {
light.setOn();
}
}
3.电视开的命令
public class TVOnCommand implements ICommand{
private TV tv;
public TVOnCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.setOn();
}
@Override
public void undo() {
tv.setOff();
}
}
4.电视关的命令
public class TVOffCommand implements ICommand{
private TV tv;
public TVOffCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.setOff();
}
@Override
public void undo() {
tv.setOn();
}
}
5.空命令,用来初始化每个遥控器的按钮
public class NoCommand implements ICommand{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
- 请求者
public class Control {
private ICommand[] onCommand;
private ICommand[] offCommand;
//用栈来存放操作,用来实现撤销的操作
private Stack<ICommand> s = new Stack<>();
public Control(){
onCommand = new ICommand[5];
offCommand = new ICommand[5];
//初始化每个按钮
for(int i = 0;i<onCommand.length;i++){
onCommand[i] = new NoCommand();
}
for(int i = 0;i<offCommand.length;i++){
offCommand[i] = new NoCommand();
}
}
//设置按钮
public void setOnCommand(int i,ICommand on){
onCommand[i] = on;
}
public void setOffCommand(int i,ICommand off){
offCommand[i] = off;
}
//按下开的按钮
public void onButton(int i){
onCommand[i].execute();
s.push(onCommand[i]);
}
//按下关的按钮
public void offButton(int i){
offCommand[i].execute();
s.push(offCommand[i]);
}
//撤销按钮
public void undoButton(){
s.pop().undo();
}
}
- 主函数
public class MainClass {
public static void main(String[] args) {
Light l = new Light();
TV t = new TV();
//设置遥控器
Control c = new Control();
ICommand lon = new LightOnCommand(l);
ICommand lof = new LightOffCommand(l);
ICommand ton = new TVOnCommand(t);
ICommand tof = new TVOffCommand(t);
c.setOnCommand(0, lon);
c.setOnCommand(1, ton);
c.setOffCommand(0, lof);
c.setOffCommand(1, tof);
//操作遥控器
c.onButton(0);
c.onButton(1);
c.offButton(0);
c.offButton(1);
//撤销操作
c.undoButton();
c.undoButton();
}
}
结果
关于宏命令(组合命令模式)
上面我们讲了普通命令模式,我们再说说宏命令也就是组合命令,顾名思义,组合命令就是将命令组合起来。其实宏命令也是一个具体的命令,只不过它包含了要组合的命令的引用,当他执行操作时,其他命令也会执行操作,所以执行一个宏命令相当于执行了很多其他命令。
具体实现
在上面的基础是我们再加一个特殊的命令宏命令,这里,我是按照开和关来组合的
1.一次全开
public class AllOnCommand implements ICommand{
private ArrayList<ICommand> all;
public AllOnCommand(ArrayList<ICommand> all){
this.all = all;
}
@Override
public void execute() {
for(int i = 0;i<all.size();i++){
ICommand c = all.get(i);
c.execute();
}
}
@Override
public void undo() {
for(int i = 0;i<all.size();i++){
ICommand c = all.get(i);
c.undo();
}
}
}
2.一次全关
public class AllOffCommand implements ICommand{
private ArrayList<ICommand> all;
public AllOffCommand(ArrayList<ICommand> all){
this.all = all;
}
@Override
public void execute() {
for(int i = 0;i<all.size();i++){
ICommand c = all.get(i);
c.execute();
}
}
@Override
public void undo() {
for(int i = 0;i<all.size();i++){
ICommand c = all.get(i);
c.undo();
}
}
}
主函数,在上面主函数的基础上加入
ArrayList<ICommand> allon = new ArrayList<>();
allon.add(lon);
allon.add(ton);
ICommand aon = new AllOffCommand(allon);
ArrayList<ICommand> allff = new ArrayList<>();
allff.add(lof);
allff.add(tof);
ICommand aof = new AllOffCommand(allff);
c.setOnCommand(2, aon);
c.setOffCommand(2, aof);
c.onButton(2);
c.offButton(2);
结果
三 模式的优点与适用场景
优点
- 在命令模式中,请求者不与接收者交互,消除了之间的耦合
- 在增加新命令与该命令的接收者时,不需要修改调用者的代码,调用者就可以使用新的命令,而且增加新的调用者也不用修改接收者与具体命令。
适用场景
- 程序需要在不同的时刻指定,执行请求
- 程序需要提供撤销操作
- 程序需要支持宏操作
这就是我所理解的命令模式,如果我有什么错误或者你有更好的想法请告诉我。