来源于WCF的设计模式:可扩展对象模式[下篇]

在《来源于WCF的设计模式:可扩展对象模式》我通过一个简单的例子介绍了基于IExtensibleObject<T>和IExtension<T>这两个接口为核心的“可扩展对象模式”。在那篇文章中,我是通过编程的方式来应用扩展到扩展对象的。其实,如何能够通过配置的方式来定义扩展,这个所谓的“可扩展对象模式”将会发挥更大的威力。[源代码从这里下载]

目录:
一、将XxxBuilder定义在配置中
二、ExtensionConfigurationElement<T>和ExtensionNameTypeElementCollection<T>
三、XxxBuilder的配置
四、RoomFactory

一、将XxxBuilder定义在配置中

为了给大家对基于配置的扩展有一个初步的印象,我们同样先通过一个具体的例子看看最终实现的效果。同样采用上篇中关于“创建房间”的例子,不过为了真正的展示配置的作为,我们为代表房间构成元素(墙、窗户和门)的类型添加相应的属性。其中Materials属性代表门的材质,Width和Height代表窗户的长和宽,而Color则代表强的颜色。

   1: public class Door
   2: {
   3:     public string Materials { get; set; }
   4: }
   5:  
   6: public class Window
   7: {
   8:     public int Width { get; set; }
   9:     public int Height { get; set; }
  10: }
  11:  
  12: public class Wall
  13: {
  14:     public Color Color { get; set; }
  15: }

当然用于构建门、窗和墙的DoorBuilder、WindowBuilder和WallBuilder需要进行相应的修改,因为在构建相应元素的时候需要为相应的属性赋值。下面是WindowBuilder的定义,DoorBuilder和WallBuilder的定义与之类似。

   1: [ConfigurationElementType(typeof(WindowBuilderData))]
   2: public class WindowBuilder : IExtension<Room>
   3: {
   4:     public int Width { get; set; }
   5:     public int Height { get; set; } 
   6:     public Window Window { get; private set; }
   7:     public WindowBuilder(int width, int height)
   8:     {
   9:         this.Width = width;
  10:         this.Height = height;
  11:     }
  12:     public void Attach(Room owner)
  13:     {
  14:         owner.Window = this.Window = new Window { Width = this.Width, Height = this.Height };
  15:     }
  16:     public void Detach(Room owner)
  17:     {
  18:         if (this.Window == owner.Window)
  19:         {
  20:             owner.Window = null;
  21:             this.Window = null;
  22:         }
  23:     }
  24: }

现在我们将需要创建的Room,连同它们的DoorBuilder、WindowBuilder和WallBuilder都定义在配置中。在下面的配置中,我们定义了两个Room(room1和room2),它们具有不同的Builder的设置(比如DoorBuilder构建门的材质分别为“Iron”和“Wood”,WindowBuilder构建的窗户尺寸分别为2*2和1*1,而WallBuilder构建的墙的颜色分别是“White”和“Gray”)。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <configSections>
   4:     <section name="roomManager" type="ExtensibleObjectPattern.Configuration.RoomManagerSettings, ExtensibleObjectPattern"/>
   5:   </configSections>
   6:   <roomManager>
   7:     <rooms>
   8:       <add name="room1">
   9:         <builders>
  10:           <add name="doorBuilder" type="ExtensibleObjectPattern.DoorBuilder" materials="Iron"/>
  11:           <add name="windowBuilder" type="ExtensibleObjectPattern.WindowBuilder" width="2" height="2"/>
  12:           <add name="WallBuilder" type="ExtensibleObjectPattern.WallBuilder" color="White"/>
  13:         </builders>
  14:       </add>
  15:       <add name="room2">
  16:         <builders>
  17:           <add name="doorBuilder" type="ExtensibleObjectPattern.DoorBuilder" materials="Wood"/>
  18:           <add name="windowBuilder" type="ExtensibleObjectPattern.WindowBuilder" width="1" height="1"/>
  19:           <add name="WallBuilder" type="ExtensibleObjectPattern.WallBuilder" color="Gray"/>
  20:         </builders>
  21:       </add>
  22:     </rooms>
  23:   </roomManager>
  24: </configuration>

