设计模式——13. 命令模式

1. 说明

命令模式(Command Pattern)是一种行为型设计模式,它将请求或操作封装成一个对象,从而允许您将客户端和接收者对象解耦。这种模式的主要目的是将请求发送者与接收者解耦,从而允许多种不同的请求来操作接收者对象,同时支持命令的撤销和重做。

以下是命令模式中涉及的关键角色:

  1. 命令(Command):该角色定义了一个抽象接口,通常包括一个执行方法(execute),用于封装具体的操作或请求。具体的命令类实现这个接口,并包含了执行特定操作所需的信息。
  2. 具体命令(Concrete Command):这些类实现了命令接口,它们包含了特定操作的实际实现,负责调用接收者的方法来完成请求。
  3. 接收者(Receiver):接收者是真正执行操作的对象,它包含了实际的业务逻辑。命令将请求传递给接收者来执行实际操作。
  4. 调用者(Invoker):调用者是客户端代码或其他对象,它负责创建命令对象并将其发送给接收者来执行请求。调用者不需要知道具体的操作细节,只需知道如何将请求发送给命令对象即可。
  5. 客户端(Client):客户端是应用程序的一部分,它创建命令对象并将其发送给调用者来执行请求。

命令模式的主要优点包括:

  • 解耦:它允许调用者和接收者之间的松耦合,因为调用者不需要知道接收者的具体实现。
  • 可扩展性:您可以轻松地添加新的命令类,无需更改现有的代码。
  • 支持撤销和重做:命令模式支持对执行过的命令进行撤销和重做,这对于实现撤销和重做功能非常有用。

2. 使用的场景

命令模式适用于许多不同的场景,特别是在需要将请求发送者与接收者解耦的情况下。以下是一些命令模式的常见使用场景:

  1. 菜单项和按钮操作:在图形用户界面中,菜单项、按钮和工具栏按钮通常需要执行特定的操作,命令模式可以将这些操作封装成命令对象,从而使用户界面组件与操作解耦。
  2. 遥控器:遥控器通常包含多个按钮,每个按钮对应一个设备或操作。命令模式可用于将每个按钮的操作封装成命令对象,从而实现遥控器的功能。
  3. 事务管理:在数据库系统中,可以使用命令模式来实现事务管理。每个数据库操作(插入、更新、删除等)可以封装成一个命令对象,并按照顺序执行,以实现事务的原子性。
  4. 日程安排和定时任务:命令模式可以用于实现日程安排和定时任务的功能。每个任务可以被封装成命令对象,并根据时间表执行。
  5. 多级撤销和重做功能:命令模式使得实现多级撤销和重做功能变得简单,因为每个命令对象都可以记录其执行前后的状态。
  6. 队列请求处理:命令模式可以用于构建请求队列,例如打印队列或任务队列。每个请求被封装成命令对象,并按照队列顺序执行。
  7. 模拟系统:在模拟和游戏开发中,命令模式可用于控制虚拟角色的行为,使其更加灵活和可扩展。
  8. 日志记录和审计:命令模式可以用于记录系统中的操作,以便后续的审计和调试。

总之,任何需要将请求或操作封装成对象,并支持撤销、重做、队列化、延迟执行等功能的情况都可以考虑使用命令模式。

3. 应用例子

以下是一个使用 Python 实现的简单命令模式示例,该示例模拟了遥控器控制电视的操作:

# 命令接口
class Command:
    def execute(self):
        pass

# 具体命令:打开电视
class TurnOnTVCommand(Command):
    def __init__(self, tv):
        self.tv = tv

    def execute(self):
        self.tv.turn_on()

# 具体命令:关闭电视
class TurnOffTVCommand(Command):
    def __init__(self, tv):
        self.tv = tv

    def execute(self):
        self.tv.turn_off()

# 接收者:电视
class Television:
    def turn_on(self):
        print("电视已打开")

    def turn_off(self):
        print("电视已关闭")

# 调用者:遥控器
class RemoteControl:
    def __init__(self):
        self.command = None

    def set_command(self, command):
        self.command = command

    def press_button(self):
        if self.command:
            self.command.execute()

# 客户端代码
tv = Television()
turn_on_command = TurnOnTVCommand(tv)
turn_off_command = TurnOffTVCommand(tv)

remote = RemoteControl()

# 打开电视
remote.set_command(turn_on_command)
remote.press_button()

# 关闭电视
remote.set_command(turn_off_command)
remote.press_button()

