// 版权所有:Anders06 2007年10月15日
前阵子,写了一篇blog:谈谈这阵子忙的事一:关于design整个过程的感想
很多博友对其最后的解决方案很感兴趣,今天补上这样的一篇文章,希望对他们有所交代。由于个人原因,时至今日才补上,希望各位看官见谅。
由于牵涉到本人公司的项目,诸多细节地方不好表达,由此借用Windows Forms Designer里的一套Service机制向大家阐述下,我们项目的最终解决方案跟此机制基本一致。
借此机会和大家共同学习下Framework里的这一套优秀设计方案。
熟悉Windows Forms Designer的应该 都知道Framework为我们提供了一套机制使我们能够很方便的定制自己的窗口设计器。那什么是窗口设计器呢,我们平时基本都用VS开发,创建个Form,然后拖几个控件过来,然后设置控件的属性,或者将几个控件按不同的对齐方式排列,这些功能就是窗口设计器提供给我们的在设计期改变控件属性的功能。 我们可以用Framework这套机制完全可以打造出个性化的IDE。DesignerHost里如何管理这些服务,如何方便用户扩充这些服务就是此篇文章想要阐述的重点。
在下图的Services块中,我们可以看到里面有IToolBoxServices,IMenuCommandService等等服务。我们知道在IToolBoxServices接口里提供了一些操作ToolBox的功能,当我们打开VS的ToolBox, 加载些什么控件,怎么加载,怎么分类都是由这个ToolBoxServices控制的。 那么如果我们想实现跟VS的ToolBox不一样的方式,我们只需定义一个这样的类,让以自己的方式实现IToolBoxServices。那么当我们实现了自己的ToolBoxServices如何让其代替现有的ToolBox的功能,其实很简单就是将其注册到Designer Host中.
<上图来自于MSDN上的一篇关于Form Designer文章,当前找不到具体的出处了,下次找到了再补上具体链接>
上面是一段关于如何注册ToolBox服务简单的示例代码,这就是本文要讲的主题。Windows Forms Designer里有一套关于Service服务的机制,我们只需简单的几行代码就可以实现自定义不同主题的服务。那么我们来详细探索下这个机制是如何实现的。
首先有一个叫IServiceContainer的接口,里面提供了几个方法,主要有3个,提供了加入服务,移除服务以及获取服务的功能,其都是对一个对象类型操作。
在DesignerHost里维护了一个这样的管理Service的容器,这样我们就可以往里面加不同的Service服务,就如
.AddService(typeof(IToolboxService), new MyToolBoxService();); 就完成了对ToolBox服务的注册。 当DesignerHost在里面需要Call ToolBoxService,以完成对工具栏的初始化时,它只需要这样做:
IToolBoxService toolBoxService = m_serviceContainer.GetService(IToolBoxService) as IToolBoxService;然后调用toolBoxService的方法就行了,这样实际上DesignerHost调用的是我们自己实现的MyToolBoxService方法,以达到提供自定义的效果。
整体机制就是这样,是不是很简单呢:)。 Ok,下面我们探讨下在实际应用中的价值。
首先我们一般定义这样一个管理service注册,获取的类,载时我们命名为ServiceManager吧,这个类最好做成单例在整个应用程序里都能访问。
在我们的项目中,层次结构是很丰富的,并且有几个不同的应用程序。某些不同的Service可能在不同的应用程序里表现不一样。因此我们可以在底层定义一个IService接口,其接口里定义了此类服务的方法比如,SayHello(),然后在同一层次,或者跟高层需要调用IService的服务的时候只需针对这个接口编程,而获得这个接口实例的方法就是去call ServiceManager的GetService()方法。最后一步就是在在不同的应用程序层里往ServiceManager里注册不同的IService实例。如此以来我们在底层就能够做到完全针对接口编程,这样带来的好处就是完全解耦,又能达到不同的应用层可以实现不同的服务的目的。
写到这里让我觉得这套机制实际上相似于依赖注入机制,只不过后者可以在XML中配置,更加灵活,不过实现的代价也更高些。
简单来总结下本人对此套机制的看法,首先我们都知道管理需求变化的最有效方式就是针对接口编程,因为接口相对来说是比较稳定的,所以我们在底层应该尽量针对接口编程。然而我们总是要初始化这个接口的, 也许我们可以在DesignerHost里提供一个IToolBoxService的属性,然后我们在应用层里对其赋值。这样做的方式有几个弊端: 其一就是DesignerHost需要挨个暴露出这样的属性,使得其变得臃肿难看,而且不利用封装,因为其暴露太多不必要的属性。其二也是最致命的,因为我们的应用层并不一定保证能够访问到DesignerHost,以至于无法完成对其赋值。 而引入这套Service管理服务可以达到注册的位置跟其使用的地方完全无关联的效果,可以是天涯海角,而且更加灵活。因此对于那些在不同的场合需要实现不同的服务的情况下,绝对是一剂良药。
最近在我们的另一个项目中,重构了原有的一些代码,实现了对某某的可插拔,就再次应用了此套服务,有点感觉屡试不爽啊:)
最后附上一个以泛型方式实现的简单的ServiceManager类,有兴趣的可以看看
ServiceManager.zip