民初思韻網

加入收藏   設為首頁
選擇語言   簡體中文
你好,請 登陸 或 注冊
首頁 人文思韻 傳奇人物 歷史思潮 時代作品 話題討論 國民思韻 民初捐助 賬戶管理
  搜索  
    人文精神 >>> 技術的天空 溫和的思緒
字體    

C#面向對象設計模式縱橫談 第9講:Composite 組合模式
C#面向對象設計模式縱橫談 第9講:Composite 組合模式
MSDN 網絡廣播 李建忠     阅读简体中文版

2006.2.17 李建忠

對象容器的問題

在面向對象系統中,我們常會遇到一類具有“容器”特征的對象——即它們在充當對象的同時,又是其他對象的容器。

image

如果我們要對這樣的對象容器進行處理:

image

上面是客戶代碼,客戶代碼里面必須要知道對象的結構,有可能還要使用遞歸的方法來處理這個對象,這樣寫耦合性就比較高。客戶代碼如果能只和IBox發生依賴就很好了,但是現在它還和ContainerBox和SingleBox發生了依賴,這樣內部實現的細節就暴露給了外界,并且和外界產生了依賴關系。

 

動機(Motivation)

上述描述的問題根源在于:客戶代碼過多地依賴于對象容器復雜的內部實現結構,對象容器內部實現結構(而非抽象接口)的變化將引起客戶代碼的頻繁變化,帶來了代碼的維護性、擴展性等弊端。

如何將“客戶代碼與復雜的對象容器結構”解耦?讓對象容器自己來實現自身的復雜結構,從而使得客戶代碼就像處理簡單對象一樣來處理復雜的對象容器?

 

意圖(Intent)

將對象組合成樹形結構以表示“部分-整體”的層次結構。Composite使得用戶對單個對象和組合對象的使用具有一致性。

——《設計模式》GoF

 

例說Composite應用

以前面的例子為例

image

image

改進的方案

期望的客戶代碼:

image

接口和SingleBox代碼都不變

image

image

ContainerBox代碼變化

image

這樣做ContainerBox里面的Process方法就不用判斷是否是ContainerBox還是SingleBox,因為它們執行的方法名字都叫做Process。而且客戶代碼也只用調用box的Process方法即可。

但是這里還有一個問題,客戶代碼訪問不了ContainerBox的Add和Remove方法,因為IBox接口里沒有定義。

為了解決這個問題,我們可以選擇在IBox接口里添加兩個方法Add和Remove,然后SingleBox的Add和Remove方法什么都不做或者拋出異常。

image

但這樣的處理方法也和理想的方法有點差距,因為IBox這個類并不符合我們類的單一職責原則,它有SingleBox和ContainerBox二者的職責,因此SingleBox對于Add和Remove也比較不好處理。但是總的來說,我們還是完成了客戶代碼的解耦工作。

我們看看整個代碼的結構,ContainerBox里面包含了很多IBox,這些IBox有的是ContainerBox,有的也是SingleBox,因此它很像一個樹形的結構。

 

結構(Structure)

image

Component抽象類或者接口對應之前例子中的IBox,Leaf對應SingleBox,Composite對應ContainerBox。客戶代碼只依賴于Component抽象類或者結構,這正是我們期望的目的。

 

Composite模式的幾個要點

Composite模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉化為“一對一”的關系,使得客戶代碼可以一致地處理對象和對象容器,無需關心處理的是單個的對象,還是組合的對象容器。

將“客戶代碼與復雜的對象容器結構”解耦是Composite模式的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對象容器的復雜內部實現結構——發生依賴關系,從而更能“應對變化”。

Composite模式中,是將“Add和Remove等和對象容器相關的方法”定義在“表示抽象對象的Component類”中,還是將其定義在“表示對象容器的Composite類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。這里有可能違背面向對象的“單一職責原則”,但是對于這種特殊結構,這又是必須付出的代價。ASP.Net控件的實現在這方面為我們提供了一個很好的示范。

Composite模式在具體實現中,可以讓父對象中的子對象反向追朔;如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率。

 

.NET框架中的Composite應用

ASP.Net中的Panel對象就是一個Composite對象,而Button對象就是Leaf對象。Button和Panel都繼承自System.Web.UI.Control類。

image

它實際上是在Panel里面加了一個Controls屬性,然后Controls屬性是一個集合屬性,它有Add和Remove方法。這樣我們的IBox也可以改為:

image

在ASP.Net中就是這樣,每一個控件都有Controls屬性,也就是說每個控件都是一種容器控件(除了LiteralControl)。

這種方式把我們對安全性的担憂,統統放到容器(即ASP.Net中的Controls,以及例子中的Boxes)中去處理。

2010.10.3

2013-08-22 08:45

歡迎訂閱我們的微信公眾賬號!
春秋茶館訂閱號
微信號 season-tea(春秋茶館)
每天分享一篇科技/遊戲/人文類的資訊,點綴生活,啟迪思想,探討古典韻味。
  清末民初歷史人物  民初人物
革命先行者民國之父
孫中山(1866年11月12日-1925年3月12日),本名孫文,字載之,號日新、逸仙,廣東香山(今中山)人,是醫師、近代中國的民主革命家、中國國民黨總理、第一任中華民國....
孫中山的啟蒙者
近現代的嶺南,湧現出大批引領中國前行的先驅者,近代改良主義者,香港華人領袖何啟便是其中的一位。他不僅是孫中山在香港西醫書院的老師,更是孫中山走向革命道路的思想導師。
資助民初精神網
        回頂部     寫評論

 
評論集
暫無評論!
發表評論歡迎你的評論
昵稱:     登陸  註冊
主頁:  
郵箱:  (僅管理員可見)

驗證:   验证码(不區分大小寫)  
© 2011   民初思韻網-清末民初傳奇時代的發現與復興   版權所有   加入收藏    設為首頁    聯繫我們    1616導航