亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

(第Ⅲ部分 結構型模式篇) 第10章 組合模式(Com

系統 1892 0
——.NET設計模式系列之十一
Terrylee 2006 3
概述
組合模式有時候又叫做部分 - 整體模式,它使我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以向處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。
意圖
將對象組合成樹形結構以表示“部分 - 整體”的層次結構。 Composite 模式使得用戶對單個對象和組合對象的使用具有一致性。 [GOF 《設計模式》 ]
結構圖
1 Composite 模式結構圖
生活中的例子
組合模式將對象組合成樹形結構以表示 " 部分 - 整體 " 的層次結構。讓用戶一致地使用單個對象和組合對象。雖然例子抽象一些,但是算術表達式確實是組合的例子。算術表達式包括操作數、操作符和另一個操作數。操作數可以是數字,也可以是另一個表達式。這樣, 2+3 和( 2+3 + 4*6 )都是合法的表達式。
2 使用算術表達式例子的 Composite 模式對象圖
組合模式解說
這里我們用繪圖這個例子來說明 Composite 模式,通過一些基本圖像元素(直線、圓等)以及一些復合圖像元素(由基本圖像元素組合而成)構建復雜的圖形樹。在設計中我們對每一個對象都配備一個 Draw() 方法,在調用時,會顯示相關的圖形。可以看到,這里復合圖像元素它在充當對象的同時,又是那些基本圖像元素的一個容器。先看一下基本的類結構圖:
3
圖中橙色的區域表示的是復合圖像元素。示意性代碼:
而其他作為樹枝構件,實現代碼如下: 現在我們要對該圖像元素進行處理:在客戶端程序中,需要判斷返回對象的具體類型到底是基本圖像元素,還是復合圖像元素。如果是復合圖像元素,我們將要用遞歸去處理,然而這種處理的結果卻增加了客戶端程序與復雜圖像元素內部結構之間的依賴,那么我們如何去解耦這種關系呢?我們希望的是客戶程序可以像處理基本圖像元素一樣來處理復合圖像元素,這就要引入 Composite 模式了,需要把對于子對象的管理工作交給復合圖像元素,為了進行子對象的管理,它必須提供必要的 Add() Remove() 等方法,類結構圖如下:
public abstract class Graphics
{
protected string _name;

public Graphics( string name)
{
this ._name = name;
}

public abstract void Draw();
}


public class Picture:Graphics
{
public Picture( string name)
:
base (name)
{}
public override void Draw()
{
//
}


public ArrayListGetChilds()
{
// 返回所有的子對象
}

}
public class Line:Graphics
{
public Line( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}


public class Circle:Graphics
{
public Circle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}


public class Rectangle:Graphics
{
public Rectangle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}
圖4
示意性代碼:
這樣引入 Composite 模式后,客戶端程序不再依賴于復合圖像元素的內部實現了。然而,我們程序中仍然存在著問題,因為 Line Rectangle Circle 已經沒有了子對象,它是一個基本圖像元素,因此 Add() Remove() 的方法對于它來說沒有任何意義,而且把這種錯誤不會在編譯的時候報錯,把錯誤放在了運行期,我們希望能夠捕獲到這類錯誤,并加以處理,稍微改進一下我們的程序: 這樣改進以后,我們可以捕獲可能出現的錯誤,做進一步的處理。上面的這種實現方法屬于透明式的 Composite 模式,如果我們想要更安全的一種做法,就需要把管理子對象的方法聲明在樹枝構件 Picture 類里面,這樣如果葉子節點 Line Rectangle Circle 使用這些方法時,在編譯期就會出錯,看一下類結構圖:
public abstract class Graphics
{
protected string _name;

public Graphics( string name)
{
this ._name = name;
}

public abstract void Draw();
public abstract void Add();
public abstract void Remove();
}


public class Picture:Graphics
{
protected ArrayListpicList = new ArrayList();

public Picture( string name)
:
base (name)
{}
public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());

foreach (Graphicsg in picList)
{
g.Draw();
}

}


public override void Add(Graphicsg)
{
picList.Add(g);
}

public override void Remove(Graphicsg)
{
picList.Remove(g);
}

}


