命令模式(Command模式)详解

article/2025/10/20 14:18:08

在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系。这不利于软件功能的扩展与维护。例如,想对行为进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。

在现实生活中,这样的例子也很多,例如,电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者),还有计算机键盘上的“功能键”等。

1. 命令模式的定义与特点

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

命令模式的主要优点如下。

  1. 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  2. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

2. 命令模式的结构与实现

可以将系统中的相关操作抽象成命令,使调用者与实现者相关分离,其结构如下。

2.1 模式的结构

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

  1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  2. 具体命令角色(Concrete    Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

其结构图如图 1 所示。

命令模式的结构图

2.2 模式的实现

命令模式的代码如下:

package command;
public class CommandPattern
{public static void main(String[] args){Command cmd=new ConcreteCommand();Invoker ir=new Invoker(cmd);System.out.println("客户访问调用者的call()方法...");ir.call();}
}
//调用者
class Invoker
{private Command command;public Invoker(Command command){this.command=command;}public void setCommand(Command command){this.command=command;}public void call(){System.out.println("调用者执行命令command...");command.execute();}
}
//抽象命令
interface Command
{public abstract void execute();
}
//具体命令
class ConcreteCommand implements Command
{private Receiver receiver;ConcreteCommand(){receiver=new Receiver();}public void execute(){receiver.action();}
}
//接收者
class Receiver
{public void action(){System.out.println("接收者的action()方法被调用...");}
}

程序的运行结果如下:

客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...

3. 命令模式的应用实例

【例1】用命令模式实现客户去餐馆吃早餐的实例。

分析:客户去餐馆可选择的早餐有肠粉、河粉和馄饨等,客户可向服务员选择以上早餐中的若干种,服务员将客户的请求交给相关的厨师去做。这里的点早餐相当于“命令”,服务员相当于“调用者”,厨师相当于“接收者”,所以用命令模式实现比较合适。

首先,定义一个早餐类(Breakfast),它是抽象命令类,有抽象方法 cooking(),说明要做什么;再定义其子类肠粉类(ChangFen)、馄饨类(HunTun)和河粉类(HeFen),它们是具体命令类,实现早餐类的 cooking() 方法,但它们不会具体做,而是交给具体的厨师去做;具体厨师类有肠粉厨师(ChangFenChef)、馄蚀厨师(HunTunChef)和河粉厨师(HeFenChef),他们是命令的接收者,由于本实例要显示厨师做菜的效果图(点此下载要显示的效果图),所以把每个厨师类定义为 JFrame 的子类;最后,定义服务员类(Waiter),它接收客户的做菜请求,并发出做菜的命令。客户类是通过服务员类来点菜的,图 2 所示是其结构图。

客户在餐馆吃早餐的结构图

程序代码如下:

package command;
import javax.swing.*;
public class CookingCommand
{public static void main(String[] args){Breakfast food1=new ChangFen();Breakfast food2=new HunTun();Breakfast food3=new HeFen();Waiter fwy=new Waiter();fwy.setChangFen(food1);//设置肠粉菜单fwy.setHunTun(food2);  //设置河粉菜单fwy.setHeFen(food3);   //设置馄饨菜单fwy.chooseChangFen();  //选择肠粉fwy.chooseHeFen();     //选择河粉fwy.chooseHunTun();    //选择馄饨}
}
//调用者:服务员
class Waiter
{private Breakfast changFen,hunTun,heFen;public void setChangFen(Breakfast f){changFen=f;}public void setHunTun(Breakfast f){hunTun=f;}public void setHeFen(Breakfast f){heFen=f;}public void chooseChangFen(){changFen.cooking();}public void chooseHunTun(){hunTun.cooking();}public void chooseHeFen(){heFen.cooking();}
}
//抽象命令:早餐
interface Breakfast
{public abstract void cooking();
}
//具体命令:肠粉
class ChangFen implements Breakfast
{private ChangFenChef receiver;ChangFen(){receiver=new ChangFenChef();}public void cooking(){       receiver.cooking();}
}
//具体命令:馄饨
class HunTun implements Breakfast
{private HunTunChef receiver;HunTun(){receiver=new HunTunChef();}public void cooking(){receiver.cooking();}
}
//具体命令:河粉
class HeFen implements Breakfast
{private HeFenChef receiver;HeFen(){receiver=new HeFenChef();}public void cooking(){receiver.cooking();}
}
//接收者:肠粉厨师
class ChangFenChef extends JFrame
{   private static final long serialVersionUID = 1L;JLabel l=new JLabel();ChangFenChef(){super("煮肠粉");l.setIcon(new ImageIcon("src/command/ChangFen.jpg"));this.add(l);this.setLocation(30, 30);this.pack();this.setResizable(false);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   }public void cooking(){this.setVisible(true);}
}
//接收者:馄饨厨师
class HunTunChef extends JFrame
{private static final long serialVersionUID=1L;JLabel l=new JLabel();HunTunChef(){super("煮馄饨");l.setIcon(new ImageIcon("src/command/HunTun.jpg"));this.add(l);this.setLocation(350, 50);this.pack();this.setResizable(false);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    }public void cooking(){this.setVisible(true);}
}
//接收者:河粉厨师
class HeFenChef extends JFrame
{private static final long serialVersionUID=1L;JLabel l=new JLabel();HeFenChef(){super("煮河粉");l.setIcon(new ImageIcon("src/command/HeFen.jpg"));this.add(l);this.setLocation(200, 280);this.pack();this.setResizable(false);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public void cooking(){this.setVisible(true);}
}

4. 命令模式的应用场景

命令模式通常适用于以下场景。

  1. 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
  2. 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
  3. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
  4. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。

5. 命令模式的扩展

在软件开发中,有时将命令模式与前面学的组合模式联合使用,这就构成了宏命令模式,也叫组合命令模式。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行它时将递归调用它所包含的所有命令,其具体结构图如图 3 所示。

组合命令模式的结构图


http://chatgpt.dhexx.cn/article/nZdFFa3w.shtml

相关文章

交换机基本命令模式

交换机基本命令模式 对于思科交换机来说,主要有2种配置途径: 其一,使用交换机自带的Console线缆连接到计算机的COM口,然后利用计算机的超级终端软件直接配置,首次配置通常使用这种方式; 其二,通…

Java设计模式及应用场景之《命令模式》

文章目录 一、命令模式定义二、命令模式的结构和说明三、命令模式示例四、命令模式扩展 -- 宏命令示例五、命令模式扩展 -- 可撤销和恢复操作示例1、反操作式(补偿式)2、存储恢复式 六、命令模式扩展 -- 队列请求七、命令模式扩展 -- 日志请求八、命令模…

【每天一个java设计模式(十五)】 - 命令模式

命令模式是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。 命令模式也就是一个用户发送请求&…

码农小汪-设计模式之-命令模式

大话设计模式的例子讲的非常的好,理解起来也方便!有时候忘了。想到这些特殊的例子感觉就是特别爽。 烤羊肉串带来的思考! 路边摊羊肉串: 老板,我这里排的比较先啊,我最先给钱。老板这个没有熟啊。我的是…

命令模式(行为型)

一、什么是命令式 命令(Command)模式又叫作动作(Action)模式或事务(Transaction)模式,是一种对象的行为模式。将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作…

命令模式---电视机遥控器

电视机是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应电视机的不同操作。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作&#…

C++设计模式-命令模式

目录 基本概念 代码与实例 基本概念 命令模式(Command),将一个请求封装为对象,从而使你看用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销操作。 命令模式的作用: …

设计模式---命令模式

命令模式 命令模式的定义 ​ 命令模式是一个高内聚的模式,其定义为:Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.(将一…

【设计模式】命令模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

文章目录 一、命令模式简介二、命令模式 适用场景三、命令模式 优缺点四、命令模式 与 备忘录模式五、命令模式 代码示例1、命令接口2、发布命令类3、关闭命令类4、游戏类5、命令执行者类6、测试类 一、命令模式简介 命令模式 : 将 不同的请求 封装成 不同的请求对象 , 以便 使…

设计模式(16)命令模式

**定义:**将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。 **类型:**行为类模式 类图: 命令模式的结构 ​ 顾名思义&#…

什么是命令模式?

一、命令模式的定义 命令是对命令的封装,每一个命令都是一个操作,请求方发出请求,接收方接收请求,并执行操作。命令模式解耦了请求方和接收方,命令模式属于行为型模式 二、命令模式的uml图和通用写法 uml 通用写法 …

设计模式之命令模式详解

1 概述 日常生活中,我们出去吃饭都会遇到下面的场景。我们可以将女招待理解成一个请求的发送者,用户通过它来发送一个“点餐”请求,而厨师是“点餐”请求的最终接收者和处理者,在图中,顾客和厨师之间并不存在直接耦合…

命令模式

一、命令模式介绍 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个。我们只需要在程序运行时指定具体的请求接收者即可,此时可以使用命令模式来设计。 命令模式使得请求发…

Java设计模式——命令模式

文章目录 命令模式 命令模式 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行…

如何设置IPv4和IPv6报文的DSCP值——网络测试仪实操

一、操作说明 在QoS测试中,经常要设置不同优先级的报文,来验证被测设备对于优先级的调度。所以,我们就要了解如何设置IPv6和IPv6报文中的DSCP(大部分使用DSCP值,也会用到TOS值) 这里我们使用测试接交换机&…

DSCP vs IPv4 Tos

首先看IPv4包头如下 其中,Qos用到的是Tos定义有下面两种: 老的IPv4 TOS Byte定义和值 新的DSCP定义和值 DSCP值 DSCP ValueMeaningDrop ProbabilityEquivalent IP Precedence Value101 110 (46)High Priority Expedited Forwarding (EF)N/A101 – …

c语言socket设置IPV4/6的dscp值

环境&#xff1a;linux centos7 、x86 、UDP包 使用sock需要增加头文件 #include <sys/socket.h> #include <sys/types.h> 设置方法很简单&#xff0c;都是使用setsockopt函数&#xff0c;就是找资料及如何太麻烦&#xff0c;尤其是IPV6。需要注意IPV4设置的是I…

tos cos dscp 区别和作用

tos cos 和dscp 都是通过iptable 的mange 的mark 标签来更改的。 谈到qos首先需要了解qos调度的几个重要过程,qos调度过程包括网络入口数据流量的分类和标记、骨干网设备上的拥塞避免和拥塞管理、网路出口的队列调度这几个重要过程. 1、cos和tos的区别: 通过acl对流量进行分类以…

IP优先级和DSCP之间的关系

1. IP优先级和DSCP之间的关系 DiffServ体系定义的DS字段&#xff0c;取代IPv4中ToS字段作出有关数据包分类和流量调节功能的策略。 1.1. ToS字段 在IPv4的报文头中&#xff0c;TOS字段是1字节&#xff0c;根据RFC1122的定义&#xff0c;IP优先级&#xff08;IPPrecedence&…

802.1P优先级、IP优先级、TOS优先级及DSCP优先级的分类和对应

1、802.1P优先级&#xff08;有时也称COS优先级&#xff09;&#xff1a; 802.1p用户优先级定义在二层802.1Q 标签头中的TCI字段中。&#xff0c;和VLAN ID一起使用&#xff0c;位于高位起16-18bit字段&#xff0c;长度3bit&#xff0c;取值范围0-7&#xff0c;0优先级最低&…