命令模式(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. 【白话设计模式八】命令模式(Command)

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factory) [白话设计模式二] ...

  2. Head First 设计模式中的命令模式 的一个错误

    最近在看Head First 设计模式,其中命令模式中有讲到实现撤销功能,并且作者还出了一道题, 下面的是书中习题: public class MarcoCommand implements Comm ...

  3. 经典的设计模式14——命令模式

    文章目录 命令模式 命令模式 定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储.传递.调用.增加与管理. 结构: 抽 ...

  4. 《Head First 设计模式》之命令模式——遥控器

    命令模式(Command) --将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 要点 将发出请求的对象和执行请求的对象解耦. ...

  5. Head First 设计模式总结(六) 命令模式

    本文总结了<Head First 设计模式>中的命令模式 命令模式--将请求封装成"对象",以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持撤销操作. ...

  6. 【源码分析设计模式 13】命令模式

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

  7. 设计模式笔记:命令模式

    首先看看命令模式的定义:命令模式将请求封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持撤销的操作. 所谓参数化,我的理解是实际执行的对象,比如light(电灯).strer ...

  8. Head First 设计模式笔记 5.命令模式

    文章目录 设计要求 命令模式 实际实现 命令模式具体应用 设计要求 小明接到了新任务,要求设计一个遥控器,这个遥控器上面有七个插槽,每个插槽都有对应的开和关按钮.要求每个都能控制任意家电如灯,电视,电 ...

  9. [设计模式随意链接]——命令模式

    1. 为什么要有命令模式 软件构建过程中.行为请求者与行为调用者可能有强耦合的情况.如下所示 if (type == a) { do_a() } else if (type == b) { do_b( ...

  10. 设计模式学习笔记——命令模式(Command)

    1.特点:将请求发送者与具体实现者解耦,可对请求排列.取消.重做,支持事务.(多请求,单处理) 2.概念:属于对象的行为模式[GOF95].命令模式又称为行动(Action)模式或交易(Transac ...

最新文章

  1. 舍友清华博士毕业,我建议他留在高校
  2. isight参数优化理论与实例详解_详解oracle数据库优化参数--cursor_sharing
  3. python s d是什意思_python里d是什么意思
  4. 【LeetCode笔记】6. Z字形变化(JAVA、思路)
  5. 关于Linux网卡调优之:RPS (Receive Packet Steering)
  6. 杭电1411 校庆神秘建筑
  7. lr_save_var() 截取任意字符串长度,以参数形式输出(参数转变量)
  8. 人工智能发展简史, 没想到17世纪AI就出现了!
  9. Python2.6+PyQt4.8+eric4+汉化+开发教程
  10. matlab空间面板门槛,学习笔记——面板门槛模型
  11. python爬取去哪儿网机票_去哪儿网机票爬虫
  12. python打开其他应用程序错误_Python应用程序错误(Udacity)
  13. 【WPS表格】快速填充数据的多种方法
  14. 关于如何有效锻炼肌肉的十个小贴士(转)
  15. 想转行做数据产品经理?这份书单赶紧收藏起来
  16. softmax,softmax loss和交叉熵的关系
  17. (转)AndroidManifest 清单文件合并时出现 【quires a placeholder substitution but no value for is provided.】问题
  18. C++ Primer Plus (第六版)编程练习记录(chapter7)
  19. 天刀服务器维护时间,《天涯明月刀》2021年3月10日服务器例行维护公告 服务器例行维护怎么样...
  20. 达梦数据库——限制IP以及网段连接数据库

热门文章

  1. Linux sed命令高级用法精讲
  2. 枚举数据类型 c# 114866833
  3. 案例 银行取款 java 1615136927
  4. Date java 1614619219
  5. 获取随机数的方式Random类对象的方法 java 0913
  6. 前端开发 个人简历的制作 0228
  7. jquery-模态框的显示与消失操作
  8. TiDB DM部署及使用
  9. tar.gz及tar.bz2两种常见格式的打包压缩及解压方法
  10. MySQL 大表优化方案,收藏了细看!