【深入理解设计模式】命令设计模式

在这里插入图片描述

命令设计模式:

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户端进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

概述

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

在这里插入图片描述
定义:

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

结构

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

  • 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
  • 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

命令模式的结构如下:

  1. 定义一个命令接口,声明一个执行命令的方法。
  2. 创建具体命令类,实现命令接口,并实现具体的命令。
  3. 创建一个调用者类,用于调用命令对象的方法。
  4. 在客户端代码中,创建命令对象,并调用其方法。

下面是一个简单的命令模式示例:

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

// 具体命令类1
public class ConcreteCommand1 implements Command {
	// 接收者
    private Receiver receiver;

    public ConcreteCommand1(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action1();
    }
}

// 具体命令类2
public class ConcreteCommand2 implements Command {
	// 接收者
    private Receiver receiver;

    public ConcreteCommand2(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action2();
    }
}

// 接收者类
public class Receiver {
    public void action1() {
        System.out.println("执行操作1");
    }

    public void action2() {
        System.out.println("执行操作2");
    }
}

// 调用者类
public class Invoker {
    private Command command;

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

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

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command1 = new ConcreteCommand1(receiver);
        Command command2 = new ConcreteCommand2(receiver);

        Invoker invoker = new Invoker();
        invoker.setCommand(command1);
        invoker.executeCommand(); // 输出:执行操作1

        invoker.setCommand(command2);
        invoker.executeCommand(); // 输出:执行操作2
    }
}

在这个示例中,我们定义了一个命令接口,两个具体命令类,以及一个接收者类和一个调用者类。客户端创建命令对象,并调用其方法。

案例实现

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

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

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

订单: 命令中包含订单。

类图如下:
在这里插入图片描述
代码如下:

/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 抽象命令
 */
public interface Commend {

    void execute();

}
/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 订单类
 */
public class Order {

    // 餐桌号
    private int diningTable;

    // 用来存储餐名并记录份数
    private final Map<String, Integer> foodDic = new HashMap<String, Integer>();


    public int getDiningTable() {
        return diningTable;
    }

    public void setDiningTable(int diningTable) {
        this.diningTable = diningTable;
    }

    public Map<String, Integer> getFoodDic() {
        return foodDic;
    }

    public void setFood(String food, int num) {
        foodDic.put(food, num);
    }
}

/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 订单命令
 */
public class OrderCommend implements Commend {

    // 持有接受者对象 -> 执行命令的对象
    private final SeniorChef seniorChef;
    // 订单
    private final Order order;

    public OrderCommend(SeniorChef seniorChef, Order order) {
        this.seniorChef = seniorChef;
        this.order = order;
    }

    @Override
    public void execute() {
        System.out.println(order.getDiningTable() + "桌的订单");
        Map<String, Integer> foodDic = order.getFoodDic();
        Set<String> foodNames = foodDic.keySet();
        for (String foodName : foodNames) {
            seniorChef.createFood(foodDic.get(foodName), foodName);
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }


        System.out.println(order.getDiningTable() + "桌的饭弄好了");
    }

}
/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 厨师类 - 命令接收者
 */
public class SeniorChef {

    public void createFood(int num, String food) {
        System.out.println(num + "份" + food );
    }

}
/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 服务员对象 - 命令的请求者
 */
public class Waiter {
	// 可同时持有多个命令
    private final List<Commend> commends = new ArrayList<>();

    public void setCommends(Commend commend) {
        commends.add(commend);
    }

