命令模式
本文翻译自: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
接口定义了一些抽象方法,为简单起见,我准备创建两个接收者,UnixFileSystemReceiver
和UnixFileSystemReceiver
,它们将运用于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 接口运用了此设计模式;