设计模式——命令模式
什么是命令模式
将请求封装成对象,这可以让你使用不同的请求、队列,或者日志请求来参数化其他对象。命令模式也可以支持撤销操作。
当需要将发出请求的对象和执行请求的对象解耦的时候,可以考虑使用命令模式。
命令模式的角色
Command: 抽象命令类
ConcreteCommand: 具体命令类
Invoker: 调用者
Receiver: 接收者
Client:客户类
实例(这里偷懒一下,直接搬运了经典实现):
/// <summary>
/// Command角色
/// </summary>
public interface ICommand
{
void Execute();
}
/// <summary>
/// ConcreteCommand角色A
/// </summary>
public class ConcreteCommandA : ICommand
{
private Receiver receiver = null;
public ConcreteCommandA(Receiver receiver)
{
this.receiver = receiver;
}
public void Execute()
{
this.receiver.DoA();
}
}
/// <summary>
/// ConcreteCommand角色B
/// </summary>
public class ConcreteCommandB : ICommand
{
private Receiver receiver = null;
public ConcreteCommandB(Receiver receiver)
{
this.receiver = receiver;
}
public void Execute()
{
this.receiver.DoB();
}
}
/// <summary>
/// Receiver角色
/// </summary>
public class Receiver
{
public void DoA()
{
//DoSomething
}
public void DoB()
{
//DoSomething
}
}
/// <summary>
/// Invoker角色
/// </summary>
public class Invoker
{
private ICommand command = null;
//设置命令
public void SetCommand(ICommand command)
{
this.command = command;
}
//执行命令
public void RunCommand()
{
command.Execute();
}
}
/// <summary>
/// 客户端调用
/// </summary>
public class Client
{
public Client()
{
Receiver receiver = new Receiver();
Invoker invoker = new Invoker();
invoker.SetCommand(new ConcreteCommandA(receiver));
invoker.RunCommand();
invoker.SetCommand(new ConcreteCommandB(receiver));
invoker.RunCommand();
}
}
第一次看到这个实现,给我的感觉是繁杂,直接使用下列代码,不是更加简洁直观吗?
public class Client
{
public Client()
{
Receiver receiver = new Receiver();
receiver.DoA();
receiver.DoB();
}
}
但是这么写,其实是将命令的请求者与命令的执行过程绑定在了一起,这是一种耦合,上述代码抽象一下就是下图:
我们将命令的执行写在了客户端的代码里,这意味着客户需要自己编写大量的命令逻辑代码,繁琐且容易出错,为了绕开这种弊端,我们可以对命令的执行进行封装,即:
这样,客户就不需要编写大量命令逻辑代码了,但这又会有问题:
可以看到,两个负责命令执行的类都执行了命令A,意味着有些代码是重复编写了,那我们在改进一下:
至此,看起来似乎没有什么问题了,但是,如果我们想在所有命令执行之前或是之后都进行一些统一的操作,例如写入日志等操作,把这些操作写入到命令执行者或是命令中?不合适,因为这会导致类的职责混乱,命令只负责完成一些动作,命令执行者只负责调用命令,那么我们在改一下:
命令执行前后的一些操作都可以通过命令调用者来执行,并且,命令调用者向client屏蔽了命令执行的细节(不在由client直接调用命令执行者执行命令),client不知道自己的命令什么时候被执行,有没有被执行,封装性得到提高,到此,看起来似乎很完美,但是还有一点美中不足的地方,上述结构中,有多少个命令执行者,命令调用者就要持有多少个命令执行者,代码编写繁琐且不易于扩展,那我们在改一下:
至此,命令模式的雏形就出来了,命令调用者只需要含有一个接口即可,接口的存在使整个系统容易扩展,满足开闭原则,客户端自己决定调用什么命令执行者,提供命令对象给命令执行者,通过命令调用者来调用命令,完成自己所需要的功能,之前逛帖子时看到一个这样的问题——为什么要把命令对象暴露给客户端?我自己的理解是命令本身是有参数的,例如sql语句,表明、字段这些都是可以变化的,这些和命令本身有关,而命令执行者只负责执行命令,不负责命令的初始化,因此有必要把命令本身暴露出来,让客户端自己初始化
上一篇: 数据库-PHP连接到服务器的问题