public class Line:Graphics
{
public Line( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

public override void Add(Graphicsg)
{}
public override void Remove(Graphicsg)
{}
}


public class Circle:Graphics
{
public Circle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

public override void Add(Graphicsg)
{}
public override void Remove(Graphicsg)
{}
}


public class Rectangle:Graphics
{
public Rectangle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

public override void Add(Graphicsg)
{}
public override void Remove(Graphicsg)
{}
}
public class Line:Graphics
{
public Line( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

public override void Add(Graphicsg)
{
// 拋出一個我們自定義的異常
}

public override void Remove(Graphicsg)
{
// 拋出一個我們自定義的異常
}

}
圖5
示意性代碼:
這種方式屬于安全式的 Composite 模式,在這種方式下,雖然避免了前面所討論的錯誤,但是它也使得葉子節點和樹枝構件具有不一樣的接口。這種方式和透明式的 Composite 各有優劣,具體使用哪一個,需要根據問題的實際情況而定。通過 Composite 模式,客戶程序在調用 Draw() 的時候不用再去判斷復雜圖像元素中的子對象到底是基本圖像元素,還是復雜圖像元素,看一下簡單的客戶端調用: .NET 中的組合模式
public abstract class Graphics
{
protected string _name;

public Graphics( string name)
{
this ._name = name;
}

public abstract void Draw();
}


public class Picture:Graphics
{
protected ArrayListpicList = new ArrayList();

public Picture( string name)
:
base (name)
{}
public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());

foreach (Graphicsg in picList)
{
g.Draw();
}

}


public void Add(Graphicsg)
{
picList.Add(g);
}

public void Remove(Graphicsg)
{
picList.Remove(g);
}

}


