Command
命令模式:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
别名:动作模式、事务模式
命令模式主要的目的就是将命令的发送者与接收者进行解耦,发送者只负责发送命令即可,对于具体怎么执行,发送者无需关心,全由接受者决定。这样使得调用更加的灵活。而且当一个命令由多个发送者(可以理解为多个快捷键)触发的时候,那么命令模式在一定的程度上还可以提高代码的复用程度。
命令模式涉及到五个角色,他们分别是:

  • 客户端角色(Client):创建一个具体命令ConcreteCommand对象并确定其接收者。
  • 抽象命令角色(ICommand): 声明一个给所有具体命令类的抽象接口。
  • 具体命令角色(ConcreteCommand):定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
  • 调用者/发送者/请求者角色(Invoker):负责调用命令对象执行请求,相关的方法叫做行动方法。通常会将命令聚合在类中。
  • 实现者/接收者角色(Receiver):负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

UML图如下:
image.png

1、场景描述

以电视遥控器为例,对电视机进行开关操作。用命令模式来实现。

2、实现

  1. /// <summary>
  2. /// 命令接收者-电视机
  3. /// </summary>
  4. public class Television
  5. {
  6. public void Start()
  7. {
  8. Console.WriteLine("电视开机");
  9. }
  10. public void Close()
  11. {
  12. Console.WriteLine("电视关机");
  13. }
  14. }
  15. /// <summary>
  16. /// 抽象命令类
  17. /// </summary>
  18. public interface ICommand
  19. {
  20. /// <summary>
  21. /// 命令执行
  22. /// </summary>
  23. public void Excute();
  24. }
  25. /// <summary>
  26. /// 具体命令-电视开机命令
  27. /// </summary>
  28. public class StartCommand : ICommand
  29. {
  30. private readonly Television _tv;
  31. public StartCommand(Television tv)
  32. {
  33. this._tv = tv;
  34. }
  35. /// <summary>
  36. /// 开机
  37. /// </summary>
  38. public void Excute()
  39. {
  40. _tv.Start();
  41. }
  42. }
  43. /// <summary>
  44. /// 具体命令-电视关机命令
  45. /// </summary>
  46. public class CloseCommand : ICommand
  47. {
  48. private readonly Television _tv;
  49. public CloseCommand(Television tv)
  50. {
  51. this._tv = tv;
  52. }
  53. /// <summary>
  54. /// 关机
  55. /// </summary>
  56. public void Excute()
  57. {
  58. _tv.Close();
  59. }
  60. }
  61. /// <summary>
  62. /// 命令发送者--电视遥控器
  63. /// </summary>
  64. public class RemoteControl
  65. {
  66. private readonly ICommand _command;
  67. public RemoteControl(ICommand command)
  68. {
  69. this._command = command;
  70. }
  71. /// <summary>
  72. /// 发送命令
  73. /// </summary>
  74. public void Action()
  75. {
  76. _command.Excute();
  77. }
  78. }
  79. // 客户端调用
  80. Television tv = new Television();
  81. ICommand startCommand = new StartCommand(tv);
  82. RemoteControl control = new RemoteControl(startCommand);
  83. control.Action();
  84. Console.WriteLine("*************************");
  85. ICommand closeCommand = new CloseCommand(tv);
  86. RemoteControl control1 = new RemoteControl(closeCommand);
  87. control1.Action();

3、优缺点

优点:

  • 降低系统各模块之间的耦合度,这也是命令模式的目的
  • 满足开闭原则,动态增加、删除命令,对原有功能不产生影响
  • 可以实现执行撤销操作,上面的例子没有撤销,其实很简单,就是在撤销方法里面执行想发的方法即可
  • 可以在执行命令的同时增加额外的功能。比如上面例子中,在StartCommand执行 _tv.Start()之前,还可以增加日志记录等操作。

缺点:

  • 如果接收值执行方法过多,可能产生大量的命令类,难以维护
  • 相比直接调用,命令模式增加了理解上的难度

    4、应用场景

  • 系统需要将请求调用者和请求接收者解耦

  • 系统需要将请求排队
  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作
  • 系统需要将一组操作组合在一起,即支持宏命令(组合命令)