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

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:以上部分内容来自互联网,侵删。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容