public class Line:Graphics
{
public Line( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}


public class Circle:Graphics
{
public Circle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}


public class Rectangle:Graphics
{
public Rectangle( string name)
:
base (name)
{}

public override void Draw()
{
Console.WriteLine(
" Drawa " + _name.ToString());
}

}
public class App
{
public static void Main()
{
Pictureroot
= new Picture( " Root " );

root.Add(
new Line( " Line " ));
root.Add(
new Circle( " Circle " ));

Rectangler
= new Rectangle( " Rectangle " );
root.Add(r);

root.Draw();
}

}
如果有人用過 Enterprise Library2.0 ,一定在源程序中看到了一個叫做 ObjectBuilder 的程序集,顧名思義,它是用來負責對象的創建工作的,而在 ObjectBuilder 中,有一個被稱為定位器的東西,通過定位器,可以很容易的找到對象, 它的結構采用鏈表結構,每一個節點是一個鍵值對,用來標識對象的唯一性,使得對象不會被重復創建。定位器的鏈表結構采用可枚舉的接口類來實現,這樣我們可以通過一個迭代器來遍歷這個鏈表。同時多個定位器也被串成一個鏈表。具體地說就是多個定位器組成一個鏈表,表中的每一個節點是一個定位器,定位器本身又是一個鏈表,表中保存著多個由鍵值對組成的對象的節點。所以這是一個典型的Composite模式的例子,來看它的結構圖:
(第Ⅲ部分 結構型模式篇) 第10章 組合模式(Composite Pattern)
圖6
正如我們在圖中所看到的, IReadableLocator 定義了最上層的定位器接口方法,它基本上具備了定位器的大部分功能。
部分代碼:
一個抽象基類 ReadableLocator 用來實現這個接口的公共方法。兩個主要的方法實現代碼如下: 可以看到,在FindBy方法里面,循環調用了 FindInLocator 方法, 如果查詢選項是只查找當前定位器,那么循環終止,否則沿著定位器的父定位器繼續向上查找。FindInLocator方法就是遍歷定位器,然后把找到的對象存入一個臨時的定位器。最后返回一個只讀定位器的新的實例。
public interface IReadableLocator:IEnumerable < KeyValuePair < object , object >>
{
// 返回定位器中節點的數量
int Count { get ;}

// 一個指向父節點的引用
IReadableLocatorParentLocator { get ;}

// 表示定位器是否只讀
bool ReadOnly { get ;}

// 查詢定位器中是否已經存在指定鍵值的對象
bool Contains( object key);

// 查詢定位器中是否已經存在指定鍵值的對象,根據給出的搜索選項,表示是否要向上回溯繼續尋找。
bool Contains( object key,SearchModeoptions);

// 使用謂詞操作來查找包含給定對象的定位器
IReadableLocatorFindBy(Predicate < KeyValuePair < object , object >> predicate);

// 根據是否回溯的選項,使用謂詞操作來查找包含對象的定位器
IReadableLocatorFindBy(SearchModeoptions,Predicate < KeyValuePair < object , object >> predicate);

// 從定位器中獲取一個指定類型的對象
TItemGet < TItem > ();

// 從定位其中獲取一個指定鍵值的對象
TItemGet < TItem > ( object key);

// 根據選項條件,從定位其中獲取一個指定類型的對象
TItemGet < TItem > ( object key,SearchModeoptions);

// 給定對象鍵值獲取對象的非泛型重載方法
object Get( object key);

// 給定對象鍵值帶搜索條件的非泛型重載方法
object Get( object key,SearchModeoptions);
}
public abstract class ReadableLocator:IReadableLocator
{
/**/ /// <summary>
/// 查找定位器,最后返回一個只讀定位器的實例
/// </summary>

public IReadableLocatorFindBy(SearchModeoptions,Predicate < KeyValuePair < object , object >> predicate)
{
if (predicate == null )
throw new ArgumentNullException( " predicate " );
if ( ! Enum.IsDefined( typeof (SearchMode),options))
throw new ArgumentException(Properties.Resources.InvalidEnumerationValue, " options " );

Locatorresults
= new Locator();
IReadableLocatorcurrentLocator
= this ;

while (currentLocator != null )
{
FindInLocator(predicate,results,currentLocator);
currentLocator
= options == SearchMode.Local ? null :currentLocator.ParentLocator;
}


return new ReadOnlyLocator(results);
}


/**/ /// <summary>
/// 遍歷定位器
/// </summary>

private void FindInLocator(Predicate < KeyValuePair < object , object >> predicate,Locatorresults,
IReadableLocatorcurrentLocator)
{
foreach (KeyValuePair < object , object > kvp in currentLocator)
{
if ( ! results.Contains(kvp.Key) && predicate(kvp))
{
results.Add(kvp.Key,kvp.Value);
}

}

}

}
從這個抽象基類中派生出一個具體類和一個抽象類,一個具體類是只讀定位器( ReadOnlyLocator ),只讀定位器實現抽象基類沒有實現的方法,它封裝了一個實現了 IReadableLocator 接口的定位器,然后屏蔽內部定位器的寫入接口方法。另一個繼承的是讀寫定位器抽象類ReadWriteLocator,為了實現對定位器的寫入和刪除,這里定義了一個對 IReadableLocator 接口擴展的接口叫做 IReadWriteLocator ,在這個接口里面提供了實現定位器的操作:
(第Ⅲ部分 結構型模式篇) 第10章 組合模式(Composite Pattern)
圖7
實現代碼如下:
public interface IReadWriteLocator:IReadableLocator
{
// 保存對象到定位器
void Add( object key, object value);

// 從定位器中刪除一個對象,如果成功返回真,否則返回假
bool Remove( object key);
}
從ReadWirteLocator派生的具體類是Locator類,Locator類必須實現一個定位器的全部功能,現在我們所看到的Locator它已經具有了管理定位器的功能,同時他還應該具有存儲的結構,這個結構是通過一個WeakRefDictionary類來實現的,這里就不介紹了。[關于定位器的介紹參考了 niwalker 的Blog]
效果及實現要點
1 Composite 模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉化“一對一”的關系,使得客戶代碼可以一致地處理對象和對象容器,無需關心處理的是單個的對象,還是組合的對象容器。
2 .將“客戶代碼與復雜的對象容器結構”解耦是 Composite 模式的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對象容器的復內部實現結構——發生依賴關系,從而更能“應對變化”。
3 Composite 模式中,是將“ Add Remove 等和對象容器相關的方法”定義在“表示抽象對象的 Component 類”中,還是將其定義在“表示對象容器的 Composite 類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。這里有可能違背面向對象的“單一職責原則”,但是對于這種特殊結構,這又是必須付出的代價。 ASP.NET 控件的實現在這方面為我們提供了一個很好的示范。
4 Composite 模式在具體實現中,可以讓父對象中的子對象反向追溯;如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率。
適用性
以下情況下適用 Composite 模式:
1 .你想表示對象的部分 - 整體層次結構
2 .你希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。
總結
組合模式解耦了客戶程序與復雜元素內部結構,從而使客戶程序可以向處理簡單元素一樣來處理復雜元素。
參考資料
閻宏,《 Java 與模式》,電子工業出版社
James W. Cooper ,《 C# 設計模式》,電子工業出版社
Alan Shalloway James R. Trott ,《 Design Patterns Explained 》,中國電力出版社
MSDN WebCast C# 面向對象設計模式縱橫談 (9) Composite 組合模式 ( 結構型模式 )

(第Ⅲ部分 結構型模式篇) 第10章 組合模式(Composite Pattern)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 四虎影视在线看免费 720p | 久久中文字幕在线观看 | 99久久国产综合精品成人影院 | 五月天婷婷在线免费观看 | 西西大胆实体啪啪色哟哟 | 欧美一级一毛片 | 最新精品在线 | 99热这里只有精品在在 | 一级毛片子 | 色片在线| 999成人国产精品 | 免费国产黄线在线观看视频 | 在线播放国产一区二区三区 | 欧美高清日韩 | 久久国产精品一国产精品金尊 | 美女被爆羞羞视频网站视频 | 四虎免费在线观看视频 | 免费a级网站 | 久久精品国产精品亚洲婷婷 | 中文字幕51精品乱码在线 | 99视频全部免费 | 欧美性猛交xxx嘿人猛交 | 加勒比精品久久一区二区三区 | 久久国产乱子伦精品免 | 国产成人亚洲精品 | 在线免费午夜视频 | 伊人久久综合热青草 | 国产伦一区二区三区四区久久 | 视频大全在线观看免费 | 特级一级毛片视频免费观看 | 亚洲精品一区久久狠狠欧美 | 久久视频精品线视频在线网站 | 欧美成人三级视频 | 99热国产免费 | 精品国产91乱码一区二区三区 | 欧美视频一区二区 | 中文一区二区 | 国产成人午夜性视频影院 | 免费一级欧美片在线观看 | 天天摸天天草 | 欧美草逼视频 |