在这个示例中:

  • Command 是命令接口,定义了 execute 方法。
  • TurnOnTVCommand 和 TurnOffTVCommand 是具体的命令类,它们分别表示打开电视和关闭电视的操作。
  • Television 是接收者,包含了实际的电视操作。
  • RemoteControl 是调用者,它可以设置命令并执行命令。

在客户端代码中,我们创建了电视对象和两个具体的命令对象(打开电视和关闭电视),然后使用遥控器对象来执行这些命令。这样,遥控器和电视之间的交互被解耦,可以轻松添加新的命令和扩展功能。

4. 实现要素

  1. Command(命令):命令角色是命令模式的核心,它定义了一个执行操作的接口。通常包含一个 execute 方法,用于执行具体的操作。
  2. ConcreteCommand(具体命令):具体命令是命令接口的实现者,它负责实现 execute 方法,其中包含了实际的操作逻辑。
  3. Invoker(调用者):调用者是发送命令的对象,它负责创建命令对象并将其传递给接收者来执行操作。
  4. Receiver(接收者):接收者是实际执行操作的对象,它包含了操作的具体实现。命令对象通常将请求委派给接收者来执行实际操作。

5. UML图

                 +-------------------+
                 |    Command        |
                 +-------------------+
                 | + execute(): void |
                 +-------------------+
                           ^
                           |
          +----------------+-----------------+
          |                |                 |
  +--------------+  +-----------------+  +-----------------+
  | ConcreteCommand1 |  | ConcreteCommand2 |  | ConcreteCommand3 |
  +--------------+  +-----------------+  +-----------------+
  | + execute(): void |  | + execute(): void |  | + execute(): void |
  +-------------------+  +-----------------+  +-----------------+
                           |                 |
                           |                 |
                    +-----------------+
                    |    Invoker      |
                    +-----------------+
                    | + setCommand(): void |
                    | + executeCommand(): void |
                    +-----------------+
                           ^
                           |
                    +-----------------+
                    |    Receiver     |
                    +-----------------+
                    | + action(): void |
                    +-----------------+
  • Command 是命令接口,其中包含 execute 方法,具体的命令类(ConcreteCommand1, ConcreteCommand2, ConcreteCommand3)实现了该接口。
  • Invoker 是调用者,它包含一个命令对象,并通过 setCommand 方法设置命令对象,然后通过 executeCommand 方法执行命令。
  • Receiver 是接收者,它包含了实际操作的具体实现,命令对象会委派给接收者执行操作。

在命令模式中,调用者(Invoker)不直接与接收者(Receiver)交互,而是通过命令对象来发出请求,这种解耦使得命令模式非常有用。

6. Java/golang/javascrip/C++ 等语言实现方式

6.1 Java实现

上述例子用Java语言实现示例如下:

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

// 具体命令:打开电视
class TurnOnTVCommand implements Command {
    private Television tv;

    public TurnOnTVCommand(Television tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOn();
    }
}

// 具体命令:关闭电视
class TurnOffTVCommand implements Command {
    private Television tv;

    public TurnOffTVCommand(Television tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOff();
    }
}

// 接收者:电视
class Television {
    public void turnOn() {
        System.out.println("电视已打开");
    }

    public void turnOff() {
        System.out.println("电视已关闭");
    }
}

// 调用者:遥控器
class RemoteControl {
    private Command command;

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

