设计模式之命令模式

article/2025/11/6 10:07:35

命令模式

    • 命令模式的定义
    • 非命令模式实现:
    • 命令模式的实现:

命令模式的定义

说实话这个模式挺令人纠结的,但从这个模式的定义上来看,有点让人摸不到什么头脑,而且查看资料以后会发现还是有点稀里糊涂的,说懂了吧也很简单,也不懂吧也有不懂的理由,于是查阅手头的各种书籍,在此写下心得体会,算是加深一下印象。

命令模式的定义:将请求封装成一个对象,从而让用户使用不同的请求把客户端参数化,以及支持可撤销和恢复的功能。

从定义上来看着实令人一脸懵逼,在这里描述一下博主个人的理解:

请求:客户端要求系统执行的操作,在java的世界里面就是某个对象的方法。

Command:请求封装成的对象,该对象是命令模式的主角。也就是说将请求方法封装成一个命令对象,通过操作命令对象来操作请求方法。在命令模式是有若干个请求的,需要将这些请求封装成一条条命令对象,客户端只需要调用不同的命令就可以达到将请求参数化的目的。将一条条请求封装成一条条命定对象之后,客户端发起的就是一个个命令对象了,而不是原来的请求方法!(行为参数化,在Java8得到了更好的支持)

Receiver:有命令,当然有命令的接收者对象:如果有只有命令,没有接受者,那不就是光棍司令了?没有电视机或者电脑主机,你对着电视机遥控器或者电脑键盘狂按有毛用?Receiver对象的主要作用就是受到命令后执行对应的操作。对于点击遥控器发起的命令来说,电视机就是这个Receiver对象,比如按了待机键,电视机收到命令后就执行了待机操作,进入待机状态。

Client:但是有一个问题摆在眼前,命令对象现在已经有了,但是谁来负责创建命令呢?这里就引出了客户端Client对象,再命令模式中命令是有客户端来创建的。打个比方来说,操作遥控器的那个人,就是扮演的客户端的角色。人按下遥控器的不同按键,来创建一条条命令。

Invoker:现在创建命令的对象Client也已经露脸了,它负责创建一条条命令,那么谁来使用或者调度这个命令呢?--命令的使用者就是Invoker对象了,还是拿人,遥控器,电视机来做比喻,遥控器就是这个Invoker对象,遥控器负责使用客户端创建的命令对象。该Invoker对象负责要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。

上面的讲解着实有些啰嗦,下面就用看电视的人(Watcher),电视机(Television),遥控器(TeleController)来模拟一下这个命令模式,其中Watcher是Client角色,Television是Receiver角色,TeleController是Invoker角色。

首先设计一个简单的电视机的对象:

//电视机对象:提供了播放不同频道的方法
public class Television {public void playCctv1() {System.out.println("--CCTV1--");}public void playCctv2() {System.out.println("--CCTV2--");}public void playCctv3() {System.out.println("--CCTV3--");}public void playCctv4() {System.out.println("--CCTV4--");}public void playCctv5() {System.out.println("--CCTV5--");}public void playCctv6() {System.out.println("--CCTV6--");}}

电视机的类创建好了,本文会以“非命令模式“和“命令模式“两种实现看电视的不同之处,加深对命令模式的理解。

非命令模式实现:

如果不用命令模式的话,其实实现看电视的功能很简单,首先设计一个看电视的人的类:Watcher;既然要看电视,所以Watcher内部需要持有一个电视机的引用。如此简单的Watcher搞定:

//观看电视的死宅类
public class Watcher {//持有一个public Television tv;public Watcher(Television tv) {this.tv = tv;}public void playCctv1() {tv.playCctv1();}public void playCctv2() {tv.playCctv2();}public void playCctv3() {tv.playCctv3();}public void playCctv4() {tv.playCctv4();}public void playCctv5() {tv.playCctv5();}public void playCctv6() {tv.playCctv6();}
}

所以简单的调用就实现了:

public static void main(String args[]) {Watcher watcher = new Watcher(new Television());watcher.playCctv1();watcher.playCctv2();watcher.playCctv3();watcher.playCctv4();watcher.playCctv5();watcher.playCctv6();}

