命令模式(Command Pattern)

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/413 访问。

命令模式属于行为型模式,它尝试将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

在该设计模式中,请求以命令的形式包裹在对象中并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象请求执行。

角色:

1、抽象命令(Command)

定义命令的接口,声明命令执行的方法;

2、具体命令(Concrete Command)

命令接口实现对象,需要维持对接收者的引用,并调用接收者的功能来完成命令要执行的操作;

3、接收者(Receiver)

真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能;

4、调用者(Invoker)

要求命令对象执行请求,需要维持命令对象的引用,可以持有很多的命令对象。

示例:

命名空间CommandPattern中包含Command基类、发票开具命令类CreateCommand、发票作废命令类CancelCommand、发票打印命令类PrintCommand、命令参数基类CommandArgs、发票开具命令参数类CommandArgs、发票作废命令参数类CancelArgs、发票打印命令参数类PrintArgs、接收者类Receiver和调用者类Invoker。本命尝试通过客户端调用不同的参数化发票命令来使调用者调用不同的功能。

namespace CommandPattern
public abstract class Command {

    protected Receiver _receiver = null;

    protected CommandArgs _commandArgs = null;

    public Command(Receiver receiver, CommandArgs commandArgs) {
this._receiver = receiver;
this._commandArgs = commandArgs;
} public abstract void Action(); }

抽象命令基类,包含Action动作执行命令,并且维持对接受者和命令参数的引用。

public class CreateCommand : Command {

    public CreateCommand(Receiver receiver, CommandArgs commandArgs)
: base(receiver, commandArgs) { } public override void Action() {
_receiver.CommandArgs = _commandArgs;
(_receiver as CreateReceiver)?.CreateInvoice();
} }

这是发票开具命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用CreateInvoice方法来开具一张发票。

public class CancelCommand : Command {

    public CancelCommand(Receiver receiver, CommandArgs commandArgs)
: base(receiver, commandArgs) { } public override void Action() {
_receiver.CommandArgs = _commandArgs;
(_receiver as CancelReceiver)?.CancelInvoice();
} }

这是发票作废命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用CancelInvoice方法来作废一张发票。

public class PrintCommand : Command {

    public PrintCommand(Receiver receiver, CommandArgs commandArgs)
: base(receiver, commandArgs) { } public override void Action() {
_receiver.CommandArgs = _commandArgs;
(_receiver as PrintReceiver)?.PrintInvoice();
} }

这是发票打印命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用PrintInvoice方法来打印一张发票。

public class CommandArgs {

    public string InvoiceType { get; set; }

}
public class CreateArgs : CommandArgs {

    public DateTime BillingDate { get; set; }

}
public class CancelArgs : CommandArgs {

    public string InvoiceCode { get; set; }
public int InvoiceNumber { get; set; }
public string CancelReason { get; set; }
public string CancelMan { get; set; }
public DateTime CancelDate { get; set; } }
public class PrintArgs : CommandArgs {

    public string InvoiceCode { get; set; }
public int InvoiceNumber { get; set; } }

参数化的命令参数基类CommandArgs和它的3个具体实现类。实际开发过程中可以将参数化命令信息封装在具体命令类中,本例为了更好的扩展性,将参数化命令信息抽象出来。

public class Invoker {

    private Command _command = null;

    public Invoker(Command command) {
this._command = command;
} public void Execute() {
_command.Action();
} }

调用者类Invoker,实际开发中这个应为具体的调用类。例如我们需要从MQ获取实时数据,并根据从MQ获取到的JSON数据来处理不同的命令,那么这个调用者类应该为MQ所在的管理类(假如名为ActiveMQManager)。这时我们需要在ActiveMQManager类中维持对命令基类的引用,并在收到不同的JSON数据时解析出相应命令和命令参数信息,然后执行命令中的Action方法。

public abstract class Receiver {

    public CommandArgs CommandArgs { get; set; }

    protected const string LINE_BREAK =
"-------------------------" +
"-------------------------";
//文章排版需要,故折成2行 }
public class CreateReceiver : Receiver {

