zl程序教程

您现在的位置是:首页 >  后端

当前栏目

掌握Java命令模式:将请求封装成对象,解耦发送者和接收者的关系

JAVA封装对象模式命令 掌握 请求 关系
2023-09-14 09:14:15 时间

在这里插入图片描述

一、简介

1.1 介绍命令模式

命令模式是一种行为设计模式,它将请求封装成对象,以便可以使用不同的请求、队列或日志来参数化其他对象。命令模式允许请求的发出者和接收者之间解耦,让不同的对象之间更加灵活和独立地交互。

使用命令模式,我们可以将请求与发送请求的对象解耦。具体来说,我们可以创建一个命令接口,该接口要求执行某些操作的所有具体命令对象实现该接口。然后,我们可以使用具体的命令对象来代表不同的请求,并将其传递给其他对象执行。

1.2 它的优点和使用场景

命令模式的优点包括:

  1. 解耦发送者和接收者:通过命令对象将请求发送给接收者,可以避免发送者和接收者之间的直接耦合。
  2. 容易增加新命令:每个具体命令就像是独立的一部分,因此很容易添加新的具体命令,从而扩展系统功能。
  3. 支持撤销操作:通过保存历史命令记录,命令模式可以支持撤销操作。

命令模式的使用场景包括:

  1. 需要将请求和接收者解耦的场景。
  2. 需要支持批处理、撤销和重做等操作的场景。
  3. 需要生成日志或记录请求历史的场景。

二、命令模式的工作原理

2.1 定义命令模式

命令模式是一种对象行为型模式,它的作用是将请求封装为一个对象,从而使我们可以用不同的请求来对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销和恢复的操作。

2.2 命令模式的角色

  1. 抽象命令(Command):定义命令的接口,包含执行命令的execute()方法。
  2. 具体命令(ConcreteCommand):实现抽象命令接口,通过调用接收者来执行具体的操作。
  3. 接收者(Receiver):执行具体操作的对象。
  4. 调用者(Invoker):负责调用命令对象并执行请求。
  5. 客户端(Client):创建命令对象,并将命令对象交给调用者进行调用执行。

2.3 命令模式的执行流程

  1. 客户端创建具体命令对象,指定其接收者。
  2. 创建调用者对象,并将具体命令对象初始化到调用者中。
  3. 调用者的调用方法会执行具体命令对象的execute()方法。
  4. 具体命令对象调用接收者的相关方法来执行具体操作。

2.4 命令模式的关键要素

  1. 命令对象将请求封装成对象,解除请求发送者和接收者之间的紧密耦合关系。
  2. 命令对象包含了接收者对象的一个引用,避免了发送者和接收者之间的直接关系。
  3. 接收者对象知道如何实现真正的操作。
  4. 调用者对象判断执行哪个具体的命令对象来进行操作,提供了一种灵活的场景。

三、实现Java命令模式

3.1 创建命令接口

首先,我们需要定义一个命令接口(Command),该接口包含一个执行命令的execute()方法,该方法实现了具体命令的操作。

public interface Command {
    void execute();
}

3.2 实现具体命令类

接下来,我们需要实现具体命令类(ConcreteCommand),该类实现命令接口,并负责调用接收者对象执行具体操作。

public class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.action();
    }
}

3.3 创建请求者类Invoker

然后创建调用者对象(Invoker),该对象负责调用具体命令对象,并执行相应的请求。

public class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        command.execute();
    }
}

3.4 创建接收者类Receiver

接收者对象(Receiver)包含了实际执行操作的方法。

public class Receiver {
    public void action() {
        System.out.println("Receiver: Performing action");
    }
}

3.5 创建客户端类Client

需要创建一个客户端类(Client),它负责创建具体命令对象、接收者对象和调用者对象,并将它们协调起来执行命令。

public class Client {
    public static void main(String args[]) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.executeCommand();
    }
}

四、命令模式扩展

4.1 反悔命令 Undo Command

