命令设计模式

命令模式是行为设计模式之一。命令设计模式用于在请求-响应模型中实现松耦合

命令模式

在命令模式中,请求被发送到调用者,调用者将其传递给封装的命令对象。命令对象将请求传递给接收者的适当方法来执行特定操作。客户程序创建接收者对象,然后将其附加到命令上。然后创建调用者对象并附加命令对象以执行操作。现在,当客户程序执行动作时,根据命令和接收者对象进行处理。

命令设计模式示例

我们将看一个实际的场景,我们可以在其中实现命令模式。假设我们想要提供一个文件系统实用程序,其中包含打开、写入和关闭文件的方法。这个文件系统实用程序应该支持多个操作系统,如Windows和Unix。为了实现我们的文件系统实用程序,首先我们需要创建接收者类,这些类将实际执行所有工作。由于我们使用Java中的接口编码,我们可以拥有FileSystemReceiver接口及其针对不同操作系统版本(如Windows、Unix、Solaris等)的实现类。

命令模式接收者类

package com.journaldev.design.command;

public interface FileSystemReceiver {

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

FileSystemReceiver接口定义了实现类的合同。为了简单起见,我创建了两种不同的接收者类来与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注释覆盖注释的好处。现在我们的接收者类已经准备就绪,我们可以开始实现我们的命令类。

命令模式接口和实现

我们可以使用接口或抽象类来创建我们的基础命令,这是一个设计决策,取决于您的需求。我们选择使用接口,因为我们没有任何默认实现。

package com.journaldev.design.command;

public interface Command {

	void execute();
}

现在,我们需要为接收器执行的所有不同类型的操作创建实现。由于我们有三个操作,我们将创建三个命令实现。每个命令实现将将请求转发到接收器的适当方法。

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命令正在将请求转发到openFile方法
		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();
	}

}

现在我们有了接收器和命令实现,所以我们可以开始实现调用者类。

命令模式调用者类

调用者是一个简单的类,封装了命令并将请求传递给命令对象来处理。

package com.journaldev.design.command;

public class FileInvoker {

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

我们的文件系统实用程序实现已经就绪,我们可以开始编写一个简单的命令模式客户端程序。但在此之前,我将提供一个实用方法来创建适当的FileSystemReceiver对象。由于我们可以使用System类来获取操作系统信息,所以我们将使用这个,否则我们可以使用工厂模式根据输入返回适当的类型。

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) {
		//创建接收器对象
		FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
		
		//创建命令并与接收器关联
		OpenFileCommand openFileCommand = new OpenFileCommand(fs);
		
		//创建调用者并与命令关联
		FileInvoker file = new FileInvoker(openFileCommand);
		
		//在调用者对象上执行操作
		file.execute();
		
		WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
		file = new FileInvoker(writeFileCommand);
		file.execute();
		
		CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
		file = new FileInvoker(closeFileCommand);
		file.execute();
	}

}

注意,客户端负责创建适当类型的命令对象。例如,如果要编写文件,则不应创建CloseFileCommand对象。客户端程序还负责将接收器附加到命令,然后将命令附加到调用者类。上述命令模式示例程序的输出是:

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

命令模式类图

以下是我们文件系统实用程序实现的类图。

命令模式的重要点

  • 命令是命令设计模式的核心,它定义了实现的契约。
  • 接收器实现与命令实现分离。
  • 命令实现类选择要在接收器对象上调用的方法,对于接收器中的每个方法,都会有一个命令实现。它作为接收器和动作方法之间的桥梁。
  • 调用者类只是将请求从客户端转发到命令对象。
  • 客户端负责实例化适当的命令和接收器实现,然后将它们关联在一起。
  • 客户端还负责实例化调用者对象,并将命令对象与其关联,并执行动作方法。
  • 命令设计模式易于扩展,我们可以在接收器中添加新的动作方法,并创建新的命令实现,而无需更改客户端代码。
  • 命令设计模式的缺点是,随着动作方法的数量增加,代码变得庞大和混乱,并且因为有太多的关联。

命令设计模式 JDK 示例

Runnable 接口(java.lang.Runnable)和 Swing Action(javax.swing.Action)使用命令模式。

Source:
https://www.digitalocean.com/community/tutorials/command-design-pattern