执行结果:

--CCTV1--
--CCTV2--
--CCTV3--
--CCTV4--
--CCTV5--
--CCTV6--

可以看出Watcher类和Television完全的耦合在一起了,目前本文的电视机对象只能播放六个电视台,如果需要增添全国所有主流卫视的话,需要做如下改动:
1、修改Television对象,增加若干个的playXXTV()的方法来播放不同的卫视。
2、修改Watcher,也添加若干个对应的playXXTV()的方法,调用Television的playXXTV(),如下:

 public void playXXTV() {tv.playXXTV();}

但是这明显违背了“对修改关闭,对扩展开放“的重要设计原则。
别的不说,就拿本看电视来说,比如调用playXXTV()的顺序是随即的,也就是你胡乱切换了一通:比如你沿着cctv1、cctv2、cctv3、cctv4、xxtv、yytv…nntv的顺序来看电视,也就是发生了如下调用:

        watcher.playCctv1();watcher.playCctv2();watcher.playCctv3();watcher.playCctv4();watcher.playCctv5();watcher.playCctv6();watcher.playXXtv();watcher.playYYtv();watcher.playNNtv();

,当前你在看nntv,如果你想看上一个看过的台也就是yytv,这很简单,只要在playNNtv() 后面,调用watcher.playYYtv();即可,但是如果你想要一直回退到cctv1呢?那么你就话发生如下可怕的调用:

        watcher.playCctv1();watcher.playCctv2();watcher.playCctv3();watcher.playCctv4();watcher.playCctv5();watcher.playCctv6();watcher.playXXtv();watcher.playYYtv();watcher.playNNtv();watcher.playYYtv();watcher.playXXtv();watcher.playCctv6();watcher.playCctv5();watcher.playCctv4();watcher.playCctv3();watcher.playCctv2();watcher.playCctv1();

为什么会这样呢?因为对于之前播放的哪个卫视并没有记录功能。是时候让命令模式来出来解决 问题了,通过命令模式的实现,对比下就能体会到命令模式的巧妙之处。

命令模式的实现:

1、设计一个抽象的命令类:
在本系统中,命令的接收者对象就是电视机Tevevision了:

public abstract class Command {//命令接收者:电视机protected Television television;public Command(Television television) {this.television = television;}//命令执行abstract void execute();
}

将播放各个卫视的操作封装成一个一个命令,实现如下:

//播放cctv1的命令
public class CCTV1Command extends Command {@Overridevoid execute() {television.playCctv1();}
}//播放cctv2的命令
public class CCTV6Command extends Command {@Overridevoid execute() {television.playCctv2();}
}
。。。。。。。。//播放cctv6的命令
public class CCTV1Command extends Command {@Overridevoid execute() {television.playCctv6();}
}

抽象类Command的几个子类实现也很简单,就是将电视机TeleVision对象的playXxTV方法分布于不同的命令对象中,且因为不同的命令对象拥有共同的抽象类,我们很容易将这些名利功能放入一个数据结构(比如数组)中来存储执行过的命令。

命令对象设计好了,那么就引入命令的调用着Invoker对象了,在此例子中电视遥控器TeleController就是扮演的这个角色:

public class TeleController {//播放记录List<Command> historyCommand = new ArrayList<Command>();//切换卫视public void switchCommand(Command command) {historyCommand.add(command);command.execute();}//遥控器返回命令public void back() {if (historyCommand.isEmpty()) {return;}int size = historyCommand.size();int preIndex = size-2<=0?0:size-2;//获取上一个播放某卫视的命令Command preCommand = historyCommand.remove(preIndex);preCommand.execute();}}

很简答,遥控器对象持有一个命令集合,用来记录已经执行过的命令。新的命令对像作为switchCommand参数来添加到集合中,注意在这里就体现出了让上文所术的请求参数化的目的。且遥控器类也提供了back方法用来模拟真实遥控器的返回功能:
所以main方法的实现如下:

       //创建一个电视机Television tv = new Television();//创建一个遥控器TeleController teleController = new TeleController();teleController.switchCommand(new CCTV1Command(tv));teleController.switchCommand(new CCTV2Command(tv));teleController.switchCommand(new CCTV4Command(tv));teleController.switchCommand(new CCTV3Command(tv));teleController.switchCommand(new CCTV5Command(tv));teleController.switchCommand(new CCTV1Command(tv));teleController.switchCommand(new CCTV6Command(tv));System.out.println("------返回上一个卫视--------");//模拟遥控器返回键teleController.back();teleController.back();

执行结果如下:

--CCTV1--
--CCTV2--
--CCTV4--
--CCTV3--
--CCTV5--
--CCTV1--
--CCTV6--
----------返回上一个卫视-------------
--CCTV1--
--CCTV5--

从上面的例子我们可以看出,命令模式的主要特点就是将请求封装成一个个命令,以命令为参数来进行切换,达到请求参数化的目的,且还能通过集合这个数据结构来存储已经执行的请求,进行回退操作。而且如果需要添加新的电视频道,只需要添加新的命令类即可

而非命令模式中,看电视的人和电视耦合在一起;而新的命令模式则使用一个遥控器就将人和电视机解耦。总让你抱着电视机你也不乐意不是?

结合上述例子,最后用一个图来简单的表示命令模式。博主喜欢“斗图".
这里写图片描述

好了,我对命令模式的理解到这里就结束了,如果大家发现有什么错误的地方,希望能不吝指正。至于命令模式的缺点,如果理解了该模式,他的缺点不是显而易见的吗?博主偷个懒就不再赘述。


http://chatgpt.dhexx.cn/article/4aZqEfUP.shtml

相关文章

《C++ 设计模式》

作者&#xff1a; 一去、二三里 个人微信号&#xff1a; iwaleon 微信公众号&#xff1a; 高效程序员 设计模式&#xff08;Design Pattern&#xff09;代表了最佳的实践&#xff0c;在面向对象的编程中被很多老鸟们反复使用。使用设计模式有很多好处&#xff1a; 可重用代码保…

设计模式课程设计

文章目录 题目要求&#xff1a; 具体作业如下&#xff1a;一、设计思路二、所用模式介绍1.简单工厂模式2.装饰模式3.观察者模式 具体实现过程程序分为三个部分: PS.代码写的比较敷衍&#xff0c;主要是应付作业用&#xff0c;请大家自行斟酌抄袭 又到了一年两度的课程设计时间&…

23种设计模式总结

一、什么是设计模式 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问&#xff0c;设计模式于己于他人于系统都是多赢…

设计模式之——桥接模式

设计模式&#xff1a; 前辈们对代码开发经验的总结&#xff0c;是解决特定问题的一系列套路。它不是语法规定。而是一套用来提高代码可复用性、可维护性、可读性、稳健性、以及安全性的解决方案 设计模式的本质是面向对象设计原则的实际运用&#xff0c;是对类的封装性、继承性…

面向对象程序设计

之前复习面向对象的时候整理的&#xff0c;丢出来一起分享一下。因为复习得很赶&#xff0c;只是大致的整理&#xff0c;且大部分图片来自老师的ppt&#xff0c;可能不是很准确。如果要详细了解其中的某个知识点请另外搜索。 但是老师不讲武德啊&#xff0c;明明提纲给了不按提…

MVC设计模式

MVC的全名是Model View Controller&#xff0c;是模型(Model)&#xff0d;视图(view)&#xff0d;控制器(controller)的缩写&#xff0c;是一种设计模式。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码&#xff0c;将众多的业务逻辑聚集到一个部件里面&#xff0c;在…

设计模式考题复习

一.定义 设计模式六大基本原则&#xff1a; 单一职责原则&#xff1a;一个类或者一个方法只负责一项职责&#xff0c;尽量做到类的只有一个行为原因引起变化&#xff1b;里氏替换原则&#xff1a;能出现子类的地方都应该可以允许父类出现&#xff0c;也就是子类可以扩展父类的…

一文带你通俗理解23种软件设计模式(推荐收藏,适合小白学习,附带C++例程完整源码)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、设计模式是什么&#xff1f; 设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应…

设计模式之策略模式详解

设计模式之策略模式详解 概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c…

设计模式——桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;&#xff0c;也叫做桥梁模式&#xff0c;结构型设计模式的一种&#xff0c;这个模式相对来说有些难理解。桥接&#xff0c;顾名思义&#xff0c;就是用来连接两个部分&#xff0c;为被分离了的抽象部分和实现部分搭桥。 …

设计模式23模式介绍

&#x1f3c6;作者简介&#xff1a;哪吒&#xff0c;CSDN2022博客之星Top1、CSDN2021博客之星Top2、多届新星计划导师✌、博客专家&#x1f4aa; &#xff0c;专注Java硬核干货分享&#xff0c;立志做到Java赛道全网Top N。 &#x1f3c6;本文收录于&#xff0c;Java基础教程系…

程序设计模式23+1种定义+UML图(有部分分析和联用)

程序设计模式这门课已经学完了&#xff0c;复习的时候做了一个这样的汇总&#xff0c;希望可以给后来学习这门课的同学一些帮助。 设计模式的分类 根据目的&#xff08;模式是用来做什么的&#xff09;可分为创建型(Creational)&#xff0c;结构型(Structural)和行为型(Behavio…

程序员必备的21种“设计模式之道”!

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的就是为了重用代码、让代码更容易被他人理解、保证代码可靠性。欢迎小伙伴们收藏关注&#xff0c;持续分享更多优质干货&#xff01; 设计模式之道 何为设计模式&#xff1f…

Microsoft Office Visio 缺失安装文件的解决方法(附viso安装密钥)

工作环境(蓝色粗体字为特别注意内容) 1&#xff0c;软件环境&#xff1a;Windows 7、Microsoft Office Visio Professional 2007、Microsoft Office Enterprise2007 2&#xff0c;参考文献&#xff1a;https://zhidao.baidu.com/question/1669305667489578747.html 最近在写文…

Microsoft visio 2019 professional 安装

Visio 2019 pro&#xff0c;全程关闭防火墙&#xff0c;运行文件采用管理员运行 Visio 2019 pro 下载jihuo汉化 Visio 2019 pro 下载 从下方链接下载 链接&#xff1a;https://pan.baidu.com/s/1OQXUfOItLJhbKsDf_u3pEQ 提取码&#xff1a;1234 里面包含&#xff1a; 点击V…

Oracle高水位线 HWM降低技巧

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;目前从事DBA及程序编程 &#xff08;Web\java\Python&#xff09;工作&#xff0c;主要服务于生产制造 现拥有 Oracle 11g OCP/OCM、 Mysql、Oceanbase&#xff08;OBCA&#xff09;认证 分布式TBase\TDSQL数据库、国…

HMM

Author: 李文乐;Email: cocoleYYoutlook.comDatawhale 直观理解 马尔可夫链&#xff08;英语&#xff1a;Markov chain&#xff09;&#xff0c;又称离散时间马尔可夫链&#xff08;discrete-time Markov chain&#xff0c;缩写为DTMC&#xff09;&#xff0c;因俄国数学家安德…

hwui简介

简介&#xff1a; hwui主要是android用于2d硬件绘图而加入的一个模块&#xff0c;在hwui之前&#xff0c;android主要是用skia来进行软件绘制&#xff0c;后由于绘制性能等问题&#xff0c;现在android的绘图几乎都是使用了hwui硬件加速绘图。hwui主要则是使用opengles来进行g…

【深度】广告流量分配HWM算法

在广告投放系统中&#xff0c;广告通常分为保量交付广告&#xff08;Guaranteed Delivery&#xff0c;GD&#xff0c;合约广告&#xff09;和不保量交付&#xff08;Non-Guaranteed Delivery&#xff0c;NGD&#xff0c;竞价广告&#xff09;两种。合约广告是提前签好合约的&am…

Oracle-HWM(High Water Mark) 高水位解读

读前须知&#xff1a;Oracle的逻辑存储管理 ORACLE在逻辑存储上分4个粒度 &#xff0c;由大到小为: 表空间, 段, 区 和 块. 块Block 块:是粒度最小的存储单位,现在标准的块大小是8K,ORACLE每一次I/O操作也是按块来操作的,也就是说当ORACLE从数据文件读数据时,是读取多少个块,而…