现在我具有一个专门用于创建Room对象的静态工厂RoomFactory,它可以直接通过指定Room在配置文件中的名称(room1和room2)直接创建我们需要的Room。如下在Console应用中调用如下代码来创建配置的两个Room对象,从输出的结构我们可以看到创建出来的对象的相应属性完全和配置是吻合的。

   1: Room room1 = RoomFactory.CreateRoom("room1");
   2: Console.WriteLine("Wall's Color\t: {0}", room1.Walls[0].Color);
   3: Console.WriteLine("Windows's Size\t: {0}*{1}", room1.Window.Width, room1.Window.Height);
   4: Console.WriteLine("Door's Material\t: {0}\n", room1.Door.Materials);
   5:  
   6: Room room2 = RoomFactory.CreateRoom("room2");
   7: Console.WriteLine("Wall's Color\t: {0}", room2.Walls[0].Color);
   8: Console.WriteLine("Windows's Size\t: {0}*{1}", room2.Window.Width, room2.Window.Height);
   9: Console.WriteLine("Door's Material\t: {0}", room2.Door.Materials);

输出结果:

   1: Wall's Color    : Color [White]
   2: Windows's Size  : 2*2
   3: Door's Material : Iron
   4:  
   5: Wall's Color    : Color [Gray]
   6: Windows's Size  : 1*1
   7: Door's Material : Wood

二、ExtensionConfigurationElement<T>和ExtensionNameTypeElementCollection<T>

现在我们来简单介绍一下具体实现,先来看看表示整个
<roomManager>配置节RoomManagerSettings的定义。从上面给出的配置结构我们可以看出,这个配置节就是一个包含具体room设置的<rooms>节点,该节点对应着RoomManagerSettings的Rooms属性。

   1: public class RoomManagerSettings: ConfigurationSection
   2: {
   3:     [ConfigurationProperty("rooms", IsRequired = true)]
   4:     public NameElementCollection<RoomConfigurationElement> Rooms
   5:     {
   6:         get { return (NameElementCollection<RoomConfigurationElement>)this["rooms"]; }
   7:         set { this["rooms"] = value; }
   8:     }
   9:     public RoomConfigurationElement GetRoomConfigurationElement(string name)
  10:     {
  11:         return (RoomConfigurationElement)this.Rooms.GetConfigurationElement(name);
  12:     }
  13:  
  14:     public static RoomManagerSettings GetCurrent()
  15:     {
  16:         return (RoomManagerSettings)ConfigurationManager.GetSection("roomManager");
  17:     }
  18: }

该属性的类型为NameElementCollection<T>,这是我们自定义的一个泛型配置元素集合(ConfigurationElementCollection)。NameElementCollection<T>的定义如下,泛型参数T为继承自NamConfigurationElement的配置元素。NamConfigurationElement则是我自定义的一个包含“name”配置属性的ConfigurationElement。

   1: public class NameElementCollection<T> : ConfigurationElementCollection where T : NameConfigurationElement
   2: {
   3:     public T GetConfigurationElement(string name)
   4:     {
   5:         return (T)this.BaseGet(name);
   6:     }
   7:     protected override ConfigurationElement CreateNewElement()
   8:     {
   9:         return Activator.CreateInstance<T>();
  10:     }
  11:     protected override object GetElementKey(ConfigurationElement element)
  12:     {
  13:         return ((NameConfigurationElement)element).Name;
  14:     }
  15: }

对于RoomManagerSettings的Rooms属性,其类型的泛型参数定义成RoomConfigurationElement。而RoomConfigurationElement则代表一个具体的Room的配置,除了具有一个名称(继承自ConfigurationElement)之外,它余下的配置就是XxxBuilder的集合了。该集合的类型为ExtensionNameTypeElementCollection<Room>。

   1: public class RoomConfigurationElement : NameConfigurationElement
   2: {
   3:     [ConfigurationProperty("builders", IsRequired = false)]
   4:     public ExtensionNameTypeElementCollection<Room> Builders
   5:     {
   6:         get { return (ExtensionNameTypeElementCollection<Room>)this["builders"]; }
   7:         set { this["builders"] = value; }
   8:     }
   9: }

实际上ExtensionNameTypeElementCollection<T>(类型T为实现了接口IExtensiableObject<T>的可扩展对象类型)是怎个配置系统的核心。在定义它的时候,我使用到了在《通过自定义配置实现插件式设计》中实现的关于“配置元素的动态解析”机制。具体来说,就是让它继承自在那篇文章中定义的NameTypeElementCollection<TConfigElement>并将泛型参数TConfigElement指定为ExtensionConfigurationElement<T>定义如下。

   1: public class ExtensionNameTypeElementCollection<T> : NameTypeElementCollection<ExtensionConfigurationElement<T>> where T : IExtensibleObject<T>
   2: {         
   3: }

