Skip to main content
 首页 » 编程设计

java实现命令模式

2022年07月19日128freeliver54

java实现命令模式

命令模式属于行为设计模式,是Gof设计模式正式列表之一。简言之,命令模式在一个对象中封装执行给定动作的所有数据,包括要执行方法,方法参数以及方法所属对象。通过该模式可以把消费者和命令对象生产者进行解耦,因此也称为生产者-消费者模式。

本文我们学习在java如何实现命令模式,分别通过面向对象和面向函数两种方法,并了解哪些用例可能有用。

面向对象实现

经典实现中,命令模式需要实现四个组件:命令(Command)、接收者(Receiver)、执行器(Invoker)以及客户端(Client)。为了更好理解模式如何工作以及每个组件对应角色,我们通过具体示例进行说明。

假设我们想开发一个文本文件应用程序,需要实现所有执行文本文件相关操作功能,如打开、写、保存等。

所以我们需要把应用分解为上述四个组件。

命令类

命令对象作用是存储执行动作所需的的所有信息,包括要执行的方法、方法参数以及实现方法的对象(接收者)。为了更准确理解命令对象,我们开始开发简单命令接口及其两个实现:

@FunctionalInterface 
public interface TextFileOperation { 
    String execute(); 
} 
 
public class OpenTextFileOperation implements TextFileOperation { 
 
    private final TextFile textFile; 
     
    public OpenTextFileOperation(TextFile textFile) { 
        this.textFile = textFile; 
    } 
     
    @Override 
    public String execute() { 
        return textFile.open(); 
    } 
} 
public class SaveTextFileOperation implements TextFileOperation { 
     
    private final TextFile textFile; 
     
    public SaveTextFileOperation(TextFile textFile) { 
        this.textFile = textFile; 
    } 
     
    @Override 
    public String execute() { 
        return textFile.save(); 
    } 
} 

TextFileOperation接口定义了命令对象API,两个实现OpenTextFileOperation 和 SaveTextFileOperation执行具体动作。前者打开文本文件,后者保存文件文件。
命令对象功能很清晰:TextFileOperation命令封装打开和保存文本文件的所有信息,包括接收对象和要调用的方法以及方法参数(本实例没有参数,但实际场景中有可能)。

值得强调的是,执行文件操作的组件是接收者(TextFile实例)。

接收者类

接收者是执行一组内聚操作的对象,是执行实际动作的组件,通过调用命令的excute方法。因此我们定义接收类,是对文本文件对象的建模:

public class TextFile { 
     
    private final String name; 
     
    public TextFile(String name) { 
        this.name = name; 
    } 
     
    public String open() { 
        return "Opening file " + name; 
    } 
     
    public String read() {   
        return "Reading file " + name; 
    } 
     
    public String write() {   
        return "Writing to file " + name; 
    } 
     
    public String save() {   
        return "Saving file " + name; 
    } 
     
    public String copy() {   
        return "Copying file " + name; 
    } 
     
    public String paste() {   
        return "Pasting file " + name; 
    } 
} 

执行类

执行对象知道如何执行给定命令但不知道命令如何被执行,其仅知道命令接口。在一些场景中,执行对象除了执行,也存储并查询命令,主要为了实现一些额外特性,如录制宏、撤销重做功能。

在我们示例中,很明显必须有一个额外组件负责调用命令对象并通过命令的execute()方法执行它们。这正是执行程序类负责的角色。

请看执行类基本的实现:

public class TextFileOperationExecutor { 
     
    private final List<TextFileOperation> textFileOperations = new ArrayList<>(); 
     
    public String executeOperation(TextFileOperation textFileOperation) { 
        textFileOperations.add(textFileOperation); 
        return textFileOperation.execute(); 
    } 
} 

TextFileOperationExecutor可是抽象层,对命令对象和消费者进行解耦并调用封装在TextFileOperation 命令对象的方法。
同时也存储命令对象至List中,当然这不是命令模式实现必须的,仅仅为了执行过程中增加一些额外的控制。

客户端类

客户端对象是控制命令执行过程,通过指定什么命令要执行,在那个阶段处理执行。因此,为了遵循模式的正式定义,我们定义客户端类,使用main方法:

public static void main(String[] args) { 
    TextFileOperationExecutor textFileOperationExecutor  = new TextFileOperationExecutor(); 
    textFileOperationExecutor.executeOperation( new OpenTextFileOperation(new TextFile("file1.txt")))); 
    textFileOperationExecutor.executeOperation( new SaveTextFileOperation(new TextFile("file2.txt")))); 
} 

面向函数实现

到目前为止,我们使用面向对象方法实现命令模式,这非常好。从java8开始,我们可以使用面向函数方法,基于lambda表达式和方法引用,是代码更小、更简洁,避免模版代码。

使用Lambda 表达式

TextFileOperation 接口是函数式接口,我们可以给执行类以lambda形式传递命令对象,则无需显示地创建TextFileOperation 实例:

TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor(); 
textFileOperationExecutor.executeOperation(() -> "Opening file file1.txt"); 
textFileOperationExecutor.executeOperation(() -> "Saving file file1.txt"); 

现在实现看起来更加精简和简洁,因为我们减少了样板代码的数量。即便如此,问题仍然存在:与面向对象的方法相比,这种方法更好吗? 没有绝对的答案,如果假设在大多数情况下,更紧凑的代码意味着更好的代码,那么事实确实如此。根据经验,我们应该根据每个情况确定何时使用lambda表达式。

使用方法引用

类似的,我们也可以使用方法引用给执行器传递命令对象:

TextFileOperationExecutor textFileOperationExecutor 
 = new TextFileOperationExecutor(); 
TextFile textFile = new TextFile("file1.txt"); 
textFileOperationExecutor.executeOperation(textFile::open); 
textFileOperationExecutor.executeOperation(textFile::save); 

在本例中,实现比使用lambdas的实现稍微复杂点,因为仍然需要创建TextFile实例。

总结

本文我们学习了命令模式的关键概念,以及如何在java通过面向对象和面向函数进行实现。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/88372541
阅读延展