设计模式第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 接口运用了此设计模式;

你可能感兴趣的:(设计模式第8篇:命令模式)