[转载]来源于WCF的设计模式:可扩展对象模式 – Artech – 博客园.
我一直很喜欢剖析微软一些产品、框架的底层实现。在我看来,这不但让我们可以更加深入地 了解其运作的原理,同时也能提高设计/架构的技能。因为对于这些框架或者产品来说,高质量的设计是它们能够成功的一个最基本的因素。比如说比如 ASP.NET,不但能够支持传统的Web Form应用,MVC同样建立在它基础之上。比如说WCF,从其诞生的那一天开始,真个架构体系就从未改变。这些应用在这些产品和框架上的设计其实是最值 得我们学习的设计案例。比如说,今天我们介绍的“可扩展对象模式(Extensible Object Pattern)”就来源于WCF。[源代码从这里下载]
一、一个简单的“可扩展对象模式”的实现
为 了让这种所谓的“可扩展对象模式”有一个大概的了解,我们先来演示一个简单的例子。现在有一个表示房间的类型Room,它具有几个基本的属性Walls、 Windows和Door分别表示房间的墙、窗户和门。现在我们要来创建一个Room对象,即分别创建组成这个Room对象的各个构成元素。按照“大事化 小”这个基本的设计原则,我们分别创建相应的Builder来分别为Room构建相应的元素。按照“可扩展对象模式”的原理,Room对象就是一个可扩展 对象,而相应的Builder实现了对它的扩展。现在我们将Room这个类型定义在实现了接口 IExtensibleObject<Room>的可扩展对象。
1: public class Room : IExtensibleObject<Room>
2: {
3: public Room()
4: {
5: this.Extensions = new ExtensionCollection<Room>(this);
6: }
7: public Door Door { get; set; }
8: public IList<Wall> Walls { get; set; }
9: public Window Window { get; set; }
10: public IExtensionCollection<Room> Extensions { get; private set; }
11: }
12: public class Door{}
13: public class Wall{}
14: public class Window{}
为Room对象构建门、窗和墙的Builder(DoorBuilder、WindowBuilder和WallBuilder)均实现了相同的接口IExtension<Room>,表明它们都是针对Room的扩展。
1: public class DoorBuilder : IExtension<Room>
2: {
3: public Door Door { get; private set; }
4: public void Attach(Room owner)
5: {
6: owner.Door = this.Door = new Door();
7: }
8: public void Detach(Room owner)
9: {
10: if (this.Door == owner.Door)
11: {
12: owner.Door = null;
13: this.Door = null;
14: }
15: }
16: }
17:
18: public class WindowBuilder : IExtension<Room>
19: {
20: public Window Window { get; private set; }
21: public void Attach(Room owner)
22: {
23: owner.Window = this.Window = new Window();
24: }
25: public void Detach(Room owner)
26: {
27: if (this.Window == owner.Window)
28: {
29: owner.Window = null;
30: this.Window = null;
31: }
32: }
33: }
34:
35: public class WallBuilder : IExtension<Room>
36: {
37: public Wall[] Walls { get; private set;}
38: public void Attach(Room owner)
39: {
40: owner.Walls = this.Walls = new Wall[] { new Wall(), new Wall(), new Wall(), new Wall() };
41: }
42: public void Detach(Room owner)
43: {
44: if (null == owner.Walls || null== this.Walls)
45: {
46: this.Walls = null;
47: return;
48: }
49: Array.ForEach(this.Walls, wall =>
50: {
51: if (owner.Walls.Contains(wall))
52: {
53: owner.Walls.Remove(wall);
54: }
55: });
56: this.Walls = null;
57: }
58: }
现在我们真正创建Room对象的程序写成如下形式:先创建可扩展对象Room,并将用于用于构建相应元素的三个Builder添加到以 Extensions属性表示的扩展集合中。经过这个简单的过程,一个完整的Room对象就已经被正常的构建了。这个简单的应用体现和很好的可扩展性:任 何针对Room对象的扩展都可以通过相应扩展对象(IExtension<Room>)来实现,如果我们需要,只需要将这些扩展添加到它的 Extensions集合中就可以了。
1: Room room = new Room();
2: room.Extensions.Add(new DoorBuilder());
3: room.Extensions.Add(new WindowBuilder());
4: room.Extensions.Add(new WallBuilder());
5:
6: Debug.Assert(room.Door != null, "Door has not been built!");
7: Debug.Assert(room.Window != null, "Window has not been built!");
8: Debug.Assert(room.Walls != null, "Walls have not been built!");
二、IExtensibleObject<T>和IExtension<T>
这个可扩展对象模式涉及到两个基本的接口,即IExtensibleObject<T>和IExtension<T>,前 者代表可扩展对象,后者代表对这个可扩展对象的扩展,而这个泛型参数T则代表定义成可扩展对象的类型。我想如果你是第一个接触者两个接口,看到这个介绍你 可能会觉得很晕,尤其是“可扩展对象类型T实现接口IExtensibleObject<T>”,不过多想想,应该会很快绕过弯子来的。
IExtensibleObject<T>接口仅仅定义了一个唯一的属性Extensions,而它类型是 IExteniosnCollection<T>代表针对可扩展对象类型T的扩展的集合。System.ServiceModel定义了具体 的集合类型ExteniosnCollection<T>实现了IExteniosnCollection<T>接口
1: public interface IExtensibleObject<T> where T: IExtensibleObject<T>
2: {
3: IExtensionCollection<T> Extensions { get; }
4: }
而所谓的“针对可扩展对象类型T的扩展”就是实现了第二个接口IExtension<T>的对象。该接口具有两个方法Attach和 Detach,具体相同的参数owner。当我们将某个扩展对象添加到某个可扩展对象的Extensions集合中的时候,Attach方法会自动被调 用,而传入的参数owner就是这个需要被扩展的对象。与此相对,Detach则在当该扩展对象从可扩展对象的Extensions集合中移出的时候被调 用。
1: public interface IExtension<T> where T: IExtensibleObject<T>
2: {
3: void Attach(T owner);
4: void Detach(T owner);
5: }
在前面的例子中,我们将DoorBuilder、WindowBuilder和WallBuilder实现 IExtension<Room>接口,将相应的针对门、窗和墙的构建实现在Attach方法中。所以当它们被作为对Room对象的扩展添加 到一个具体的Room对象的Extensions集合上的时候,这个Romm对象就分别有了一道门、一扇窗和四面墙。此外,由于被添加的Builder有 可能被移除,如果被移除后,先前被创建的门、窗和墙应该也一并移掉,而这些操作被定义在Detach方法中。
三、总结
在这里,我们将围绕着IExtensibleObject<T>和IExtension<T>这两个接口的设计方式说成一 种“设计模式”,可能不太妥当。实际上,任何存在扩展可能的类型都可以按照这样的方式来设计。而我们熟悉的一些设计模式都可以按照“可扩展对象”的方式来 设计。文中Room采用的涉及模式可以看成是Builder模式。
注:关于“可扩展对象模式”,李会军同学写了一篇很好的文章《技巧:使用可扩展对象模式扩展HttpApplication》