而ExtensionConfigurationElement<T>实际上代表了具体扩展(也就是我们例子中的XxxBuilder)的配置元素它则继承自《通过自定义配置实现插件式设计》中定义的NameTypeConfigurationElement,并定义了一个虚方法CreateExtension来创建相应的扩展,即XxxBuilder。

   1: public class ExtensionConfigurationElement<T> : NameTypeConfigurationElement where T: IExtensibleObject<T>
   2: {
   3:     public virtual IExtension<T> CreateExtension()
   4:     {
   5:         throw new NotImplementedException("This method must be overriden in sub class.");
   6:     }
   7: }

三、XxxBuilder的配置

现在我们来具体看看DoorBuilder、WindowBuilder和WallBuilder配置元素的定义,它们都具有相同的基类ExtensionConfigurationElement<Room>,并且通过重写CreateExtension方法根据配置创建相应的DoorBuilder、WindowBuilder和WallBuilder。下面的代码片断代表WindowBuilder的配置元素类型WindowBuilderData。

   1: public class WindowBuilderData : ExtensionConfigurationElement<Room>
   2: {
   3:     [ConfigurationProperty("width", IsRequired =false, DefaultValue = 1)]
   4:     public int Width
   5:     {
   6:         get { return (int)this["width"]; }
   7:         set { this["width"] = value; }
   8:     }
   9:     [ConfigurationProperty("height", IsRequired = false, DefaultValue = 1)]
  10:     public int Height
  11:     {
  12:         get { return (int)this["height"]; }
  13:         set { this["height"] = value; }
  14:     }
  15:     public override IExtension<Room> CreateExtension()
  16:     {
  17:         return new WindowBuilder(this.Width, this.Height);
  18:     }
  19: }

WindowBuilder和WindowBuilderData之间的关联通过应用在WindowBuilder上的ConfigurationElementTypeAttribute特性实现。配置系统之所以能够将针对WindowBuilder的配置(XML元素)反序列化为WindowBuilderData,就在于这个特殊的ConfigurationElementTypeAttribute特性。大体的原理是这样的:首先配置系统知道WindowBuilder所在配置是一个NameTypeConfigurationElement(WindowBuilderData的基类),它可以根据type属性得到WindowBuilder的类型。然后反射得到应用在它上面的ConfigurationElementTypeAttribute特性,并进一步得到它对应的配置元素类型为WindowBuilderData。于是它就可以正确地将XML元素反序列化成相应的配置元素对象。具体的原理可以参阅《通过自定义配置实现插件式设计》。

   1: [ConfigurationElementType(typeof(WindowBuilderData))]
   2: public class WindowBuilder : IExtension<Room>
   3: {
   4:     //...
   5: }

四、RoomFactory

最后来看看创建Room的工厂类RoomFactory的定义。用于创建CreateRoom方法的实现其实很简单:根据名称得到对应Room的配置,然后获取所有扩展(即XxxBuilder)的配置来创建具体的扩展(还记得ExtensionConfigurationElement<T>的CreateExtension方法吗?),最后将所有的扩展添加到创建的Room对象的Extensions列表中即可。RoomFactory的整个配置如下:

   1: public static class RoomFactory
   2: {
   3:     public static Room CreateRoom(string name)
   4:     {
   5:         Room room = new Room();
   6:         var roomSettings = RoomManagerSettings.GetCurrent().GetRoomConfigurationElement(name);
   7:         foreach (ExtensionConfigurationElement<Room> element in roomSettings.Extensions)
   8:         {
   9:             room.Extensions.Add(element.CreateExtension());
  10:         }
  11:         return room;
  12:     }
  13: }

来源于WCF的设计模式:可扩展对象模式[上篇]

