Java设计模式之《命令模式》

发布于:2025-08-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

1、介绍

1.1、命令模式定义

1.2、对比

1.3、典型应用场景

2、命令模式的结构

2.1、组成部分:

2.2、整体流程

3、实现

3.1、没有命令模式

3.2、命令模式写法

4、命令模式的优缺点


前言

java设计模式分类:


1、介绍

1.1、命令模式定义

     命令模式(Command Pattern)是一种行为型设计模式,又叫动作模式或事务模式

        它将请求(命令)封装成对象,使得可以用不同的请求对客户端进行参数化具体的请求可以在运行时更改、排队或记录,它将发出者和接收者解耦(顺序:发出者-->命令-->接收者)。

总结:

        把请求的动作和请求的接收者进行解耦,让你能将动作封装成对象,再传递、存储、撤销或排队等。

1.2、对比

状态模式:

Context(电梯)-- 持有当前状态 --> State(状态接口)-- 实现 --> OpenState, RunState等

命令模式:

1.3、典型应用场景

1、Runnable接口:

        Java中的Runnable接口就是一个典型的命令模式的应用。Runnable接口封装了需要执行的任务,然后可以交给线程去执行


2、Timer和TimerTask类:

        这两个类用于定时任务调度,TimerTask类封装了要执行的任务,然后由Timer类作为调用者执行这些任务。


3、Statement接口:

        在Java中与数据库交互时,SQL语句被封装成Statement对象,然后由数据库驱动程序执行相应的命令。

生活示例:

1、 餐厅点餐:

        在一家餐厅中,服务员充当调用者,厨师充当接收者,菜品可以作为具体命令。当顾客想点菜时,服务员会将顾客的需求封装成一个命令对象,并传递给厨师。厨师根据命令对象中的信息来完成相应的烹饪工作。这样,顾客和厨师之间不需要直接沟通,而是通过命令对象来实现点餐和烹饪的解耦。


2、遥控器控制家电:

        拿电视遥控器举例,遥控器是调用者,电视是接收者。每个按键都可以看作是一个具体命令,例如音量加、音量减、切换频道等。

        当用户按下某个按键时,遥控器会发送相应的命令给电视,然后电视根据命令执行相应的操作,如增加音量、减小音量或切换频道。


2、命令模式的结构

2.1、组成部分:

  1. Command(命令接口/抽象类) 声明execute()方法。
  2. ConcreteCommand(具体命令) 实现Command,并持有Receiver引用,把请求操作的接收者封装进来,调用接收者的方法。
  3. Receiver(接收者) 真正执行业务逻辑的对象。
  4. Invoker(调用者) 有个成员变量保存Command,并执行command.execute()
  5. Client(客户端): 组装命令、接收者、并给调用者下令。

2.2、整体流程

所有的“做某件事”,都变成订单(命令对象)Waiter只“发订单”,Chef只“实现订单”:

如下图所示:

        命令模式,把“执行动作”的代码封装成对象。Waiter(服务员)不需要知道每个命令详情,只要保存一份“命令单子”,然后调用它的执行方法即可。


3、实现

假设你在饭店点菜。

  • 你(Client 客户端):我要点“宫保鸡丁”。
  • 服务员(Invoker 调用者):拿到菜单,把点菜单(订单)交给厨房。
  • 订单(Command 命令对象):订单上写着“做宫保鸡丁”。
  • 厨师(Receiver 接收者):收到订单,立刻开始炒菜。

这样,服务员并不需要会做菜,他只要拿订单就行了。

3.1、没有命令模式

你直接告诉服务员 “去让张师傅做宫保鸡丁”:

代码如下所示:

// 直接耦合
public class Waiter {
    private Chef chef = new Chef();

    public void orderGongBaoChicken() {
        chef.makeGongBaoChicken();
    }
}

public class Chef {
    public void makeGongBaoChicken() {
        System.out.println("炒宫保鸡丁!");
    }
}
// 调用
Waiter waiter = new Waiter();
waiter.orderGongBaoChicken();