反悔命令是一种特殊的命令,它允许用户撤销最近执行的操作。为了实现反悔功能,我们需要在每个具体命令类中实现一个反悔方法,该方法应该能够撤销相应的命令操作。一种常见的实现方式是使用一个栈来维护所有执行的命令对象,当用户请求撤销操作时,我们可以从栈中弹出最后一个对象并调用它的撤销方法。

4.2 宏命令 Macro Command

宏命令是一种将多个命令组合在一起的命令模式。由于宏命令本身也是一种命令对象,因此它可以被其他命令对象传递和执行。一个宏命令中包含了多个具体命令对象,并负责协调它们的执行。例如,当用户单击一个复杂的按钮时,可以将多个具体命令对象组合在一起形成一个宏命令,并将宏命令对象传递给调用者进行执行。

4.3 多个接收者的命令 Multiple Receivers Command

在标准的命令模式中,一个具体命令对象只能绑定一个接收者对象,因此无法直接将命令发送给多个接收者对象。如果想要实现一个将命令发送给多个接收者的功能,可以使用一种称为“回调”的方法。回调是一种程序设计技术,它允许我们将一段代码传递给另一个对象,并将该代码作为参数在接收方触发。在命令模式中,我们可以实现一个多重命令接口(MultipleCommand),该接口中包含了回调方法,具体命令对象实现该接口,并在回调方法中调用接收者对象执行相应的操作。这样,命令对象就可以将命令发送到多个接收者对象中了。

五、在Java项目中使用命令模式

5.1 例子1:使用命令模式实现日志记录

  1. 首先定义命令接口,并为每个具体命令对象实现日志记录的方法:
public interface Command {
    void execute();
    void log();
}

public class LoginCommand implements Command {
    private LoginReceiver receiver;
    
    public LoginCommand(LoginReceiver receiver) {
        this.receiver = receiver;
    }
    
    public void execute() {
        receiver.login();
    }
    
    public void log() {
        System.out.println("LoginCommand: Logged user login");
    }
}

public class LogoutCommand implements Command {
    private LogoutReceiver receiver;
    
    public LogoutCommand(LogoutReceiver receiver) {
        this.receiver = receiver;
    }
    
    public void execute() {
        receiver.logout();
    }
    
    public void log() {
        System.out.println("LogoutCommand: Logged user logout");
    }
}

  1. 然后创建接收者对象,并实现具体操作:
public interface LoginReceiver {
    void login();
}

public class UserLoginReceiver implements LoginReceiver {
    public void login() {
        System.out.println("UserLoginReceiver: User logged in");
    }
}

public interface LogoutReceiver {
    void logout();
}

public class UserLogoutReceiver implements LogoutReceiver {
    public void logout() {
        System.out.println("UserLogoutReceiver: User logged out");
    }
}

  1. 创建一个调用者对象,负责执行具体的命令并记录日志:
public class CommandInvoker {
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void execute() {
        command.execute();
        command.log();
    }
}

  1. 在客户端代码中,创建具体命令对象和相关的接收者,并将它们传递给调用者执行:
public static void main(String[] args) {
    LoginReceiver loginReceiver = new UserLoginReceiver();
    Command loginCommand = new LoginCommand(loginReceiver);
    
    LogoutReceiver logoutReceiver = new UserLogoutReceiver();
    Command logoutCommand = new LogoutCommand(logoutReceiver);
    
    CommandInvoker invoker = new CommandInvoker();
    
    // Execute login command
    invoker.setCommand(loginCommand);
    invoker.execute();
    
    // Execute logout command
    invoker.setCommand(logoutCommand);
    invoker.execute();
}
  1. 运行程序,观察控制台输出:
UserLoginReceiver: User logged in
LoginCommand: Logged user login
UserLogoutReceiver: User logged out
LogoutCommand: Logged user logout

可以看到,通过使用命令模式,我们可以将具体操作和日志记录分离开来,避免了收到操作代码的干扰,同时也更方便地记录了操作历史。