设计模式详解(十九)——命令模式

命令模式简介

命令模式定义

命令模式(Command Pattern)是一种在面向对象程序设计中常用的行为型设计模式。命令模式的核心思想在于将请求封装成一个对象,从而使发出请求的责任和执行请求的责任分割开。它可以让请求发送者和请求接收者之间消除彼此之间的耦合关系。这种模式的关键在于将请求的行为参数化,使得不同的请求可以在不同时间由不同的对象来处理。同时,它支持对请求排队、记录请求日志,以及实现可撤销的操作。

命令模式包含以下角色:

  1. 命令角色(Command):这是一个抽象类或接口,它定义了执行命令的方法,通常包含执行(execute)方法,但不实现具体的命令行为。
  2. 具体命令角色(Concrete Command):实现了命令接口,持有接收者的引用,并在执行方法中调用接收者的方法来执行具体的命令行为;实现execute()方法,负责调用接收者的相应操作。
  3. 接收者角色(Receiver):接收者是执行命令的对象,它实现了命令所需要执行的具体操作。接收者可以有多个,每个接收者都可以执行不同的操作。
  4. 请求者角色(Invoker):请求者持有命令对象的引用,并通过调用命令的execute()方法来执行请求。它不需要知道命令的具体实现细节,只需知道如何调用命令对象。负责调用命令对象执行请求,相关的方法叫做行动方法(如action())。
  5. 客户端(Client):创建具体命令对象并设置其接收者,将命令对象传递给调用者执行请求。

命令模式优缺点:

优点:

  1. 解耦合:命令模式降低了调用者和接收者之间的耦合度。调用者只需知道命令的接口,而不需要知道命令的具体实现细节,从而降低系统的耦合度。
  2. 可扩展性:新的命令可以很容易地添加到系统中,而无需修改现有的代码结构,从而不影响现有的命令,符合开闭原则。
  3. 支持撤销和重做:由于命令被封装成对象,在具体命令类中保存状态,可以轻松地实现命令的撤销和重做功能。
  4. 日志记录:可以在命令对象中增加日志记录功能,记录命令的执行情况。这对于系统的调试和审计非常有帮助。
  5. 容易实现队列和宏命令:可以创建宏命令(复合命令),即把多个简单命令组合成一个更复杂的命令。
  6. 更好的安全性:命令模式可以帮助限制对敏感方法的访问权限,因为命令接口可以隐藏这些方法的实现细节。

缺点:

  1. 过多的具体命令类:每个具体命令都需要一个单独的类,可能会导致类的数量增加。如果系统中的命令种类非常多,可能会导致产生大量的具体命令类
  2. 系统复杂性:引入了额外的类和对象,增加了系统的复杂性。
  3. 执行效率:由于命令被封装成对象,可能会导致执行效率略有降低,特别是在需要大量命令对象的情况下。
  4. 可能增加对象数量:为了支持撤销操作,每个命令可能需要保存额外的状态信息,这会增加对象的数量。

使用场景

  1. 撤销和重做操作:当需要支持撤销和重做操作时,可以使用命令模式将每个操作封装成一个命令对象,从而实现撤销和重做功能。
  2. 队列请求:当需要将请求排队、记录请求日志、支持事务等场景时,可以使用命令模式将请求封装成命令对象,然后由调用者依次执行这些命令对象。
  3. 宏命令:当需要将多个简单命令组合成一个复合命令时,可以使用命令模式。
  4. 远程控制和自动化:在远程控制系统中,可以使用命令模式来发送远程指令给接收设备。
  5. 日程安排系统:在日程安排系统中,可以使用命令模式实现对日程的增加、删除、修改等操作,支持撤销和重做功能。
  6. GUI 应用程序:在图形用户界面中,命令模式可以用来处理用户的输入事件,例如按钮点击、菜单选项选择等
  7. 日志记录和历史追踪:命令模式可以用来记录操作的历史,这对于审计和调试非常有用。

以下举一个命令模式的例子:

下面通过一个简单的例子来演示。

假如我们自己开发一个音乐播放器,有播放功能、切换上一首功能、切换下一首功能、暂停功能,我们自己去操作的时候并不是直接调用音乐播放器的方法,而是通过一个控制条去传达指令给播放器内部。那么每个按钮就相当于是对一条命令的封装。

创建接收者角色(Receiver)

java 复制代码
/**
 * 音乐播放器,作为接收者角色(Receiver)
 */
public class MusicPlayer {
    public void play() {
        System.out.println("播放");
    }

    public void stop() {
        System.out.println("暂停");
    }

    public void previousSong() {
        System.out.println("拖动进度条");
    }

    public void nextSong() {
        System.out.println("停止播放");
    }
}

创建命令角色(Command)

java 复制代码
/**
 * 命令角色(Command)
 */
