设计模式(六)命令模式

题记

    在计算机中,很多时候当你遇到一个棘手的问题时,尝试在中间加一层,或许你的问题就会迎刃而解。

概述

    有一个控制电灯开关的遥控器,要求它能控制不同厂商生产的电灯,而且不同厂商生产的电灯提供的接口又不同,比如厂商A开关灯函数是lightOn和lightOff,而厂商B开关灯函数是lightStart和lightStop。这个时候你肯定不会像在遥控器的实现代码中先分别判断厂商类型,再根据不同厂商类型调用具体类型的函数,这明显不符合开闭原则,万一哪天来了一个厂商C呢,你就不得不再次修改遥控器的实现代码。这个时候,你再看看本文的题记,或许你就有思路了。

命令模式

    命令模式就是用新的命令对象包装原有对象的请求,调用者再利用这个命令对象来完成原始的请求。整体上看来,就是在调用者和接收者的中间加了一层,这一层就是命令对象,以达到将调用者和接收者解耦的目的。类图如下:


命令模式

    如上图,命令模式的命令对象为了对外提供统一的方法,因此采用了一个类实现一个接口的方式,Receiver就是实际的功能提供者,提供doSomething方法,Invoker是功能的调用者,它需要调用Receiver的doSomething方法,但如果直接调用,就会代码紧密耦合,所有采用了ConcreteCommand这个命令对象来做一层媒介,这样Invoker就只管调用Command接口提供的方法,而不用在意底层到底是什么对象,关心底层对象的责任转移到了ConcreteCommand命令对象中来,这就使得Invoker和Receiver彻底解耦。ConcreteCommand对象的execute方法调用具体receiver对象的doSomething完成具体的请求。注意,命令模式可以方便的支持撤销操作,就是那个undo方法,这使得命令模式被用于事物,日志等场景,比如在事物中执行完execute后发现有问题,就可以再调用undo方法回到原来的状态。
    接下来完成遥控器的例子:
    A,B厂商提供的电灯:
public class LightA {
    public void lightOn(){
        System.out.println("A厂电灯开启");
    }

    public void lightOff(){
        System.out.println("A厂电灯关闭");
    }
}
public class LightB {
    public void lightStart(){
        System.out.println("B厂电灯开启");
    }

    public void lightStop(){
        System.out.println("B厂电灯关闭");
    }
}
    命令对象要实现的接口:
public interface Command {
    public void execute();
    public void undo();
}
    两种电灯的开关命令:
public class LightAOnCommand implements Command{
    private LightA lightA;

    public LightAOnCommand(LightA lightA) {
        this.lightA = lightA;
    }

    @Override
    public void execute() {
        lightA.lightOn();
    }

    @Override
    public void undo() {
        lightA.lightOff();
    }
}
public class LightAOffCommand implements Command{
    private LightA lightA;

    public LightAOffCommand(LightA lightA) {
        this.lightA = lightA;
    }

    @Override
    public void execute() {
        lightA.lightOff();
    }

    @Override
    public void undo() {
        lightA.lightOn();
    }
}
public class LightBOnCommand implements Command{
    private LightB lightB;

    public LightBOnCommand(LightB lightB) {
        this.lightB = lightB;
    }

    @Override
    public void execute() {
        lightB.lightStart();
    }

    @Override
    public void undo() {
        lightB.lightStop();
    }
}
public class LightBOffCommand implements Command{
    private LightB lightB;

    public LightBOffCommand(LightB lightB) {
        this.lightB = lightB;
    }

    @Override
    public void execute() {
        lightB.lightStop();
    }

    @Override
    public void undo() {
        lightB.lightStart();
    }
}
    空命令:
public class NoCommand implements Command{
    @Override
    public void execute() {
        System.out.println("空命令");
    }

    @Override
    public void undo() {
        System.out.println("空命令");
    }
}
    遥控器代码:
public class Telecontroller {
    private Command lightOnCommand;
    private Command lightOffCommand;
    private Command currentCommand;

    public Telecontroller(Command lightOnCommand,Command lightOffCommand,Command currentCommand) {
        this.lightOnCommand = lightOnCommand;
        this.lightOffCommand = lightOffCommand;
        this.currentCommand = currentCommand;
    }

    public void lightOn(){
        lightOnCommand.execute();
        currentCommand = lightOnCommand;
    }

    public void lightOff(){
        lightOffCommand.execute();
        currentCommand = lightOffCommand;
    }

    public void undo(){
        currentCommand.undo();
    }

    public void setLightOnCommand(Command lightOnCommand) {
        this.lightOnCommand = lightOnCommand;
    }
    public void setLightOffCommand(Command lightOffCommand) {
        this.lightOffCommand = lightOffCommand;
    }

    public void setCurrentCommand(Command currentCommand) {
        this.currentCommand = currentCommand;
    }
}
    客户端代码:
public class Main {
    public static void main(String[] args) {
        LightA lightA = new LightA();
        LightB lightB = new LightB();
        LightAOnCommand lightAOnCommand = new LightAOnCommand(lightA);
        LightAOffCommand lightAOffCommand = new LightAOffCommand(lightA);
        LightBOnCommand lightBOnCommand = new LightBOnCommand(lightB);
        LightBOffCommand lightBOffCommand = new LightBOffCommand(lightB);
        //刚开始用A厂的电灯
        Telecontroller telecontroller = new Telecontroller(lightAOnCommand,lightAOffCommand,new NoCommand());
        telecontroller.lightOn();//开灯
        telecontroller.lightOff();//关灯
        telecontroller.undo();//撤销,即撤销关灯操作,就是开启电灯
        System.out.println("========================================");
        //现在换成B厂的电灯
        telecontroller.setCurrentCommand(new NoCommand());
        telecontroller.setLightOnCommand(lightBOnCommand);
        telecontroller.setLightOffCommand(lightBOffCommand);
        telecontroller.lightOn();//开灯
        telecontroller.lightOff();//关灯
        telecontroller.undo();//撤销,即撤销关灯操作,就是开启电灯
    }
}
    运行结果:



    以上代码中,控制器和电灯实现了解耦,正如主函数中所看到的,命令对象对电灯包装,控制器只依赖命令对象,当控制器要完成开灯操作时,只管调用Command的execute方法即可,至于具体调用的是A还是B厂的电灯,控制器不关心,这就是解耦的表现,当要把A厂的电池全部换成B厂时,只用设置控制器的命令对象为B厂的命令对象即可。

命令对象其它用法

    如果一个命令对象中包含了一个命令对象数组,即在execute方法中可以调用很多其它命令对象的execute方法,那么就构成了一个宏命令,简单来说就是构建一个新的命令对象,它用来批量执行已有的命令对象的execute方法,即是对现有命令的一个组合。
    命令模式还被广泛用于队列,事物,日志等场景。

总结

    命令模式就是在调用者和提供者中间提供一个对提供者方法的封装,以解耦调用者和具体的提供者,适用于同一个功能有很多不同的实现的时候,比如上面的一个遥控器开灯,而灯又有不同厂家提供的情况。其实我觉得没有必要刻意去记这个模式,当你遇到遥控器开灯的情况时,你自然会想到要做一个中间层来屏蔽不同的具体提供者,这个中间层就是命令对象,它把原本不同的接口变得统一。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值