    public void CreateInvoice() {
var args = CommandArgs as CreateArgs;
if (args == null) throw new InvalidOperationException();
Console.WriteLine("Create Invoice!");
Console.WriteLine(
$"InvoiceType is {args.InvoiceType},{Environment.NewLine}" +
$"BillingDate is {args.BillingDate.ToString("yyyy-MM-dd HH:mm:ss")}!");
Console.WriteLine(LINE_BREAK);
} }
public class CancelReceiver : Receiver {

    public void CancelInvoice() {
var args = CommandArgs as CancelArgs;
if (args == null) throw new InvalidOperationException();
Console.WriteLine("Cancel Invoice!");
Console.WriteLine(
$"InvoiceCode is {args.InvoiceCode},{Environment.NewLine}" +
$"InvoiceNumber is {args.InvoiceNumber},{Environment.NewLine}" +
$"InvoiceType is {args.InvoiceType},{Environment.NewLine}" +
$"CancelReason is {args.CancelReason},{Environment.NewLine}" +
$"CancelMan is {args.CancelMan},{Environment.NewLine}" +
$"CancelDate is {args.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}!");
Console.WriteLine(LINE_BREAK);
} }
public class PrintReceiver : Receiver {

    public void PrintInvoice() {
var args = CommandArgs as PrintArgs;
if (args == null) throw new InvalidOperationException();
Console.WriteLine("Print Invoice!");
Console.WriteLine(
$"InvoiceCode is {args.InvoiceCode},{Environment.NewLine}" +
$"InvoiceNumber is {args.InvoiceNumber},{Environment.NewLine}" +
$"InvoiceType is {args.InvoiceType}!");
Console.WriteLine(LINE_BREAK);
} }

接收者基类Receiver和它的3个具体接收者类,需要维持对命令参数基类的引用,以便我们可以获取相应信息。接收者基类并不是命令模式必须的,但考虑到里氏替换原则和开闭原则,我们引入接收者基类并在不同的实现类里解耦不同的命令操作。

public class Program {

    private static Receiver _receiver = null;

    public static void Main(string[] args) {
_receiver = new CreateReceiver();
Command command = new CreateCommand(
_receiver, new CreateArgs {
InvoiceType = "004",
BillingDate = DateTime.UtcNow
}); var invoker = new Invoker(command);
invoker.Execute(); _receiver = new CancelReceiver();
command = new CancelCommand(
_receiver, new CancelArgs {
InvoiceCode = "310987289304",
InvoiceNumber = 34156934,
InvoiceType = "007",
CancelReason = "Invoice missing!",
CancelMan = "Iori",
CancelDate = DateTime.UtcNow
}); invoker = new Invoker(command);
invoker.Execute(); _receiver = new PrintReceiver();
command = new PrintCommand(
_receiver, new PrintArgs {
InvoiceCode = "310987289304",
InvoiceNumber = 34156934,
InvoiceType = "026"
}); invoker = new Invoker(command);
invoker.Execute(); Console.ReadKey();
} }

以上是为了测试本案例所编写的代码,通过不同的命令并提供额外的参数化命令信息来执行不同的功能。以下是这个案例的输出结果:

Create Invoice!
InvoiceType is 004,
BillingDate is 2018-07-19 05:34:45!
--------------------------------------------------
Cancel Invoice!
InvoiceCode is 310987289304,
InvoiceNumber is 34156934,
InvoiceType is 007,
CancelReason is Invoice missing!,
CancelMan is Iori,
CancelDate is 2018-07-19 05:34:45!
--------------------------------------------------
Print Invoice!
InvoiceCode is 310987289304,
InvoiceNumber is 34156934,
InvoiceType is 026!
--------------------------------------------------

优点:

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/413 访问。

1、降低对象之间的耦合度,通过参数化的命令信息来驱动程序的运行;

2、新的命令可以很容易地加入到系统中;

3、可以比较容易地设计一个组合命令;

4、调用同一方法实现不同的功能。

缺点:

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

使用场景:

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

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

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

C#设计模式之14-命令模式的更多相关文章

  1. [设计模式] 14 命令模式 Command

    Command 模式通过将请求封装到一个对象(Command)中,并将请求的接受者存放到具体的 ConcreteCommand 类中(Receiver)中,从而实现调用操作的对象和操作的具体实现者之间 ...

  2. C#设计模式学习笔记:(14)命令模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7873322.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第二个模式--命 ...

