设计模式:命令模式
一、命令模式定义
在软件设计系统中,行为的请求者与行为的实现者总是耦合在一起,在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式。在现实生活中,电视遥控器,我们只需要知道按那个按钮能够打开电视、关闭电视和换台即可,并不需要知道是怎么开电视、关电视和换台的,在这个例子中电视机酒是行为的实现者,而遥控器就是行为的请求者,
命令模式:将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。
模式结构
Command:定义命令的接口,声明要执行的方法;
ConcreteCommand:命令接口实现的对象,实现了Command接口中的方法,一般是虚实现,调用接收者的功能来完成要执行的操作;
Receiver:接收者,命令的真正执行对象;
Invoker: 命令的请求者,是命令模式中相当重要的角色,通过Invoker来对各个命令进行控制;
Client:创建具体的命令对象,并设置命令对象的接收者,这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,更确切的Client应该称为装配者,真正的命令的客户端是从Invoker来执行的;
二、命令模式的举例
我们还是以现实生活中的遥控器与电视的例子来实现命令模式;对于电视机内部可以实现很多的功能,比如电视机开机,电视机关机,电视机切换台等功能;对于遥控器中每个按钮的功能我们抽象出一个命令接口;
public interface ICommand { /// <summary> /// 电视机按钮的命令执行 /// </summary> void Execute(); }
命令的执行最终是在电视机上执行,因此在电视机中会有遥控器中命令的真正实现;
public interface ITelevision { void open(); void close(); void changeChannel(); } public class TCLTelevision : ITelevision { public void open() { Console.WriteLine("电视机正在开机......."); } public void close() { Console.WriteLine("电视机正在关机......."); } public void changeChannel() { Console.WriteLine("切换电视频道......"); } }
ITelevision接口就相当于模式结构中的Receiver的角色,而接口中的方法对应的是对应的是遥控器中按钮发出的命令的真正实现;
对于遥控器中的每个按钮都相当于一个命令对象,当遥控器中的按钮被按下相当于一条命令发出,因此所有的按钮都执行都实现ICommand接口;对于OpenCommand,CloseCommand,ChangeChannelCommand 充当了ConcreteCommand的角色;
public class OpenCommand:ICommand { private ITelevision Tv; public OpenCommand(ITelevision _Tv) { Tv = _Tv; } public void Execute() { Tv.open(); } } public class CloseCommand : ICommand { private ITelevision Tv; public CloseCommand(ITelevision _Tv) { Tv = _Tv; } public void Execute() { Tv.close(); } } public class ChangeChannelCommand : ICommand { private ITelevision Tv; public ChangeChannelCommand(ITelevision _Tv) { Tv = _Tv; } public void Execute() { Tv.changeChannel(); } }
对于遥控器是对所有的命令按钮进行控制,当在遥控器中按下开机键时,就执行遥控器中的Open方法,通过SetOpenCommand方法来进行对开机命令进行设置;
public class TvControl { private ICommand openCommand; private ICommand closeCommand; private ICommand changeChannelCommand; public TvControl() { } public void SetOpenCommand(ICommand _openCommand) { this.openCommand = _openCommand; } public void SetCloseCommand(ICommand _closeCommand) { this.closeCommand = _closeCommand; } public void SetChangeChannelCommand(ICommand _changeChannelCommand) { this.changeChannelCommand = _changeChannelCommand; } public void Open() { openCommand.Execute(); } public void Close() { closeCommand.Execute(); } public void ChangeChannel() { changeChannelCommand.Execute(); } }
class Program { static void Main(string[] args) { ITelevision tcltv = new TCLTelevision(); ICommand openCommand = new OpenCommand(tcltv); ICommand closeCommand = new CloseCommand(tcltv); ICommand changeChannelCommand = new ChangeChannelCommand(tcltv); TvControl tvControl = new TvControl(); tvControl.SetOpenCommand(openCommand); tvControl.SetCloseCommand(closeCommand); tvControl.SetChangeChannelCommand(changeChannelCommand); tvControl.Open(); tvControl.ChangeChannel(); tvControl.Close(); Console.ReadLine(); } }
在Program类中,我们对遥控器中的按钮命令进行装配,创建了具体的命令对象(OpenCommand,CloseCommand,ChangeChannelCommand),并且设置了这些命令对象的接收者(TCLTelevision);SAN
三、总结
命令模式的特点
一,命令模式将发出请求的对象(Invoker)和执行请求的对象(Receiver)解耦。
二,发出请求的对象和执行请求的对象之间是通过命令对象进行沟通的。
三,命令对象封装了一个或者是一组动作。
四,命令模式比较容易实现一个命令队列(上面改进后实现一次订单多个命令就是如此)。
五,允许是否要撤销命令的执行(比如点菜后又不点这个菜了)。
六,比较容易实现对请求的撤销和重做(比如我点菜后又不点了,可想了一下还是点这个菜吧)。
七,由于对命令类进行了抽象,所以增加新的具体命令类非常容易。
八,可以比较方便的实现日志请求。
九,命令模式一个比较明显的缺点就是会导致系统有过多的具体命令类(鬼晓得会有多少种命令啊)。
模式优点
1.降低对象之间的耦合对,主要是命令的请求者与命令的执行者;
2.新的命令很容易加入到系统中;
3. 可以比较容易的设计一个组合命令
模式缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。