命令模式

Post on Oct 05, 2021 by Wei Lin

介绍

命令模式(Command Pattern)是一种行为型设计模式,用于将请求封装成一个对象,使得可以将不同的请求参数化、延迟执行或者支持撤销操作。该模式将请求发送者和请求接收者解耦,使得请求发送者不需要知道具体的请求接收者。


命令模式的核心概念是引入了一个命令对象(Command),该对象封装了特定的操作(或称为命令),并包含了操作所需的参数和方法。请求发送者(Invoker)通过调用命令对象的方法来发出请求,而不需要直接与请求接收者(Receiver)交互。

模式角色

(1) 抽象命令(Command)

定义了执行操作的接口,通常包含一个execute()方法,用于执行具体的操作。


(2) 具体命令(Concrete Command)

实现了抽象命令接口,封装了具体的操作,通常会持有一个具体的请求接收者对象,并调用其方法来执行操作。


(3) 请求接收者(Receiver)

执行具体操作的对象,具体命令对象会与请求接收者关联,并在执行时调用其方法来完成操作。


(4) 请求发送者(Invoker)

包含了命令对象,并在需要执行操作时调用命令对象的execute()方法来触发执行。

命令模式.png

代码示例

package com.behavioral;

/**
 * 以开关灯为例实现命令模式
 * 使用者向灯发送命令,由灯执行具体命令
 */
public class CommandPattern {
    public static void main(String[] args) {
        // 请求接收者(Receiver)
        Light light = new Light();
        // 请求发送者(Invoker)
        RemoteControl remoteControl = new RemoteControl();

        // 生成具体的命令
        Command lightOnCommand = new LightOnCommand(light);
        //传送或设置命令
        remoteControl.setCommand(lightOnCommand);
        // 执行命令,最后是由具体的命令类(ConcreteCommand),即LightOnCommand执行的
        // Light is on
        remoteControl.pressButton();

        // 关灯的执行拖成
        Command lightOffCommand = new LightOffCommand(light);
        remoteControl.setCommand(lightOffCommand);
        remoteControl.pressButton(); // Light is off
    }
}

/**
 * 定义抽象命令接口(Command)
 */
interface Command {
    void execute();
}

/**
 * 具体的命令类(ConcreteCommand),实现抽象命令接口
 */
class LightOnCommand implements Command {
    private Light light;

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

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

class LightOffCommand implements Command {
    private Light light;

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

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

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

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

/**
 * 请求发送者(Invoker)
 */
class RemoteControl {
    private Command command;

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

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

应用场景

(1) 需要将请求发送者和请求接收者解耦

命令模式可以将请求封装成一个命令对象,使得发送者和接收者彼此解耦,它们不需要直接交互。这样可以提高系统的灵活性和可扩展性。


(2) 需要支持撤销操作

命令模式可以通过保存命令的历史记录,实现对命令的撤销操作。这对于需要实现撤销、恢复或回滚操作的场景非常有用。


(3) 需要支持事务处理

命令模式可以将一系列操作封装成一个事务,保证这些操作要么全部执行成功,要么全部回滚。这对于需要确保一组操作的原子性和一致性的场景非常有用。


(4) 需要支持延迟执行和异步执行

命令模式可以将命令对象存储起来,在需要执行时再进行调用。这样可以支持延迟执行和异步执行的需求。


(5) 需要支持命令的动态配置和扩展

命令模式可以通过动态配置和组合不同的命令对象,实现不同的操作组合。这样可以在运行时动态地添加、替换或组合命令,灵活地扩展系统的功能。

优缺点

(1) 命令模式的优点

  • 降低系统的耦合度:命令模式通过将请求发送者和请求接收者解耦,使得二者可以独立地变化,而不会相互影响。
  • 增强系统的灵活性和可扩展性:命令模式可以轻松地添加新的具体命令类,而不需要修改已有的代码。它还支持动态配置和扩展命令的组合,从而灵活地扩展系统的功能。
  • 支持撤销和恢复操作:命令模式通过保存命令的历史记录,可以实现对命令的撤销和恢复操作。
  • 支持事务处理:命令模式可以将一系列操作封装成一个事务,保证这些操作要么全部执行成功,要么全部回滚。
  • 支持延迟执行和异步执行:可以将命令对象存储起来,在需要执行时再进行调用。


(2) 命令模式的缺点

  • 可能会导致类膨胀:引入了许多具体命令类和请求接收者类,可能会增加系统的类和对象数量。
  • 命令的无状态性:命令模式中的命令对象通常是无状态的,它们只负责执行特定的操作,而不维护状态。这可能需要在某些情况下引入其他的管理机制。

Android中的应用-Activity

Android中Activity的生命周期执行就是通过命令模式实现的。

其中system端作为请求者,向APP端发送命令,APP端收到命令后执行具体Activity的生命周期。

  • system端进程:请求发送者(Invoker),向APP端发送ClientTransactionItem子类对象;
  • ClientTransactionItem对象:抽象命令(Command),定义了生命周期对象的接口,将它封装到ClientTransaction对象中并跨进程传输给APP端;
  • ResumeActivityItem等:具体命令(Concrete Command),需要由APP端执行的具体的生命周期指令;
  • APP端-TransactionExecutor对象:请求接收者(Receiver),是APP端接收命令后的具体执行对象,由它调度命令的执行过程。