前言
命令模式,对象行为型模式的一种。它帮助我们将功能的调用者与实现者之间解耦(甚至完全解耦)。调用者与实现者之间并不是直接引用关系,调用者只需要知道如何发送当前功能的请求即可,而不用关心该请求由谁在何时完成。
“ 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。”
——《设计模式 - 可复用的面向对象软件》
结构
- 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)


