0
点赞
收藏
分享

微信扫一扫

JAVA设计模式之命令模式

命令模式的主要组成部分有以下几个:

  1. 命令接口(Command):定义了一个执行操作的接口,通常包含一个名为 execute() 的方法。
  2. 具体命令(ConcreteCommand):实现命令接口的具体类,包含一个接收者(Receiver)对象的引用,并在 execute() 方法中调用接收者的相应操作。
  3. 调用者(Invoker):负责调用命令对象的 execute() 方法。调用者并不需要了解命令是如何执行的,只需知道命令接口即可。
  4. 接收者(Receiver):负责执行与命令相关的具体操作。接收者对象通常是一个特定的业务对象。

命令模式的优点有:

  • 降低系统的耦合度,使得调用者和接收者之间的依赖关系更加清晰。
  • 可以对请求进行排队、记录日志、撤销和重做等操作。
  • 可以将复杂的业务逻辑分解为较小的、更易于管理的部分,提高代码的可维护性。

下面是一个简单的 Java 代码示例,展示了命令模式的使用:

// 命令接口
interface Command {
    void execute();
}

// 具体命令类
class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOn();
    }
}

// 接收者
class Light {
    public void turnOn() {
        System.out.println("Light is on");
    }

    public void turnOff() {
        System.out.println("Light is off");
    }
}

// 请求者
class RemoteControl {
    private Command command;

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

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

public class Main {
    public static void main(String[] args) {
        // 创建一个灯
        Light light = new Light();
        // 创建一个开灯命令
        Command lightOnCommand = new LightOnCommand(light);

        RemoteControl remoteControl = new RemoteControl();
        remoteControl.setCommand(lightOnCommand);
        remoteControl.pressButton();
    }
}

在这个示例中,创建了一个简单的家庭自动化系统,可以通过遥控器控制灯光的开关。Light 类是接收者,LightOnCommand 类是具体命令,RemoteControl 类是请求者。将命令对象设置到遥控器中,然后按下遥控器的按钮来执行命令。这样,遥控器可以控制灯光,而不需要知道如何实现灯光的开关操作。

使用命令模式有以下好处:

  1. 解耦:命令模式将请求发送者(Invoker)与请求接收者(Receiver)解耦,请求发送者不需要知道接收者的具体实现,只需知道如何发送请求。这样可以降低系统各部分之间的耦合度,使系统更易于维护和扩展。
  2. 可扩展性:新增命令时,只需实现一个新的具体命令类,而不需要修改请求发送者或请求接收者的代码。这使得系统更容易扩展,符合开闭原则。
  3. 灵活性:命令模式允许在运行时动态地更改命令。例如,在上面的示例中可以在运行时为遥控器设置不同的命令,从而控制不同的设备。
  4. 可以实现宏命令:命令模式可以方便地实现宏命令(Macro Command),即一组命令的组合。宏命令可以按顺序执行多个命令,甚至可以实现撤销和重做功能。
  5. 命令历史和撤销功能:命令模式可以用于记录已执行的命令,从而实现命令历史、撤销和重做等功能。例如,文本编辑器可以使用命令模式记录用户的编辑操作,以便在需要时撤销或重做这些操作。

总之,命令模式通过将操作封装为一个对象,实现了对操作的解耦、记录操作历史、撤销操作等功能。这使得系统更加灵活、可扩展和易于维护。

宏命令

下面是一个 Java 代码示例,展示了如何使用命令模式实现宏命令。在这个示例中扩展了之前的家庭自动化系统,使其可以同时控制灯光和空调。

// 命令接口
interface Command {
    void execute();
}

// 具体命令类:开灯
class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOn();
    }
}

// 具体命令类:关灯
class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOff();
    }
}

// 接收者:灯
class Light {
    public void turnOn() {
        System.out.println("Light is on");
    }

    public void turnOff() {
        System.out.println("Light is off");
    }
}

// 具体命令类:开空调
class AirConditionerOnCommand implements Command {
    private AirConditioner airConditioner;

    public AirConditionerOnCommand(AirConditioner airConditioner) {
        this.airConditioner = airConditioner;
    }

    public void execute() {
        airConditioner.turnOn();
    }
}

// 具体命令类:关空调
class AirConditionerOffCommand implements Command {
    private AirConditioner airConditioner;

    public AirConditionerOffCommand(AirConditioner airConditioner) {
        this.airConditioner = airConditioner;
    }

    public void execute() {
        airConditioner.turnOff();
    }
}

// 接收者:空调
class AirConditioner {
    public void turnOn() {
        System.out.println("Air conditioner is on");
    }

