两截门(Dutch Door)——(名词)一个被水平分割为两部分的门,这样每一部分均可以独立保持开放或者封闭。(《美国传统英语字典》第4版,2000年)html
假设您设计的程序已经部署到用户的计算机上,而且可以正常运行了。可是有一天,用户打来了电话——他们要求增长新的功能。肯定了用户的需求后,你居然发现原有的软件架构已经没法胜任新增任务的需求——你须要从新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户须要的应用,却不能保证用户的需求不会再次变动。也就是说,需求蔓延的可能性依然存在。所以,这种状况下插件构架更能显示出它的优越性。java
一样的原理,能够应用到咱们的widows服务开发中。编程
OCP:开放封闭原则api
软件实体(类、模块、函数等)应该是能够扩展的,可是不可修改。微信
这意味着模块的行为是能够扩展的。当应用的需求改变时,咱们能够对模块进行扩展,使其具备知足那些改变的新行为。还句话说,咱们能够改变模块的功能。架构
对于模块行为进行扩展时,没必要改动模块的源代码或者二进制代码。模块的二进制可执行版本,不管是可连接的库、DLL或者.EXE文件,都无需改动。框架
怎样可能在不改动模块源代码的状况下去更改它的行为呢?若是不更改一个模块,又怎么可以去改变它的功能呢?tcp
答案是抽象。在C#或者其余任何的OOPL(面向对象程序设计语言,如java)中,能够建立出固定却可以描述一组任意个可能行为抽象体。这个抽象体就是抽象基类。而这一组任意个可能得行为则表现为可能得派生类。函数
模块可能对抽象体进行操做。因为模块依赖于一个固定的抽象体,因此它对于更改能够是封闭的。同时,经过从这个抽象体派生,能够扩展此模块的行为。网站
插件式架构
插件式架构是遵循OCP原则的。插件式架构,一种开放性的、高扩展性的架构体系。基于插件的设计好处不少,把扩展功能从框架中剥离出来,下降了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,二者在保持接口不变的状况下,能够独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。
而笔者实际项目中,一般是按照业务归属将不一样业务模块设计成不一样插件。
C#实现插件式开发的理论基础
在dotNET framework中,给开发人员提供了反射机制,经过反射能够动态加载类库。
Assembly类能够得到正在运行的装配件信息,也能够动态的加载装配件,以及在装配件中查找类型信息,并建立该类型的实例。
Type类能够得到对象的类型信息,此信息包含对象的全部要素:方法、构造器、属性等等,经过Type类能够获得这些要素的信息,而且调用之。
MethodInfo包含方法的信息,经过这个类能够获得方法的名称、参数、返回值等,而且能够调用之。
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。
实现基本目标:
(1)建立一个WinowsService服务,建立三个wcf服务,而后分别实现各自wcf的业务逻辑。以上步骤比较简单。
(2)而后就是编译wcf.dll,并拷贝到host服务的指定目录,好比个人在\plugins\xx_wcf.dll。
(3)在Host宿主的配置文件,添加endpoint和操做契约
<system.serviceModel> <bindings> <netTcpBinding> <binding name="tcpBinding"> <security mode="None"> </security> </binding> </netTcpBinding> </bindings> <services> <!--添加服务--> <!--B服务--> <service name="Plugins.BService.WcfService.BusinessBService" behaviorConfiguration="Plugins.BService.WcfService"> <!--name 必须与代码中的host实例初始化的服务同样 behaviorConfiguration 行为配置 --> <host> <baseAddresses> <!--添加调用服务地址--> <add baseAddress="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService"/> </baseAddresses> </host> <!--添加契约接口 --> <endpoint address="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService" binding="netTcpBinding" contract="Plugins.BService.WcfService.IBusinessBService" bindingConfiguration="tcpBinding" name="Plugins.BService.WcfService.BusinessBasicInfoService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> <!--A服务--> <service name="Plugins.AService.WcfService.BusinessAService" behaviorConfiguration="Plugins.AService.WcfService"> <!--name 必须与代码中的host实例初始化的服务同样 behaviorConfiguration 行为配置 --> <host> <baseAddresses> <!--添加调用服务地址--> <add baseAddress="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService"/> </baseAddresses> </host> <!--添加契约接口 --> <endpoint address="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService" binding="netTcpBinding" contract="Plugins.AService.WcfService.IBusinessAService" bindingConfiguration="tcpBinding" name="Plugins.AService.WcfService.BusinessBasicInfoService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> <!--Resource.Robot服务--> <service name="ResourceRobot.White.WcfService.WhiteListService" behaviorConfiguration="ResourceRobot.White.WcfService"> <!--name 必须与代码中的host实例初始化的服务同样 behaviorConfiguration 行为配置 --> <host> <baseAddresses> <!--添加调用服务地址--> <add baseAddress="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService"/> </baseAddresses> </host> <!--添加契约接口 --> <endpoint address="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService" binding="netTcpBinding" contract="ResourceRobot.Channels.IWhiteListService" bindingConfiguration="tcpBinding" name="ResourceRobot.White.WcfService.WhiteListService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> </services> <!--定义WcfServiceBehavior的行为--> <behaviors> <serviceBehaviors> <behavior name="Plugins.BService.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> <behavior name="Plugins.AService.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> <behavior name="ResourceRobot.White.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
(4)接下来就是利用.NET反射机制,去遍历plugins目录下的wcf.dll,分别注册到ServiceHost中。
在service.cs的OnStart方法中,增长如下代码:
//1.寻找wcf.dll List<string> pluginpaths = PluginHelper.Find(); //2.遍历并解析 foreach (string filename in pluginpaths) { try { //获取文件名 string asmfile = filename; string asmname = Path.GetFileNameWithoutExtension(asmfile); if (asmname != string.Empty) { //利用反射,构造DLL文件的实例 Assembly asm = Assembly.LoadFile(asmfile); //利用反射,从程序集(DLL)中,提取类,并把此类实例化 Type[] t = asm.GetExportedTypes(); foreach (Type type in t) { //3.注册 ServiceHost host = new ServiceHost(type); if (host != null) { host.Open(); } } } } catch (Exception ex) { Console.Write(ex.Message); }
这样就实现了一个serivcehost承载多个wcf服务的要求。
3 总结
OCP:开放-封闭原则是敏捷开发中很是重要的原则。
使用插件式架构可以是整个软件更加灵活,扩展性更强,同时也便于部署和维护。
最后分享一下趣图:
转载请注明出处,本文同步更新至