3.2、命令模式写法

1. 命令接口

public interface Command {
    void execute();   // 执行命令
    void undo();      // 撤销命令(可选)
}
  • 这里的Command相当于“点菜单的模板”,所有的点菜单都要有一个execute方法。

2. 接收者(Receiver)——厨师

public class Chef {
    public void makeGongBaoChicken() {
        System.out.println("厨师:炒宫保鸡丁!");
    }
    public void makeFriedRice() {
        System.out.println("厨师:炒蛋炒饭!");
    }
}
  • Chef能够真的炒不同的菜。

3. 具体命令(ConcreteCommand)——不同的订单

public class GongBaoChickenCommand implements Command {
    private Chef chef;

    public GongBaoChickenCommand(Chef chef) {
        this.chef = chef;
    }

    @Override
    public void execute() {
        chef.makeGongBaoChicken();
    }

    @Override
    public void undo() {
        System.out.println("取消宫保鸡丁!");
    }
}

public class FriedRiceCommand implements Command {
    private Chef chef;
    public FriedRiceCommand(Chef chef) {
        this.chef = chef;
    }
    @Override
    public void execute() {
        chef.makeFriedRice();
    }
    @Override
    public void undo() {
        System.out.println("取消蛋炒饭!");
    }
}
  • 每个命令就是一张“点菜单”,知道自己要交给哪个厨师(Receiver)。

4、调用者(Invoker)服务员

public class Waiter {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }
    public void placeOrder() {
        // 点菜
        command.execute();
    }
    public void cancelOrder() {
        // 撤销点菜
        command.undo();
    }
}

5、客户端组装

public class Customer {
    public static void main(String[] args) {
        Chef chef = new Chef();
        Command gongBaoChicken = new GongBaoChickenCommand(chef);
        Command friedRice = new FriedRiceCommand(chef);

        Waiter waiter = new Waiter();

        // 点宫保鸡丁
        waiter.setCommand(gongBaoChicken);
        waiter.placeOrder();
        // 点蛋炒饭
        waiter.setCommand(friedRice);
        waiter.placeOrder();
        // 撤销刚才的蛋炒饭
        waiter.cancelOrder();
    }
}

输出:

厨师:炒宫保鸡丁!
厨师:炒蛋炒饭!
取消蛋炒饭!

有上述示例可知命令模式的功能:

  • 解耦:Waiter只需要认识命令Command,不用知道Chef做什么。
  • 追加功能:要新增一道菜,只要做一个新的Command类即可,Waiter不用改。
  • 支持队列、日志、撤销:命令对象能被存储、排队,可以撤销。

4、命令模式的优缺点

1、优点:

彻底解耦“请求者”与“执行者”,调用者无需知道接收者是啥

请求可以参数化(每个对象带不同接收者和参数)。

请求可以放队列、做日志,如事务撤销与恢复、任务队列。

增加新命令类非常方便。

2、缺点:

类的数量可能会增多(每种命令都要实现 Command 接口)。


总结

        命令模式适用于需要将请求封装成对象,实现请求发出者和接收者之间的解耦,并支持撤销队列化延迟执行的场景。它虽然可以提高系统的可扩展性和灵活性,但是需要权衡额外的开销和复杂性。


参考文章:

1、设计模式第19讲——命令模式(Command)-CSDN博客文章浏览阅读1.1w次,点赞38次,收藏174次。命令模式是一种行为设计模式,它将请求封装为对象,使发出者和接收者解耦。文章介绍了命令模式的角色(抽象命令、具体命令、接收者和调用者),优缺点以及在餐厅点餐和Java场景中的应用,还提供了代码实现示例。 https://blog.csdn.net/weixin_45433817/article/details/131465270?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522d919eba72e6dd9b4ef68572ad0a68137%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=d919eba72e6dd9b4ef68572ad0a68137&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-131465270-null-null.142^v102^control&utm_term=%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4187


网站公告

今日签到

点亮在社区的每一天
去签到