    public void turnOff() {
        System.out.println("Air conditioner is off");
    }
}

// 宏命令类
class MacroCommand implements Command {
    private List<Command> commands;

    public MacroCommand() {
        commands = new ArrayList<>();
    }

    public void addCommand(Command command) {
        commands.add(command);
    }

    public void execute() {
        for (Command command : commands) {
            command.execute();
        }
    }
}

// 请求者:遥控器
class RemoteControl {
    private Command command;

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

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

// 在 main 方法中添加关闭所有设备的宏命令
public class Main {
    public static void main(String[] args) {
        // ... 前面的代码省略

        // 创建宏命令:开启所有设备
        MacroCommand turnAllOnCommand = new MacroCommand();
        turnAllOnCommand.addCommand(lightOnCommand);
        turnAllOnCommand.addCommand(airConditionerOnCommand);

        // 创建宏命令:关闭所有设备
        MacroCommand turnAllOffCommand = new MacroCommand();
        turnAllOffCommand.addCommand(lightOffCommand);
        turnAllOffCommand.addCommand(airConditionerOffCommand);

        RemoteControl remoteControl = new RemoteControl();

        // 执行宏命令:开启所有设备
        System.out.println("Turn all devices on:");
        remoteControl.setCommand(turnAllOnCommand);
        remoteControl.pressButton();

        // 执行宏命令:关闭所有设备
        System.out.println("\nTurn all devices off:");
        remoteControl.setCommand(turnAllOffCommand);
        remoteControl.pressButton();
    }
}

在这个示例中创建了两个宏命令:turnAllOnCommand 和 turnAllOffCommandturnAllOnCommand 用于开启所有设备,而 turnAllOffCommand 用于关闭所有设备。通过为遥控器设置不同的宏命令,可以一键控制家中的多个设备。

这个示例展示了如何使用命令模式实现宏命令功能。通过将多个命令组合成一个宏命令,可以轻松地实现批量操作。此外,宏命令还可以与其他命令模式功能(例如撤销和重做)结合使用,实现更强大的功能。

历史命令和撤销

以下是一个使用命令模式实现命令历史和撤销功能的 Java 代码示例。在这个示例中创建了一个简单的文本编辑器,支持添加文本、删除文本、撤销和重做操作。

import java.util.Stack;

// 命令接口
interface Command {
    void execute();
    void undo();
}

// 具体命令类:添加文本
class AddTextCommand implements Command {
    private StringBuilder textEditor;
    private String text;

    public AddTextCommand(StringBuilder textEditor, String text) {
        this.textEditor = textEditor;
        this.text = text;
    }

    public void execute() {
        textEditor.append(text);
    }

    public void undo() {
        textEditor.delete(textEditor.length() - text.length(), textEditor.length());
    }
}

// 请求者:文本编辑器
class TextEditor {
    private StringBuilder text;
    private Stack<Command> commandHistory;

    public TextEditor() {
        text = new StringBuilder();
        commandHistory = new Stack<>();
    }

    public void addText(String newText) {
        Command command = new AddTextCommand(text, newText);
        command.execute();
        commandHistory.push(command);
    }

    public void undo() {
        if (!commandHistory.isEmpty()) {
            Command lastCommand = commandHistory.pop();
            lastCommand.undo();
        }
    }

    public void printContent() {
        System.out.println(text.toString());
    }
}

public class Main {
    public static void main(String[] args) {
        TextEditor textEditor = new TextEditor();

        // 添加文本
        textEditor.addText("Hello, ");
        textEditor.addText("world!");
        textEditor.printContent(); // 输出:Hello, world!

        // 撤销操作
        textEditor.undo();
        textEditor.printContent(); // 输出:Hello, 

        // 再次添加文本
        textEditor.addText("Command Pattern!");
        textEditor.printContent(); // 输出:Hello, Command Pattern!
    }
}

在这个示例中创建了一个简单的文本编辑器 TextEditor,它使用一个 StringBuilder 对象存储文本内容,并使用一个栈(Stack)存储命令历史。添加文本操作对应的具体命令类是 AddTextCommand,它实现了 execute() 和 undo() 方法。当执行 addText() 方法时,编辑器会创建一个 AddTextCommand 对象并执行它,然后将这个命令对象添加到命令历史栈中。当执行 undo() 方法时,编辑器会从命令历史栈中弹出上一个命令对象并执行它的 undo() 方法,从而撤销该命令。

这个示例展示了如何使用命令模式实现命令历史和撤销功能。通过维护一个命令历史栈,可以轻松地记录已执行的命令,并在需要时撤销这些命令。此外,这种方法还可以扩展为支持重做操作。

命令模式 VS 策略模式

此时可能会觉得,命令模式跟策略模式、工厂模式非常相似啊,那它们的区别在哪里呢?不仅如此,在留言区中我还看到有不止一个同学反映,感

命令模式和策略模式确实在某些方面相似,但它们的核心目的和应用场景是不同的。

