• 行为型模式 - 命令模式


    概述

    日常生活中,我们出去吃饭都会遇到下面的场景。

     

    定义:

            将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

    结构

    命令模式包含以下主要角色:

    • 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。

    • 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

    • 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

    • 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

    案例实现

    将上面的案例用代码实现,那我们就需要分析命令模式的角色在该案例中由谁来充当。

    服务员: 就是调用者角色,由她来发起命令。

    资深大厨: 就是接收者角色,真正命令执行的对象。

    订单: 命令中包含订单。

    类图如下:

     代码如下:

    1. public interface Command {
    2. void execute();//只需要定义一个统一的执行方法
    3. }
    4. public class OrderCommand implements Command {
    5. //持有接受者对象
    6. private SeniorChef receiver;
    7. private Order order;
    8. public OrderCommand(SeniorChef receiver, Order order){
    9. this.receiver = receiver;
    10. this.order = order;
    11. }
    12. public void execute() {
    13. System.out.println(order.getDiningTable() + "桌的订单:");
    14. Set keys = order.getFoodDic().keySet();
    15. for (String key : keys) {
    16. receiver.makeFood(order.getFoodDic().get(key),key);
    17. }
    18. try {
    19. Thread.sleep(100);//停顿一下 模拟做饭的过程
    20. } catch (InterruptedException e) {
    21. e.printStackTrace();
    22. }
    23. System.out.println(order.getDiningTable() + "桌的饭弄好了");
    24. }
    25. }
    26. public class Order {
    27. // 餐桌号码
    28. private int diningTable;
    29. // 用来存储餐名并记录份数
    30. private Map foodDic = new HashMap();
    31. public int getDiningTable() {
    32. return diningTable;
    33. }
    34. public void setDiningTable(int diningTable) {
    35. this.diningTable = diningTable;
    36. }
    37. public Map getFoodDic() {
    38. return foodDic;
    39. }
    40. public void setFoodDic(String name, int num) {
    41. foodDic.put(name,num);
    42. }
    43. }
    44. // 资深大厨类 是命令的Receiver
    45. public class SeniorChef {
    46. public void makeFood(int num,String foodName) {
    47. System.out.println(num + "份" + foodName);
    48. }
    49. }
    50. public class Waitor {
    51. private ArrayList commands;//可以持有很多的命令对象
    52. public Waitor() {
    53. commands = new ArrayList();
    54. }
    55. public void setCommand(Command cmd){
    56. commands.add(cmd);
    57. }
    58. // 发出命令 喊 订单来了,厨师开始执行
    59. public void orderUp() {
    60. System.out.println("美女服务员:叮咚,大厨,新订单来了.......");
    61. for (int i = 0; i < commands.size(); i++) {
    62. Command cmd = commands.get(i);
    63. if (cmd != null) {
    64. cmd.execute();
    65. }
    66. }
    67. }
    68. }
    69. public class Client {
    70. public static void main(String[] args) {
    71. //创建2个order
    72. Order order1 = new Order();
    73. order1.setDiningTable(1);
    74. order1.getFoodDic().put("西红柿鸡蛋面",1);
    75. order1.getFoodDic().put("小杯可乐",2);
    76. Order order2 = new Order();
    77. order2.setDiningTable(3);
    78. order2.getFoodDic().put("尖椒肉丝盖饭",1);
    79. order2.getFoodDic().put("小杯雪碧",1);
    80. //创建接收者
    81. SeniorChef receiver=new SeniorChef();
    82. //将订单和接收者封装成命令对象
    83. OrderCommand cmd1 = new OrderCommand(receiver, order1);
    84. OrderCommand cmd2 = new OrderCommand(receiver, order2);
    85. //创建调用者 waitor
    86. Waitor invoker = new Waitor();
    87. invoker.setCommand(cmd1);
    88. invoker.setCommand(cmd2);
    89. //将订单带到柜台 并向厨师喊 订单来了
    90. invoker.orderUp();
    91. }
    92. }

    优缺点

    1,优点:

    • 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。

    • 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。

    • 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。

    • 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

    2,缺点:

    • 使用命令模式可能会导致某些系统有过多的具体命令类。

    • 系统结构更加复杂。

    使用场景

    • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

    • 系统需要在不同的时间指定请求、将请求排队和执行请求。

    • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

    JDK源码解析

    Runable是一个典型命令模式,Runnable担当命令的角色,Thread充当的是调用者,start方法就是其执行方法。

    1. //命令接口(抽象命令角色)
    2. public interface Runnable {
    3. public abstract void run();
    4. }
    5. //调用者
    6. public class Thread implements Runnable {
    7. private Runnable target;
    8. public synchronized void start() {
    9. if (threadStatus != 0)
    10. throw new IllegalThreadStateException();
    11. group.add(this);
    12. boolean started = false;
    13. try {
    14. start0();
    15. started = true;
    16. } finally {
    17. try {
    18. if (!started) {
    19. group.threadStartFailed(this);
    20. }
    21. } catch (Throwable ignore) {
    22. }
    23. }
    24. }
    25. private native void start0();
    26. }

    会调用一个native方法start0(),调用系统方法,开启一个线程。而接收者是对程序员开放的,可以自己定义接收者。

    1. /**
    2. * jdk Runnable 命令模式
    3. * TurnOffThread : 属于具体
    4. */
    5. public class TurnOffThread implements Runnable{
    6. private Receiver receiver;
    7. public TurnOffThread(Receiver receiver) {
    8. this.receiver = receiver;
    9. }
    10. public void run() {
    11. receiver.turnOFF();
    12. }
    13. }
    1. /**
    2. * 测试类
    3. */
    4. public class Demo {
    5. public static void main(String[] args) {
    6. Receiver receiver = new Receiver();
    7. TurnOffThread turnOffThread = new TurnOffThread(receiver);
    8. Thread thread = new Thread(turnOffThread);
    9. thread.start();
    10. }
    11. }
  • 相关阅读:
    framebuffer驱动
    k8s使用rbd作为存储
    云渲染是什么?云渲染怎么用?
    【spring】一文读懂SpringIOC和AOP
    几个算法题解
    c语言写逆置数
    Linux - linux命令进阶
    nginx启动报错纠正
    什么是敏捷测试
    大语言模型 LLM book 笔记(一)
  • 原文地址:https://blog.csdn.net/qq_36942720/article/details/131818175