美文网首页
设计模式之命令模式 -- 基础篇

设计模式之命令模式 -- 基础篇

作者: 天若有情天亦老 | 来源:发表于2017-12-22 15:00 被阅读43次

0x01前言


在阎宏博士(是谁?我也不知道,反正知道是大佬就行了)的《JAVA与模式》一书中开头是这样描述命令(Command)模式的:
  命令模式属于对象的行为模式。命令模式又称为行动(Action)模式或交易(Transaction)模式。(这个..哲♂学模式,emmmm)
  命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

0x02 命令模式的组成


Command
定义命令的接口,声明执行的方法。

ConcreteCommand
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

Receiver
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

Invoker
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

Client
创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。

0x03 代码结构

// 抽象命令角色接口
public interface Command {
    /**
     * 执行方法
     */
    void execute(); 
}
// 具体命令角色类
public class ConcreteCommand implements Command{
    // 持有相应的接受对象
    private Receiver receiver = null ;
    // 构造方法
    public ConcreteCommand(Receiver receiver) {
        if (receiver != null ) {
            this.receiver = receiver;
        }
    }
    @Override
    public void execute() {
        // 转调接收者对象的相应方法,让接收者来执行真正的功能。
        receiver.action();
    }   
}
// 接收者角色类
public class Receiver {
    /**
     * 真正执行命令相应的操作
     */
    public void action(){
        System.out.println("执行操作");
    }
}
// 请求者
public class Invoke {
    /**
     * 持有命令对象
     */
    private Command command = null ;
    
    public Invoke(Command command) {
        if (command != null) {
            this.command = command;
        }
    }
    /**
     * 行动方法
     */
    public void action(){
        command.execute();
    }
}
// 客户端
public class Client {
    
    public static void main(String[] args) {
        // 创建接收者
        Receiver receiver = new Receiver() ;
        
        // 创建命令对象,设定它的接收者
        Command concreteCommand = new ConcreteCommand(receiver);
        
        // 创建请求者
        Invoke invoke = new Invoke(concreteCommand);
        
        // 执行操作
        invoke.action();
    }
}

0x04 举个实栗

上边是命令模式的一个简单的实现,现在我们需要举个简单的栗子看看命令模式的具体应用,电视机想必大家都见过,以电视机的开关命令为栗。

// 抽象命令类
public interface Command {
    // 执行方法
    void execute();
}
// 接收者角色类
public class TV {
    // 打开方法
    public void turnON(){
        System.out.println("电视打开!");
    }
    // 关闭方法
    public void turnOFF() {
        System.out.println("电视关闭!");
    }
}
// 具体命令角色类 --关闭命令
public class TvOFFCommand implements Command{
    private TV tv ;
    public TvOFFCommand(TV tv) {
        this.tv = tv ;
    }
    @Override
    public void execute() {
        tv.turnOFF();
    }
}
// 具体命令角色类 --打开命令
public class TvONCommand implements Command{
    private TV tv ;
    public TvONCommand(TV tv) {
        this.tv = tv ;
    }
    @Override
    public void execute() {
        tv.turnON();
    }
}
// 请求者 -- 遥控器
public class RemoteInvote {
    private Command ON, OFF;
    public RemoteInvote(Command ON,Command OFF) {
        this.ON = ON ;
        this.OFF = OFF ;
    }
    public void turnON() {
        ON.execute();
    }
    public void turnOFF() {
        OFF.execute();
    }
}
// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建接收者
        TV receiver = new TV();
        // 创建命令对象 设定接收者
        Command onCommand = new TvONCommand(receiver);
        Command offCommand = new TvOFFCommand(receiver);
        // 请求者 --创建遥控器
        RemoteInvote invote = new RemoteInvote(onCommand, offCommand);
        // 打开电视
        invote.turnON();
        // 关闭电视
        invote.turnOFF();
    }
}

结果

电视打开!
电视关闭!

0x05 关于例子的补充说明:


虽然代码看似挺多,但其实命令模式的结构还是比较清晰的,总的来说命令模式的使用流程就是首先创建一个抽象命令,然后创建多个具体命令实现抽象命令接口,然后创建一个命令接收者角色,它包含各种的行为的具体实现,然后再有一个命令调用者角色,提供给客户端,用于接收客户端的参数;

0x06 命令模式的扩展


由于命令模式篇(wo)幅(bu)太(xiang)长(xie),这里单独拆分出去:

  • 命令模式扩展篇 - 宏命令:时间不够,栗子未定,代码暂无
  • 命令模式扩展篇 - 撤销命令:时间不够,栗子未定,代码暂无
  • 命令模式扩展篇 - 命令队列: 时间不够,栗子未定,代码暂无
  • 命令模式扩展篇 - 请求日志:时间不够,栗子未定,代码暂无
敬请期待~

0x07 优缺点


  • 优点
1.命令模式将行为调用者和各种行为分隔开,降低程序的耦合,便于程序扩展;
2.命令模式将行为的具体实现封装起来,客户端无需关心行为的具体实现;
3.命令模式可为多种行为提供统一的调用入口,便于程序对行为的管理和控制;
  • 缺点
1.使用命令模式的话,不用管命令多简单,都需要写一个命令类来封装,使用命令模式可能会导致系统有过多的具体命令类;(上面的例子就可以看出,每个命令都要有一个具体的命令类);

0x08 应用场景

1.希望将行为请求者和行为实现者解耦,不直接打交道;
2.希望分离掉行为请求者一部分的责任,行为请求者只需要将命令发给调用者,不再主动的去让行为实现者产生行为,符合单一职责原则;
3.希望可以控制执行的命令列表,方便记录,撤销/重做以及事务等功能;
4.期待可以将请求排队,有序执行;
5.希望可以将请求组合使用,即支持宏命令;

0x09 总结

命令模式最大的好处就是实现了行为请求者与行为实现者的解耦;
  在实际场景中的使用:
      Struts2中action中的调用过程中存在命令模式;
      数据库中的事务机制的底层实现;
      命令的撤销和恢复:增加相应的撤销和恢复命令的方法(比如数据库中的事务回滚);
例如:Java的Runnable就是命令模式的变形应用:

public class Test {
    public static void main(String[] args) {
        //范例
        Runnable runnable = () -> System.out.println("具体命令"); // Command cmd = ConcreteCommand
        Thread thread1 = new Thread(runnable); // 将 cmd 交给 Thread (Invoker)
        thread1.start(); // Invoker 调用 cmd 的执行方法
    }
} 

Tips:以上部分内容来自互联网,侵删。

相关文章

网友评论

      本文标题:设计模式之命令模式 -- 基础篇

      本文链接:https://www.haomeiwen.com/subject/ccbawxtx.html