    public void pressButton() {
        if (command != null) {
            command.execute();
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Television tv = new Television();
        Command turnOnCommand = new TurnOnTVCommand(tv);
        Command turnOffCommand = new TurnOffTVCommand(tv);

        RemoteControl remote = new RemoteControl();

        // 打开电视
        remote.setCommand(turnOnCommand);
        remote.pressButton();

        // 关闭电视
        remote.setCommand(turnOffCommand);
        remote.pressButton();
    }
}

6.2 Golang实现

上述例子用golang实现示例如下:

package main

import "fmt"

// Command 接口定义了命令的执行方法
type Command interface {
        Execute()
}

// TurnOnTVCommand 具体命令:打开电视
type TurnOnTVCommand struct {
        TV *Television
}

func (c *TurnOnTVCommand) Execute() {
        c.TV.TurnOn()
}

// TurnOffTVCommand 具体命令:关闭电视
type TurnOffTVCommand struct {
        TV *Television
}

func (c *TurnOffTVCommand) Execute() {
        c.TV.TurnOff()
}

// Television 接收者:电视
type Television struct{}

func (tv *Television) TurnOn() {
        fmt.Println("电视已打开")
}

func (tv *Television) TurnOff() {
        fmt.Println("电视已关闭")
}

// RemoteControl 调用者:遥控器
type RemoteControl struct {
        command Command
}

func (r *RemoteControl) SetCommand(command Command) {
        r.command = command
}

func (r *RemoteControl) PressButton() {
        if r.command != nil {
                r.command.Execute()
        }
}

func main() {
        tv := &Television{}
        turnOnCommand := &TurnOnTVCommand{TV: tv}
        turnOffCommand := &TurnOffTVCommand{TV: tv}

        remote := &RemoteControl{}

        // 打开电视
        remote.SetCommand(turnOnCommand)
        remote.PressButton()

        // 关闭电视
        remote.SetCommand(turnOffCommand)
        remote.PressButton()
}

6.3 Javascript实现

上述例子用javascript实现示例如下:

// 命令接口
class Command {
  execute() {}
}

// 具体命令:打开电视
class TurnOnTVCommand extends Command {
  constructor(tv) {
    super();
    this.tv = tv;
  }

  execute() {
    this.tv.turnOn();
  }
}

// 具体命令:关闭电视
class TurnOffTVCommand extends Command {
  constructor(tv) {
    super();
    this.tv = tv;
  }

  execute() {
    this.tv.turnOff();
  }
}

// 接收者:电视
class Television {
  turnOn() {
    console.log("电视已打开");
  }

  turnOff() {
    console.log("电视已关闭");
  }
}

// 调用者:遥控器
class RemoteControl {
  constructor() {
    this.command = null;
  }

  setCommand(command) {
    this.command = command;
  }

  pressButton() {
    if (this.command) {
      this.command.execute();
    }
  }
}

// 客户端代码
const tv = new Television();
const turnOnCommand = new TurnOnTVCommand(tv);
const turnOffCommand = new TurnOffTVCommand(tv);

const remote = new RemoteControl();

// 打开电视
remote.setCommand(turnOnCommand);
remote.pressButton();

// 关闭电视
remote.setCommand(turnOffCommand);
remote.pressButton();

6.4 C++实现

上述例子用C++实现如下:

#include <iostream>

// 命令接口
class Command {
public:
    virtual void execute() = 0;
};

// 具体命令:打开电视
class TurnOnTVCommand : public Command {
private:
    Television* tv;

public:
    TurnOnTVCommand(Television* tv) : tv(tv) {}

    void execute() override {
        tv->turnOn();
    }
};

// 具体命令:关闭电视
class TurnOffTVCommand : public Command {
private:
    Television* tv;

public:
    TurnOffTVCommand(Television* tv) : tv(tv) {}

    void execute() override {
        tv->turnOff();
    }
};

// 接收者:电视
class Television {
public:
    void turnOn() {
        std::cout << "电视已打开" << std::endl;
    }

    void turnOff() {
        std::cout << "电视已关闭" << std::endl;
    }
};

// 调用者:遥控器
class RemoteControl {
private:
    Command* command;

public:
    RemoteControl() : command(nullptr) {}

    void setCommand(Command* cmd) {
        command = cmd;
    }

    void pressButton() {
        if (command) {
            command->execute();
        }
    }
};

int main() {
    Television tv;
    TurnOnTVCommand turnOnCommand(&tv);
    TurnOffTVCommand turnOffCommand(&tv);

    RemoteControl remote;

    // 打开电视
    remote.setCommand(&turnOnCommand);
    remote.pressButton();

    // 关闭电视
    remote.setCommand(&turnOffCommand);
    remote.pressButton();

    return 0;
}

7. 练习题

使用命令模式创建一个简单的文本编辑器。该编辑器应具备以下功能:

  1. 添加文本:用户可以输入文本并将其添加到文档中。
  2. 删除文本:用户可以选择删除文档中的文本。
  3. 撤销:用户可以撤销上一步操作,恢复到之前的文本状态。
  4. 重做:用户可以重做已撤销的操作,恢复到之后的文本状态。

要求:

  • 使用命令模式,为每个操作创建一个具体的命令类。
  • 编写一个接收者类,表示文档,并在接收者类中实现文本的添加和删除操作。
  • 编写一个调用者类,表示文本编辑器,用户通过调用者执行操作。
  • 编写一个客户端程序,创建文本编辑器对象,添加文本、删除文本、撤销、重做等。

你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guohuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值