来源于WCF的设计模式:可扩展对象模式[下篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-22 16:29:02

来源于WCF的设计模式:可扩展对象模式[下篇]的相关文章

来源于WCF的设计模式:可扩展对象模式[上篇]

我一直很喜欢剖析微软一些产品.框架的底层实现.在我看来,这不但让我们可以更加深入地了解其运作的原理,同时也能提高设计/架构的技能.因为对于这些框架或者产品来说,高质量的设计是它们能够成功的一个最基本的因素.比如说比如ASP.NET,不但能够支持传统的Web Form应用,MVC同样建立在它基础之上.比如说WCF,从其诞生的那一天开始,真个架构体系就从未改变.这些应用在这些产品和框架上的设计其实是最值得我们学习的设计案例.比如说,今天我们介绍的"可扩展对象模式(Extensible Object

一起谈.NET技术,技巧:使用可扩展对象模式扩展HttpApplication

概述 HttpApplication对象对于做ASP.NET开发的朋友,我想没有人不熟悉它.在ASP.NET开发中,经常避免不了要在HttpApplication中执行一些操作,如使用了ASP.NET MVC框架,就会在Application_Start 事件中避免不了这样的路由规则配置代码: protected void Application_Start(){RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); R

技巧:使用可扩展对象模式扩展HttpApplication

概述 HttpApplication对象对于做ASP.NET开发的朋友,我想没有人不熟悉它.在ASP.NET开发中,经常避免不了要在HttpApplication中执行一些操作,如使用了ASP.NET MVC框架,就会在Application_Start 事件中避免不了这样的路由规则配置代码: protected void Application_Start() { RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

使用可扩展对象模式扩展HttpApplication

概述 HttpApplication对象对于做ASP.NET开发的朋友,我想没有人不熟悉它.在ASP.NET开发中,经常避免 不了要在HttpApplication中执行一些操作,如使用了ASP.NET MVC框架,就会在Application_Start 事件 中避免不了这样的路由规则配置代码: protected void Application_Start() { RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}"

角色对象模式

作者:Dirk Bäumer, Dirk Riehle, Wolf Siberski, and Martina Wulf  原文链接  译者:f0tlo <1357654289@qq.com> 意图 单个对象透过不同的角色对象来满足不同客户的不同需求.每一个角色对象针对不同的客户内容来扮演其角色.对象能够动态的管理其角色集合.角色作为独立的对象是的不同的内容能够简单的被分离开来,系统间的配置也变得容易. 译注:为了行文的流畅性及内容意思的准确性,尽量贴近原文使用英文单词标记特定内容, 如Cus

php设计模式介绍之伪对象模式

面向对象的编程之所以丰富多彩,部分是由于对象间的相互联系与作用.一个单一的对象就能封装一 个复杂的子系统,使那些很复杂的操作能够通过一些方法的调用而简化.(无所不在的数据库连接就是 这样的一个对象实例.) 然而经常有这样的情况,对象间的交互性是如此复杂以至于我们不得 不面对类似"先有鸡还是先有蛋"这样伤脑筋的问题:如何创建并测试这样一个对象,他要 么依赖于很多已创建的对象,要么依赖于其他一些难以意识到的情况,如整个数据库的创建和测试. 问题 如何分隔并测试一个与其他对象和资源有关的代码

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述         在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件判断语

浅析php设计模式之数据对象映射模式_php实例

php中的设计模式中有很多的各种模式了,在这里我们来为各位介绍一个不常用的数据映射模式吧,希望文章能够帮助到各位. 数据映射模式使您能更好的组织你的应用程序与数据库进行交互. 数据映射模式将对象的属性与存储它们的表字段间的结合密度降低.数据映射模式的本质就是一个类,它映射或是翻译类的属性或是方法到数据库的相应字段,反之亦然. 数据映射的作用(工作)就在于能对双方所呈现出的信息的理解,并能对信息的存取进行控制,如根据存储在数据表中的信息 重建新的域对象,或是用域对象的信息来更新或删除数据表中的相关

PHP设计模式之装饰者模式代码实例

  这篇文章主要介绍了PHP设计模式之装饰者模式代码实例,装饰者模式就是不修改原类代码和继承的情况下动态扩展类的功能,本文就给出了代码实例,需要的朋友可以参考下 定义: 装饰者模式就是不修改原类代码和继承的情况下动态扩展类的功能.传统的编程模式都是子类继承父类实现方法重载,使用装饰器模式,只需添加一个新的装饰器对象,更加灵活,避免类数量和层次过多. 角色: Component(被装饰对象基类) ConcreteComponent(具体被装饰对象) Decorator(装饰者基类) Contret