前言
命令模式,对象行为型模式的一种。它帮助我们将功能的调用者与实现者之间解耦(甚至完全解耦)。调用者与实现者之间并不是直接引用关系,调用者只需要知道如何发送当前功能的请求即可,而不用关心该请求由谁在何时完成。
“ 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。”
——《设计模式 - 可复用的面向对象软件》
结构
- Command(命令类接口):声明执行操作的接口;
- ConcreteCommand(命令类):Command接口的实现,用来调用具体的实现;
- Invoker(调用者):用来控制命令的执行,个人理解为Command的代理类;
- Receiver(接收者):功能具体的实现,由ConcreteCommand调用;
示例
考虑一个能够控制各种智能家电的App。在这个App中用户可以随意添加按钮来控制某个家电的某个功能。也就是说当我们开发这个App时并不能确定用户添加的按钮是控制什么家电执行什么功能。现在我们使用命令模式来模拟
/// <summary> /// 命令的接收者 /// </summary> public class Television { public void Open() { Console.WriteLine("打开电视机"); } public void Close() { Console.WriteLine("关闭电视机"); } } /// <summary> /// 命令接口 /// </summary> public interface ICommand { void Execute(); } /// <summary> /// 开机命令 /// </summary> public class TvOpenCommand : ICommand { private Television _tv = null; public TvOpenCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Open(); } } /// <summary> /// 关机命令 /// </summary> public class TvCloseCommand : ICommand { private Television _tv = null; public TvCloseCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Close(); } } /// <summary> /// 命令的调用者 /// </summary> public class Button { public ICommand Command { set; get; } public Button(ICommand command) { this.Command = command; } public void Click() { this.Command.Execute(); } } static void Main(string[] args) { Television tv = new Television(); //创建电视机对象(命令的接收者) ICommand tvOpen = new TvOpenCommand(tv); //创建开机命令 ICommand tvClose = new TvCloseCommand(tv); //创建关机命令 Button button = new Button(tvOpen); //创建开机按钮(命令的调用者) button.Click(); //执行命令 button.Command = tvClose; //将按钮功能变更为关机 button.Click(); //执行命令 Console.ReadKey(); }
在上述示例中,Button类充当调用者角色,Television类充当接收者角色。我们为Television类中的每一个函数都创建了命令类,不同的命令类决定了不同的操作,而该操作具体的实现由接收者完成。作为调用者的Button类并不知道它使用了哪个类执行了哪些操作,它只知道在它的Click函数中调用了ICommand接口的Execute函数。这就体现了命令模式的本质,将调用者与接收者解耦。
-
命令宏
在日常生活中,我们往往希望通过一个按钮来执行一系列操作(比如一键打开电视和空调)。这个时候可以将命令模式与组合模式一同使用来实现一个命令宏(又称命令队列)。
/// <summary> /// 命令的接收者 /// </summary> public class Television { public void Open() { Console.WriteLine("打开电视机"); } public void Close() { Console.WriteLine("关闭电视机"); } } /// <summary> /// 命令接收者 /// </summary> public class AirConditioner { public void Open() { Console.WriteLine("打开空调"); } public void Close() { Console.WriteLine("关闭空调"); } } /// <summary> /// 命令接口 /// </summary> public interface ICommand { void Execute(); } /// <summary> /// 开机命令 /// </summary> public class TvOpenCommand : ICommand { private Television _tv = null; public TvOpenCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Open(); } } /// <summary> /// 关机命令 /// </summary> public class TvCloseCommand : ICommand { private Television _tv = null; public TvCloseCommand(Television t { this._tv = tv; } public void Execute() { this._tv.Close(); } } /// <summary> /// 开机命令 /// </summary> public class AcOpenCommand : ICommand { private AirConditioner _ac = null; public AcOpenCommand(AirConditioner ac) { this._ac = ac; } public void Execute() { this._ac.Open(); } } /// <summary> /// 关机命令 /// </summary> public class AcCloseCommand : ICommand { private AirConditioner _ac = null; public AcCloseCommand(AirConditioner ac) { this._ac = ac; } public void Execute() { this._ac.Close(); } } /// <summary> /// 命令宏 /// </summary> public class MacorCommand : ICommand { public List<ICommand> Commands { set; get; } = new List<ICommand>(); public void Execute() { foreach (var command in this.Commands) { command.Execute(); } } } /// <summary> /// 命令的调用者 /// </summary> public class Button { public ICommand Command { set; get; } public Button(ICommand command) { this.Command = command; } public void Click() { this.Command.Execute(); } } static void Main(string[] args) { Television tv = new Television(); //电视对象(命令接收者) AirConditioner ac = new AirConditioner(); //空调对象(命令接收者) TvOpenCommand tvOpenCommand = new TvOpenCommand(tv); //电视开机命令 AcOpenCommand acOpenCommand = new AcOpenCommand(ac); //空调开机命令 MacorCommand macorCommand = new MacorCommand(); //宏命令 macorCommand.Commands.Add(tvOpenCommand); //设置宏命令 macorCommand.Commands.Add(acOpenCommand); //设置宏命令 Button button = new Button(macorCommand); //创建宏按钮 button.Click(); //执行命令 Console.ReadKey(); }
示例中创建一个聚合了ICommand接口的MacorCommand类充当组合模式中的Compsite角色,用来存储一系列的命令。它的存在能够使调用者一次执行多个使用不同接收者的命令。
对于命令模式的扩展还有很多,比如请求日志和逆向操作(撤销)等等。这里就不一一举例了。
总结
命令模式的核心思想是将一个请求封装,将一个请求命令的发出(调用)和接收处理分割开,达到将调用者与接收者解耦的目的。命令模式中的每个命令类都保持了一个很小的颗粒度,因为它只封装了一个接收类中的一个函数。好处是在开发的过程中调用者不必关心也不知道具体执行的函数,保证了调用者与接收者的松耦合状态,以便更好的控制和应对各种变化。同时也意味着令类需要封装的函数越多命令类也就越多,存在类爆炸的风险。
以上,就是我对命令模式的理解,希望对你有所帮助。
示例源码:https://gitee.com/wxingChen/DesignPatternsPractice
系列汇总:https://www.cnblogs.com/wxingchen/p/10031592.html
本文著作权归本人所有,如需转载请标明本文链接(https://www.cnblogs.com/wxingchen/p/10031585.html)