  1. 目的:
  • 命令模式(Command Pattern)的主要目的是将操作封装成对象,使请求者与执行者之间的耦合度降低。这样可以实现命令的存储、传递、撤销等功能。
  • 策略模式(Strategy Pattern)的主要目的是定义一系列算法或策略,并将它们封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户端,使客户端可以在运行时选择不同的算法或策略。
  1. 应用场景:
  • 命令模式适用于需要对操作进行记录、撤销或重做的场景,或者需要将操作作为参数传递给其他对象的场景。例如,文本编辑器、数据库事务管理等场景。
  • 策略模式适用于需要在多个算法或策略之间进行选择的场景,或者需要将算法或策略独立于客户端的场景。例如,排序算法、压缩算法等场景。
  1. 实现方式:
  • 命令模式通常包括命令接口(Command)和具体命令类(ConcreteCommand),以及请求者(Invoker)和接收者(Receiver)。请求者负责调用命令,接收者负责执行命令。
  • 策略模式通常包括策略接口(Strategy)和具体策略类(ConcreteStrategy),以及使用策略的上下文类(Context)。上下文类通过策略接口调用具体策略的方法,从而实现算法的切换。

尽管命令模式和策略模式在结构上有一定的相似性,但它们解决的问题和应用场景是不同的。在实际项目中,根据需求和目标来选择合适的设计模式。

使用场景

命令模式在实际开发中有很多应用场景,以下是一些常见的案例:

  1. 撤销和重做操作:在文本编辑器、图形编辑器等软件中,用户可能希望撤销或重做之前的操作。通过使用命令模式,可以将每个操作封装为一个命令对象,然后将它们存储在一个历史记录列表中。这样,撤销操作就可以通过反向执行命令列表中的命令来实现,而重做操作则可以通过正向执行命令列表来实现。
  2. 菜单和工具栏按钮:在图形用户界面中,菜单项和工具栏按钮通常与特定的操作相关联。命令模式允许将操作封装为命令对象,这样菜单项和按钮就可以通过调用命令对象的 execute() 方法来执行相应的操作。这种方式使得菜单和工具栏与底层的操作逻辑解耦,提高了系统的可扩展性和可维护性。
  3. 任务队列和后台任务:命令模式可以用于实现任务队列。例如,在一个多线程应用程序中,可以将不同的任务封装为命令对象,然后将它们添加到一个任务队列中。后台线程可以从队列中获取命令对象,并执行它们。这样可以实现对任务的并行处理和优先级控制。
  4. 宏(Macro):在一些软件中,用户可以创建宏来执行一系列操作。命令模式可以用于实现这种功能。每个操作都可以封装为一个命令对象,然后将这些命令对象组合成一个宏命令。用户可以通过执行宏命令来一次性执行所有操作。
  5. 电商系统中的订单处理:在电商系统中,可以将不同的订单操作(例如创建订单、取消订单、支付订单等)封装为命令对象。这样,处理这些操作的代码可以与具体的订单对象解耦,使得系统更加灵活和可维护。
  6. 智能家居系统:在智能家居系统中,各种家电设备(如灯光、空调、电视等)的操作可以被封装为命令对象。通过使用命令模式,用户可以通过一个统一的接口(例如手机App或语音助手)来控制不同的设备,而无需关心设备的具体实现。
  7. 游戏开发:在游戏开发中,玩家的操作(如移动角色、使用技能等)可以被封装为命令对象。这样可以将玩家操作与游戏逻辑解耦,实现更加灵活的游戏控制和操作记录。
  8. 状态模式与命令模式结合:在某些场景下,可以将状态模式与命令模式结合使用。例如,一个文档编辑器可能有多种编辑模式(如插入模式、选择模式等),每种模式对应不同的操作集合。可以将这些操作封装为命令对象,并根据当前的编辑模式来选择执行哪个命令。

这些案例展示了命令模式在实际应用中的广泛应用和灵活性。通过使用命令模式,可以实现更加松耦合、可扩展和可维护的系统。

JAVA设计模式之命令模式_重做

举报

相关推荐

0 条评论