首页 > 编程 > .NET > 正文

.NET中的设计模式四:命令模式

2024-07-10 12:56:40
字体:
来源:转载
供稿:网友
命令模式(command)结构并不复杂,简单的说,就是将某些职责封装成对象,对象的激发者(invoker)和接收者(reciever)都不过问命令的执行过程。用这样的方式解除了激发者和接受者之间的耦合,为程序提供了更清晰的层次结构。实际运用中经常是将系统中的命令抽象成一个接口,所有的命令都是这个接口的实现,如下所示:


command类是icommand的实现,command类实现了execute方法,其他命令类都是command方法的子类。这样激发者接受者和接受者面临的环境就非常简单了:统一作为command类型就可以处理了。

最简单的命令模式的实现代码如下:

///定义command接口

public interface icommand

{

void execute();

}



///实现icommand接口

///command类是所有命令的基类,在这里实现一些命令的共同处理过程

public abstract class command : icommand

{

public virtual void execute()

{

}

//实现其他的方法....

}



///实现一个具体的command1

public class command1 : command

{

public void override execute()

{

//具体的处理过程

}

}



///实现一个具体的command2

public class command2 : command

{

public void override execute()

{

//具体的处理过程

}

}



///实现一个具体的command3

public class command3 : command

{

public void override execute()

{

//具体的处理过程

}

}


命令模式的实际应用

下面从一个系统实例看一看命令模式的应用。reportingservice是一个报表系统的一部分,是一个运行在windows系统上的service程序,所需要完成的功能是:每天晚上11点到凌晨2点,将数据库中的大批量的数据生成数百张报表。报表的分布较为复杂,有的要求在web服务器上发布报表,有的要求将报表数据输出成一个文本文件,还有的要将报表保存在数据库系统中。

系统的设计运用了命令模式,其中的一部分结构如下:



程序中定义了icommand接口,实现了多个command类(这里只画出3个表示示例),分别执行各个报表的生成任务。command类提供了下列接口:

分类 名称 类型 说明
属性 finished bool 表示任务是否已经进行完毕
属性 context iapplicationcontext 任务执行需要的环境和资源,包括数据库连接、文件系统等等
方法 execute void 执行任务

service的主线程每天定时将系统运行环境进行初始化,建立一个applicationcontext类的实例(application是一个singlton模式的类,图中没有体现),这个类中保存了command运行需要使用的全部资源。然后初始化一个command对象列表,逐个执行每一个command。这样一来,复杂的业务逻辑就从程序的主框架中分离开了,service的主线程所需要做的全部工作就是协调资源的分配和异常的处理,还可以不断检查命令执行的状态,看看finished属性是否为真。如果执行完以后发现某些命令执行不成功,要做相应的处理。

程序采用了单元测试的开发方式,在对程序主线程进行测试的时候,使用了一个虚拟的command实现。测试每一个command类的时候则为每个command实现了虚拟的applicationcontext。这样的结构为单元测试提供了很多便利。关于提高程序的可测试性可以参见我的另一篇文章:怎样测试代码中难测试的部分。

命令模式和其他模式的结合

为了给command的建立提供一个整洁简单的方式,command模式经常和工厂模式(factory)结合使用,采用一个工厂对command对象的建立进行管理,减少命令激发者与命令对象之间的耦合程度。

在窗体程序中,command模式还经常与组合模式(composition)结合使用。可以在初始化窗体的时候,将画面上的菜单项、工具栏、按钮等控件与相应的command进行组合,这样在控件点击时,调用对应的command,即可实现对命令的处理。

在《程序员》杂志本年第10月期介绍了一个利用command模式实现程序中undo功能的文章。程序中为每个command定义了“执行”和“撤销”两种操作,如下:



图中的action就是commamnd对象,只是名称不同,意义是一样的。action对象存在do和undo方法,分别是“执行”和“撤销”方法。可以将执行完毕的action对象的done属性设置为true,再将其保存在一个列表中——actionlist。在actionlist中找到最后一个done属性为true的action,执行其undo方法,即可实现“撤销”的操作。

命令类的序列化

如果将command基类及其每一个子类都进行序列化,可以实现更多的强大的功能。程序执行过程中,如果发生异常,比如网络发生中断、文件丢失等异常,可以将没有执行完毕的command序列化保存下来。等待可以执行的时候再将这些对象重新加载,命令可以继续执行。甚至用户已经重新启动了计算机,命令也可以继续执行。command对象的序列化也为分布式的操作提供了便利。在网络环境中,可以在某处建立一个command对象,将其序列化后传输到网络上另一台计算机上进行执行。这种情况下不要忘记异常类的序列化,如果自己定义了exception的子类,也要将其进行序列化,以便在网络环境中捕捉和处理command执行时发生的异常。microsoft网站上有一篇文章介绍.net中对象的序列化,详细的介绍了对象序列化的概念。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表