  3. 设计模式学习之命令模式(Command,行为型模式)(12)

    一.命令模式的定义 命令模式属于对象的行为型模式.命令模式是把一个操作或者行为抽象为一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开.命令模式的实现可以提供命令的撤销和恢复功能 ...

  4. 14.命令模式(Command Pattern)

    耦合与变化:    耦合是软件不能抵御变化灾难的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系.                                ...

  5. Java设计模式学习记录-命令模式

    前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...

  6. java设计模式-----23、命令模式

    概念: Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数. 命令模式(Command Pattern)是一种 ...

  7. c#设计模式系列:命令模式(Command Pattern)

    引言 命令模式,我感觉"命令"就是任务,执行了命令就完成了一个任务.或者说,命令是任务,我们再从这个名字上并不知道命令的发出者和接受者分别是谁,为什么呢?因为我们并不关心他们是谁, ...

  8. 大话设计模式Python实现-命令模式

    命令模式(Command Pattern):将请求封装成对象,从而使可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤消的操作. 下面是一个命令模式的demo: #!/usr/bi ...

  9. 《Head first设计模式》之命令模式

    命令模式将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 一个家电公司想邀请你设计一个家电自动化遥控器的API.这个遥控器有7个 ...

  10. 重学 Java 设计模式:实战命令模式「模拟高档餐厅八大菜系,小二点单厨师烹饪场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 持之以恒的重要性 初学编程往往都很懵,几乎在学习的过程中会遇到 ...

随机推荐

  1. Serverless的概念&定义-无服务计算详解

    过去几年间,Serverless 发展迅猛,与其相伴的还有从小程序.移动端等到前后端一体化的演进与实践,也正因如此,从云计算到前端,众多开发者都极为关注 Serverless到底是什么? 在国内,Se ...

  2. 将一个Linux系统中的文件或文件夹复制到另一台Linux服务器上(scp的使用)

    一.复制文件: (1)将本地文件拷贝到远程scp 文件名 用户名@计算机IP或者计算机名称:远程路径(2)从远程将文件拷回本地scp 用户名@计算机IP或者计算机名称:文件名 本地路径 二.复制文件夹 ...

  3. XML转Bean

    XML转Bean有很多方式,我使用的是xtream方式实现xml与bean的互转. 下面是简单的xml转bean /** * XML转换成bean * @param obj * @return yuy ...

  4. python-socket网络编程笔记(UDP+TCP)

    端口 在linux系统中,有65536(2的16次方)个端口,分为: 知名端口(Well Known Ports):0-1023,如80端口分配给HTTP服务,21端口分配给FTP服务. 动态端口(D ...

  5. 教孩子学编程 python语言版PDF高清完整版免费下载|百度云盘|Python入门

    百度云盘:教孩子学编程 python语言版PDF高清完整版免费下载 提取码:mnma 内容简介 本书属于no starch的经典系列之一,英文版在美国受到读者欢迎.本书全彩印刷,寓教于乐,易于学习:读 ...

  6. Nginx安全优化与性能调优

    目录 Nginx基本安全优化 隐藏Nginx软件版本号信息 更改源码隐藏Nginx软件名及版本号 修改Nginx服务的默认用户 修改参数优化Nginx服务性能 优化Nginx服务的worker进程数 ...

  7. 常见的HTTP返回状态值

    200 (成功) 服务器已成功处理了请求. 通常,这表示服务器提供了请求的网页. 301 (永久移动) 请求的网页已永久移动到新位置. 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自 ...

  8. 前端需要知道的 HTML5 SEO优化

    title标签,字数不能过长(不超过70个字符最优) <title>淘宝网 - 淘!我喜欢</title> 使用description以及keyword标签(不超过300个字符 ...

  9. zookeeper代替eureka与springcloud整合

    注册中心 zookeeper: zookeeper是一个分布式协调工具,可以实现注册中心功能 关闭Linux服务器防火墙后启动zookeeper服务器 zookeeper服务器取代Eureka服务器, ...

  10. pandas_处理异常值缺失值重复值数据差分

    # 处理异常值缺失值重复值数据差分 import pandas as pd import numpy as np import copy # 设置列对齐 pd.set_option("dis ...