设计模式第8篇:命令模式

命令模式

本文翻译自:https://www.journaldev.com/1624/command-design-pattern

命令者模式下,请求会被发送到调用者,然后调用者将其传递给封装命令模式的对象;命令模式对象再调用相应的方法来执行指定的动作;
客户端程序创建接收者然后将其添加到命令当中,然后继续创建调用者并引用命令者来执行一个动作;当客户端程序执行动作时,它要依靠调用者和命令者。

Command Design Pattern Example

我们使用一个真实的生活场景来印证命令者模式,假如我们想要提供一个文件系统,它带有实用的打开、读写、关闭文件的功能;这个文件系统支持多种操作,比如Windows、Unix等。
为了集成这样一个系统,第一件要做的事情就是创建一个接收文件的类,并且做最实际的工作,因此我们需要面向接口编程,我们创建一个FileSystemReceiver接口,这个接口将会被不同的操作系统类所实现,如Windows, Unix, Solaris 等;

Command Pattern Receiver Classes

package com.journaldev.design.command;

public interface FileSystemReceiver {

    void openFile();
    void writeFile();
    void closeFile();
}

FileSystemReceiver接口定义了一些抽象方法,为简单起见,我准备创建两个接收者,UnixFileSystemReceiverUnixFileSystemReceiver ,它们将运用于Unix和Windows系统;

package com.journaldev.design.command;

public class UnixFileSystemReceiver implements FileSystemReceiver {

    @Override
    public void openFile() {
        System.out.println("Opening file in unix OS");
    }

    @Override
    public void writeFile() {
        System.out.println("Writing file in unix OS");
    }

    @Override
    public void closeFile() {
        System.out.println("Closing file in unix OS");
    }
}
package com.journaldev.design.command;

public class WindowsFileSystemReceiver implements FileSystemReceiver {

    @Override
    public void openFile() {
        System.out.println("Opening file in Windows OS");
        
    }

    @Override
    public void writeFile() {
        System.out.println("Writing file in Windows OS");
    }

    @Override
    public void closeFile() {
        System.out.println("Closing file in Windows OS");
    }

}

你注意到了Override注释吗?如果你想知道为何用这个注释,请参考 java annotations 以及 override annotation benefits;译者注:一个较大的好处,就是父类的方法改动,会使得子类报警戒线,从而引起你的注意;现在接收者已经准备好了,我们可以创建命令者了;

Command Pattern Interface and Implementations

我们可以实现接口或者抽象类来创建基础命令者,这是一个设计决定,同时取决于你的需求。

package com.journaldev.design.command;

public interface Command {

    void execute();
}

现在我们需要创建不同类型的实现者,由于我们有三个动作,openFile、writeFile、closeFile;每个命令者实现类会将请求派发到指定的接收者,因此将创建三个命令者。

package com.journaldev.design.command;

public class OpenFileCommand implements Command {

    private FileSystemReceiver fileSystem;
    
    public OpenFileCommand(FileSystemReceiver fs){
        this.fileSystem=fs;
    }
    @Override
    public void execute() {
        //open command is forwarding request to openFile method
        this.fileSystem.openFile();
    }

}
package com.journaldev.design.command;

public class CloseFileCommand implements Command {

    private FileSystemReceiver fileSystem;
    
    public CloseFileCommand(FileSystemReceiver fs){
        this.fileSystem=fs;
    }
    @Override
    public void execute() {
        this.fileSystem.closeFile();
    }

}
package com.journaldev.design.command;

public class WriteFileCommand implements Command {

    private FileSystemReceiver fileSystem;
    
    public WriteFileCommand(FileSystemReceiver fs){
        this.fileSystem=fs;
    }
    @Override
    public void execute() {
        this.fileSystem.writeFile();
    }

}

现在接收者和命令者都准备完毕,因此我们接下来将实现调用者。

Command Pattern Invoker Class

调用者是一个简单的类,它封装了命令者并且将请求传递给命令者然后执行。

package com.journaldev.design.command;

public class FileInvoker {

    public Command command;
    
    public FileInvoker(Command c){
        this.command=c;
    }
    
    public void execute(){
        this.command.execute();
    }
}

我们的文件系统现在准备好了已经,我们可以写一个小的测试客户端来测试一下,但在那之前我要创建一个具有效用的方法。由于我们使用了系统类(System class)来获取操作系统的信息,所以我们使用工厂模式返回合适的类。

package com.journaldev.design.command;

public class FileSystemReceiverUtil {
    
    public static FileSystemReceiver getUnderlyingFileSystem(){
         String osName = System.getProperty("os.name");
         System.out.println("Underlying OS is:"+osName);
         if(osName.contains("Windows")){
             return new WindowsFileSystemReceiver();
         }else{
             return new UnixFileSystemReceiver();
         }
    }
    
}

现在让我们立刻来测试命令者模式:

package com.journaldev.design.command;

public class FileSystemClient {

    public static void main(String[] args) {
        //Creating the receiver object
        FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
        
        //creating command and associating with receiver
        OpenFileCommand openFileCommand = new OpenFileCommand(fs);
        
        //Creating invoker and associating with Command
        FileInvoker file = new FileInvoker(openFileCommand);
        
        //perform action on invoker object
        file.execute();
        
        WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
        file = new FileInvoker(writeFileCommand);
        file.execute();
        
        CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
        file = new FileInvoker(closeFileCommand);
        file.execute();
    }

}

注意到了吗?FileSystemClient负责创建合适的命令类型,比如你想写文件,那么就不应该创建CloseFileCommand类;FileSystemClient同时也负责将接收者以参数的形式添加到命令者当中,然后调用者再调用命令者执行相应的程序。

输出为:

Underlying OS is:Mac OS X
Opening file in unix OS
Writing file in unix OS
Closing file in unix OS

Command Pattern Important Points

  • 命令者是命令者模式的核心,它为它的实现类定义了一些条约。
  • 接收者和命令者之间是分离的。
  • Command implementation classes chose the method to invoke on receiver object, for every method in receiver there will be a command implementation. It works as a bridge between receiver and action methods.
  • 调用者只是负责将请求传递给命令者。
  • 客户端负责实例化命令者和接收者然后让他们联系起来。
  • 客户端也负责实例化调用者;
  • 命令者模式的就有高拓展性,我们可以通过再接收者中添加方法,然后创建新的命令者;这个过程不需要更改其他地方的代码;
  • 命令模式的缺点就是代码冗长,调用关系巨多,容易让人产生困惑。

Command Design Pattern JDK Example

Runnable 接口运用了此设计模式;

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

推荐阅读更多精彩内容