來源於WCF的設計模式:可擴展對象模式[上篇]
我一直很喜歡剖析微軟一些產品、框架的底層實現。在我看來,這不但讓我們可以更加深入地了解其運作的原理,同時也能提高設計/架構的技能。因為對於這些框架或者產品來說,高質量的設計是它們能夠成功的一個最基本的因素。比如說比如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>,前者代表可擴展對象,後者代表對這個可擴展對象的擴展,而這個泛型參數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》
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 15:33:46