    public void orderUp() {
        System.out.println("订单来了!");
        for (Commend commend : commends) {
            commend.execute();
        }
    }

}
/**
 * @author OldGj 2024/03/09
 * @version v1.0
 * @apiNote 测试类 - 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 创建订单
        Order order1 = new Order();
        order1.setDiningTable(1);
        order1.setFood("爆炒河粉",2);
        order1.setFood("可乐",2);
        Order order2 = new Order();
        order2.setDiningTable(2);
        order2.setFood("肉丝",2);
        order2.setFood("雪碧",2);

        //创建厨师
        SeniorChef seniorChef = new SeniorChef();

        // 创建命令
        Commend commend = new OrderCommend(seniorChef,order1);
        Commend commend2 = new OrderCommend(seniorChef,order2);

        // 创建服务员
        Waiter waiter = new Waiter();
        waiter.setCommends(commend);
        waiter.setCommends(commend2);
        waiter.orderUp();
    }
}

命令模式的主要优点是:

  1. 命令对象将动作的请求与执行请求的对象解耦,使得可以很方便地扩展或修改行为。
  2. 命令对象可以记录请求的日志,可以进行撤销和重做操作。
  3. 命令对象可以实现宏命令,一组命令可以组合成一个命令对象。

命令模式的主要缺点是:

  1. 命令对象可能过多,导致程序性能下降。
  2. 命令对象可能需要实现一些公共接口,导致代码冗余。系统结构更加复杂。

使用场景

  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • 系统需要在不同的时间指定请求、将请求排队和执行请求。
  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/456291.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

onecloud刷CasaOS系统后如何安装内网穿透实现公网访问本地文件

文章目录 1. CasaOS系统介绍2. 内网穿透安装3. 创建远程连接公网地址4. 创建固定公网地址远程访问 2月底&#xff0c;玩客云APP正式停止运营&#xff0c;不再提供上传、云添加功能。3月初&#xff0c;有用户进行了测试&#xff0c;局域网内的各种服务还能继续使用&#xff0c;但…

国产化兼容问题与解决办法: java.lang.ClassNotFoundException: javafx.util.Pair

先说解决办法:找一个大版本相同的jdk将/jre/lib/ext中的所有jar包放到服务器jdk相同路径下,跳过相同名称. 下面是详细的问题分析,感觉啰嗦或者没有用,可以直接关闭 运行环境: 服务器:麒麟v10.x86_64 jdk:BiSheng (build 1.8.0_402-b11) 问题描述: 将程序部署在国产化服务器…

STC89C52单片机 启动!!!(一)

跑马灯实现 直接上代码 #include<regx52.h> sbit D1P2^0; sbit D2P2^1; sbit D3P2^2; sbit D4P2^3; sbit D5P2^4; sbit D6P2^5; sbit D7P2^6; sbit D8P2^7; void delay(int num){while(num--){} } void led_running(){//从第1盏灯到第8盏灯依次点亮D10;delay(40000);D2…

unity2D生成9*9格子

1.创建一个空对象和格子 2将格子做成预制体&#xff08;直接将格子拖到这里即可&#xff0c;拖了过后删掉原来的格子&#xff09; 3.创建脚本并将脚本拖到空对象上 using System.Collections; using System.Collections.Generic; using UnityEngine;public class CreateMap : M…

2024年雪糕线上市场未来发展趋势分析(2024京东淘宝天猫雪糕数据分析报告)

据相关媒体报道&#xff0c;北京多位雪糕批发商称钟薛高停产了&#xff0c;从年前开始就已经不供货了。还有记者实探钟薛高的北京总部&#xff0c;发现有不少人离职&#xff0c;办公区内仅剩零星几人。 从60元到2.5元&#xff0c;钟薛高在这两年经历了不少风波&#xff0c;终究…

鸿蒙不再适合JS语言开发

ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript&#xff08;简称TS&#xff09;基本语法风格的基础上&#xff0c;对TS的动态类型特性施加更严格的约束&#xff0c;引入静态类型。同时&#xff0c;提供了声明式UI、状态管理等相应的能力&#xff0c;让开发者可以以更简洁、…

mysql事务(MVCC机制:undo日志)(mysql执行过程:redo日志,Buffer Pool缓存池)

事务 目的&#xff1a;保证数据的最终一致性## 事务的目的 事务的4大特性&#xff08;ACID&#xff09; 1.原子性(Atomicity):由undo log日志来保证 2.一致性(Consistency):使用事务的最终目的&#xff0c;由业务代码正确逻辑保证,比如错误的try-catch 3.隔离性(Isolation):…

在任意一个文件下,进入cmd

直接在界面上输入cmd&#xff0c;回车就出来了

安卓六大布局

LinearLayout&#xff08;线性布局&#xff09; 1.简介 线性布局在开发中使用最多&#xff0c;具有垂直方向与水平方向的布局方式。LinearLayout 默认是垂直排列的&#xff0c;但是可以通过设置 android:orientation 属性来改变为水平排列。 2.常用属性 orientation&#xf…

Windows系统下载安装Emby结合内网穿透实现公网访问本地影音网站

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

3.2 RK3399项目开发实录-初次使用的环境搭建(物联技术666)

通过百度网盘分享的文件&#xff1a;嵌入式物联网单片… 链接:https://pan.baidu.com/s/1Zi9hj41p_dSskPOhIUnu9Q?pwd8qo1 提取码:8qo1 复制这段内容打开「百度网盘APP 即可获取」 1. 用户和密码 1.1. Ubuntu Desktop 系统 Ubuntu Desktop 系统开机启动后&#xff0c;自动登录…

权限管理和操作指令

文章目录 前言一、文件的权限分类二、操作时无相应权限解决办法1.使用sudo指令2.修改文档权限 总结 前言 &#x1f4a6; Linux操作系统中&#xff0c;主要都是对文件进行操作&#xff0c;完成读写或者执行功能。Ubuntu 下我们会常跟用户权限打交道&#xff0c;权限就是用户对于…

python操作dataframe--打乱df的顺序

在Python中&#xff0c;可以使用Pandas库来操作DataFrame。要打乱DataFrame的顺序&#xff0c;可以使用sample方法来实现。以下是一个示例代码&#xff1a; import pandas as pd# 创建一个示例DataFrame data {A: [1, 2, 3, 4, 5],B: [10, 20, 30, 40, 50]} df pd.DataFrame…

为什么ERP与MES集成那么难搞?怎么有效解决这一难题

在现代企业信息化进程中&#xff0c;ERP&#xff08;企业资源规划&#xff09;和MES&#xff08;制造执行系统&#xff09;作为企业管理的核心信息系统&#xff0c;它们之间的深度集成是提升生产效率、实现精益管理和智能决策的关键环节。然而&#xff0c;ERP与MES集成并非易事…

【Python】成功解决NameError: name ‘sns‘ is not defined

【Python】成功解决NameError: name ‘sns’ is not defined &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您…

1个二维码能包含多个视频吗?制作视频二维码的方法

二维码在生活中现在随处可见&#xff0c;除了用于支付之外&#xff0c;展示内容也可以通过二维码来展现&#xff0c;比如常见的视频、图片、文件、音频等内容都可以通过二维码来展现。那么当我们需要将多个视频存入一个二维码中展示时&#xff0c;该如何利用二维码生成器的工具…

开发知识点-python-Tornado框架

介绍 Tornado是一个基于Python语言的高性能Web框架和异步网络库&#xff0c;它专注于提供快速、可扩展和易于使用的网络服务。由于其出色的性能和灵活的设计&#xff0c;Tornado被广泛用于构建高性能的Web应用程序、实时Web服务、长连接的实时通信以及网络爬虫等领域。 Torna…

jmeter接口自动化测试通过csv文件读取用例并执行测试

最近在公司测试中经常使用jmeter这个工具进行接口自动化&#xff0c;简单记录下~ 一、在csv文件中编写好用例 首先在csv文件首行填写相关参数&#xff08;可根据具体情况而定&#xff09;并编写测试用例。脚本可通过优先级参数控制执行哪些接口&#xff0c;通过端口参数同时执…

leetcode110.平衡二叉树

之前没有通过的样例 return语句只写了一个 return abs(l-r)<1缺少了 isBalanced(root->left)&&isBalanced(root->right);补上就好了 class Solution { public:bool isBalanced(TreeNode* root) {if(!root){return true;}int lgetHeight(root->left);i…

第三百九十一回

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何混合选择多个图片和视频文件"相关的内容&#xff0c;本章回中将介绍如何通过相机获取视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. …