public interface Command {
    void execute();
}

创建4个具体命令角色(Concrete Command),分别是实现播放,暂停,上一首,下一首的操作

java 复制代码
/**
 * 播放操作
 */
public class Play implements Command{
    private MusicPlayer musicPlayer;

    public Play(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.play();
    }
}
java 复制代码
/**
 * 暂停操作
 */
public class Stop implements Command{
    private MusicPlayer musicPlayer;

    public Stop(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.stop();
    }
}
java 复制代码
/**
 * 切换上一首歌操作
 */
public class PreviousSong implements Command{
    private MusicPlayer musicPlayer;

    public PreviousSong(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.previousSong();
    }
}
java 复制代码
/**
 * 切换下一首歌操作
 */
public class NextSong implements Command{
    private MusicPlayer musicPlayer;

    public NextSong(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.nextSong();
    }
}

创建请求者角色(Invoker)

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 请求者角色(Invoker)
 */
public class Action {
    private List<Command> actions = new ArrayList<Command>();

    public void addAction(Command action){
        actions.add(action);
    }

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

    public void executes(){
        for (Command action:actions) {
            action.execute();
        }
        actions.clear();
    }
}

创建客户端(Client)

java 复制代码
/**
 * 客户端(Client)
 */
public class Client {
    public static void main(String[] args) {

        MusicPlayer musicPlayer = new MusicPlayer();
        Action action = new Action();
        //立即执行操作
        action.execute(new Play(musicPlayer));
        action.execute(new Stop(musicPlayer));
        action.execute(new PreviousSong(musicPlayer));
        action.execute(new NextSong(musicPlayer));
        System.out.println("=========================");

        // 将方法添加到列表中,方便执行多条操作
        // 例如音乐播放器点下一首时,会切换下一首歌,并进行播放,这就设计一个按钮就行,然后这个按钮可以直接实现,切换下一首歌并播放的操作
        Action actionOther = new Action();
        actionOther.addAction(new NextSong(musicPlayer));
        actionOther.addAction(new Play(musicPlayer));
        actionOther.executes();
    }
}

输出结果如下所示:

java 复制代码
播放
暂停
切换到上一首歌
切换到下一首歌
=========================
切换到下一首歌
播放

在上述例子中,

展示了命令模式在音乐播放器中的应用。MusicPlayer 类作为接收者,提供了播放、暂停、上一首和下一首的操作。Command 接口定义了命令的通用执行方法。四个具体命令类 (Play, Stop, PreviousSong, NextSong) 实现了这些操作,并持有 MusicPlayer 的引用。Action 类作为调用者,管理命令列表并执行命令。客户端通过 Action 对象立即执行单个命令或批量执行一系列命令,实现了命令的解耦和灵活管理。

总而言之:

命令模式是一种行为设计模式,用于将请求封装为对象,以便使用不同的请求对客户端进行参数化,支持命令队列、日志记录和撤销操作。核心概念包括命令接口(Command)、具体命令类(Concrete Command)、接收者(Receiver)和调用者(Invoker)。命令接口定义了所有命令共有的执行方法,具体命令类实现了命令接口并封装了接收者对象的引用,接收者执行实际的业务逻辑,调用者则请求命令对象执行命令。命令模式的优势在于降低调用者与接收者之间的耦合度,支持撤销操作和宏命令,易于扩展新命令。但同时也可能导致产生大量具体命令类,客户端需要管理命令对象,且可能存在性能开销。适用于GUI应用程序、事务处理、宏命令、命令行界面和需要撤销操作的场景。

以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git

相关推荐
丶白泽3 小时前
重修设计模式-结构型-组合模式
设计模式·组合模式
yunhuibin4 小时前
ffmpeg面向对象——参数配置秘密探索及其设计模式
学习·设计模式·ffmpeg
_祝你今天愉快5 小时前
技术成神之路:设计模式(十四)享元模式
java·设计模式
蔚一6 小时前
Java设计模式—面向对象设计原则(三) -----> 依赖倒转原则DIP(完整详解,附有代码+案例)
java·开发语言·设计模式·intellij-idea·依赖倒置原则
丶白泽7 小时前
重修设计模式-概览
java·设计模式
java_heartLake10 小时前
设计模式之建造者模式
java·设计模式·建造者模式
G皮T10 小时前
【设计模式】创建型模式(四):建造者模式
java·设计模式·编程·建造者模式·builder·建造者
战神刘玉栋11 小时前
《程序猿之设计模式实战 · 观察者模式》
python·观察者模式·设计模式
nakyoooooo12 小时前
【设计模式】工厂模式、单例模式、观察者模式、发布订阅模式
观察者模式·单例模式·设计模式
严文文-Chris13 小时前
【设计模式-享元】
android·java·设计模式