关机的实现:关机也是调用Windows API 实现的,具体代码参考上次列出的清单
PInvokeService.DoExitWin(int) 函数。
值得一提的是 关机对话框调用了 Windows 中一个未公开的 API, 我们不知道这个函数
的名字,但是我们知道它的地址,我使用 ISO C++ 进行了封装,代码如下:
2
3
编译生成的 dll 是 CPPCode.Shutdown.dll
然后你可以在 PInvokeService.cs 里面找到这样的代码:
[DllImport("CPPCode.Shutdown.dll", ExactSpelling = true, SetLastError = true)]
public static extern void ShowShutdownDialog();
抛开具体细节讨论抽象,现在可以开始正式讲本次的内容了。
Mediator模式的应用
动机(Motivation):
在软件的构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种
复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
在这种情况下,我们可以使用一个“中介对象”来管理对象间的关联关系,避免相互的对象之间
紧耦合引用关系,从而更好地抵御变化。
意图(Intent):
用一个中介对象来封装一系列对象交互。中介者使各对象不需要显示的相互引用,从而使其
耦合松散,而且可以独立地改变它们之间的交互。
————《设计模式》GOF
我们抛开这些精典的理论,看看如何把我们的功能细节分离到一系列 class 中,以及让菜单工作。
先总体上看一下最后的类图:
点击下载完整类图
Element 和 Mediator 都是抽象类,但他们之间存在很强的耦合关系, Element 依赖 Mediator 对象,
同时Mediator对象内部包含 Element 的集合。因此 Mediator 也依赖 Element,Element 了类化就
可以在这些类中实现我们的功能细节。那就先看看这两个抽象类吧:
2
在这里,MainForm 是主窗口,它充当了中介,它可以被 Element 和 Mediator 引用。
你可能已经发现,Element还依赖于一个 ICommand 接口,IComand 接口引用了一个 object 的对象
在它的一个实现类 MenuItemCommand 中,这个对象指向的是一个 ToolStripMenuItem 的对象。
2
那么你可能会问,Element 为什么不直接依赖 ToolStripMenuItem 呢?其实在这个项目里,我们完全
可以这么做,因为我们要使用的仅仅是ToolStripMenuItem, 我们之所以抽象出来一个 ICommand, 完全是
考虑到可能会用到其他的UI组件比如 Button 等,那么如果有这样的需求,很简单,我们可以实现一个
ButtonCommand,让他实现 ICommand 接口,这样保证了接口的统一,它就可以和 ToolStripMenuItem 一起
工作了。简单地说,抽象一个 ICommand 只是为了统一接口。Element 和 Mediator 还实现了 IDispose
接口,这是.Net中被经常提到的 Dispose 模式,如果感兴趣,你可以看看我的另一篇博客:
.Net Dispose 模式 与 C++/CLI 确定性资源清理
实际使用中,Dispose 方法都是通过 Mediator 对象调用的。
好的,抽象已经完成,看看具体怎么实现各个 Menu 的工作细节吧。举一个穿透桌面功能的例子吧:
OnExecute 是点击菜单是会执行的方法,OnStatusChanged 的调用可能是因为用户单击了任何一个菜单,
也可能是MainForm中的代码调用了 mediator.Notify(), 由于 Mediator 中引用着所有 Element 的集合,
所以调用 Notify() 将导致所有 Element 的 OnStatusChanged() 被调用,同样,直接点击任何一个菜单项,
也会导致其他(还有自身)菜单接到通知,即 OnStatusChanged() 被调用。所以这些都是在抽象基类里面完成
的,实体 Element 只需要处理这两个方法就可以了。实体 Mediator 本软件中只有一个,他的实现很简单。
2
为了更好地利用 Visual Studio 的 Designer 设计器,菜单还是在 MainForm 里面用 Designer 生成的,只是在
MainForm 的构造行函数里面,菜单项被 Element 引用。由于进行了设计,功能都被分散了,MainForm 里面的实体
代码并不多,看看吧。
这里 Designer 生成的代码没有列出来,其中 SetupMenuItemElements 方法就是完成了实例化 Element 和 Mediator
具体类的作用, 这样他们都被 medator 引用,所以你不用担心它马上会被垃圾收集干掉。
参考资料:
关机对话框
李建忠-C#面向对象模式纵横谈第七讲-(行为型模式) Mediator 中介者模式