24组合模式(Composite Pattern)
動機(Motivate):
??? 組合模式有時候又叫做部分-整體模式,它使我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以向處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。
意圖(Intent):
????將對象組合成樹形結構以表示“部分-整體”的層次結構。Composite模式使得用戶對單個對象和組合對象的使用具有一致性。
??? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? -----------《設計模式》GOF
結構圖(Struct):
??? ?? ?? ?? ?? ?? ?? ?? ???
生活中的例子:
??? ?? ?? ?? ?? ?? ?? ??????????????????????
適用性:???
??? 1.你想表示對象的部分-整體層次結構
??? 2.你希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。
代碼實現:
????這里我們用繪圖這個例子來說明Composite模式,通過一些基本圖像元素(直線、圓等)以及一些復合圖像元素(由基本圖像元素組合而成)構建復雜的圖形樹。在設計中我們對每一個對象都配備一個Draw()方法,在調用時,會顯示相關的圖形。可以看到,這里復合圖像元素它在充當對象的同時,又是那些基本圖像元素的一個容器。先看一下基本的類結構圖:
??? ?? ?? ???
圖中橙色的區域表示的是復合圖像元素。
示意性代碼:
?2?{
?3?????protected?string?_name;
?4?
?5?????public?Graphics(string?name)
?6?????{
?7?????????this._name?=?name;
?8?????}
?9?????public?abstract?void?Draw();
10?}
11?
12?public?class?Picture?:?Graphics
13?{
14?????public?Picture(string?name)
15?????????:?base(name)
16?????{?}
17?????public?override?void?Draw()
18?????{
19?????????//
20?????}
21?
22?????public?ArrayList?GetChilds()
23?????{?
24?????????//返回所有的子對象
25?????}
26?}
而其他作為樹枝構件,實現代碼如下:
?1?public?class?Line:Graphics?2?{
?3?????public?Line(string?name)
?4?????????:?base(name)
?5?????{?}
?6?
?7?????public?override?void?Draw()
?8?????{
?9?????????Console.WriteLine("Draw?a"?+?_name.ToString());
10?????}
11?}
12?
13?public?class?Circle?:?Graphics
14?{
15?????public?Circle(string?name)
16?????????:?base(name)
17?????{?}
18?
19?????public?override?void?Draw()
20?????{
21?????????Console.WriteLine("Draw?a"?+?_name.ToString());
22?????}
23?}
24?
25?public?class?Rectangle?:?Graphics
26?{
27?????public?Rectangle(string?name)
28?????????:?base(name)
29?????{?}
30?
31?????public?override?void?Draw()
32?????{
33?????????Console.WriteLine("Draw?a"?+?_name.ToString());
34?????}
35?}
??? 現在我們要 對該圖像元素進行處理:在客戶端程序中,需要判斷返回對象的具體類型到底是基本圖像元素,還是復合圖像元素。如果是復合圖像元素,我們將要用遞歸去處理, 然而這種處理的結果卻增加了客戶端程序與復雜圖像元素內部結構之間的依賴,那么我們如何去解耦這種關系呢?我們希望的是客戶程序可以像處理基本圖像元素一 樣來處理復合圖像元素,這就要引入Composite模式了,需要把對于子對象的管理工作交給復合圖像元素,為了進行子對象的管理,它必須提供必要的Add(),Remove()等方法,類結構圖如下:
示意代碼:
?2?{
?3?????protected?string?_name;
?4?
?5?????public?Graphics(string?name)
?6?????{
?7?????????this._name?=?name;
?8?????}
?9?????public?abstract?void?Draw();
10?????public?abstract?void?Add();
11?????public?abstract?void?Remove();
12?}
13?
14?public?class?Picture?:?Graphics
15?{
16?????protected?ArrayList?picList?=?new?ArrayList();
17?
18?????public?Picture(string?name)
19?????????:?base(name)
20?????{?}
21?????public?override?void?Draw()
22?????{
23?????????Console.WriteLine("Draw?a"?+?_name.ToString());
24?
25?????????foreach?(Graphics?g?in?picList)
26?????????{
27?????????????g.Draw();
28?????????}
29?????}
30?
31?????public?override?void?Add(Graphics?g)
32?????{
33?????????picList.Add(g);
34?????}
35?????public?override?void?Remove(Graphics?g)
36?????{
37?????????picList.Remove(g);
38?????}
39?}
40?
41?public?class?Line?:?Graphics
42?{
43?????public?Line(string?name)
44?????????:?base(name)
45?????{?}
46?
47?????public?override?void?Draw()
48?????{
49?????????Console.WriteLine("Draw?a"?+?_name.ToString());
50?????}
51?????public?override?void?Add(Graphics?g)
52?????{?}
53?????public?override?void?Remove(Graphics?g)
54?????{?}
55?}
56?
57?public?class?Circle?:?Graphics
58?{
59?????public?Circle(string?name)
60?????????:?base(name)
61?????{?}
62?
63?????public?override?void?Draw()
64?????{
65?????????Console.WriteLine("Draw?a"?+?_name.ToString());
66?????}
67?????public?override?void?Add(Graphics?g)
68?????{?}
69?????public?override?void?Remove(Graphics?g)
70?????{?}
71?}
72?
73?public?class?Rectangle?:?Graphics
74?{
75?????public?Rectangle(string?name)
76?????????:?base(name)
77?????{?}
78?
79?????public?override?void?Draw()
80?????{
81?????????Console.WriteLine("Draw?a"?+?_name.ToString());
82?????}
83?????public?override?void?Add(Graphics?g)
84?????{?}
85?????public?override?void?Remove(Graphics?g)
86?????{?}
87?}
??? 這樣引入Composite模式后,客戶端程序不再依賴于復合圖像元素的內部實現了。然而,我們程序中仍然存在著問題,因為Line,Rectangle,Circle已經沒有了子對象,它是一個基本圖像元素,因此Add(),Remove()的方法對于它來說沒有任何意義,而且把這種錯誤不會在編譯的時候報錯,把錯誤放在了運行期,我們希望能夠捕獲到這類錯誤,并加以處理,稍微改進一下我們的程序:
?
?2?{
?3?????public?Line(string?name)
?4?????????:?base(name)
?5?????{?}
?6?
?7?????public?override?void?Draw()
?8?????{
?9?????????Console.WriteLine("Draw?a"?+?_name.ToString());
10?????}
11?????public?override?void?Add(Graphics?g)
12?????{?
13?????????//拋出一個我們自定義的異常
14?????}
15?????public?override?void?Remove(Graphics?g)
16?????{
17?????????//拋出一個我們自定義的異常
18?????}
19?}
??? 這樣改進以后,我們可以捕獲可能出現的錯誤,做進一步的處理。上面的這種實現方法屬于透明式的Composite模式,如果我們想要更安全的一種做法,就需要把管理子對象的方法聲明在樹枝構件Picture類里面,這樣如果葉子節點Line,Rectangle,Circle使用這些方法時,在編譯期就會出錯,看一下類結構圖:
示意代碼:
?2?{
?3?????protected?string?_name;
?4?
?5?????public?Graphics(string?name)
?6?????{
?7?????????this._name?=?name;
?8?????}
?9?????public?abstract?void?Draw();
10?}
11?
12?public?class?Picture?:?Graphics
13?{
14?????protected?ArrayList?picList?=?new?ArrayList();
15?
16?????public?Picture(string?name)
17?????????:?base(name)
18?????{?}
19?????public?override?void?Draw()
20?????{
21?????????Console.WriteLine("Draw?a"?+?_name.ToString());
22?
23?????????foreach?(Graphics?g?in?picList)
24?????????{
25?????????????g.Draw();
26?????????}
27?????}
28?
29?????public?void?Add(Graphics?g)
30?????{
31?????????picList.Add(g);
32?????}
33?????public?void?Remove(Graphics?g)
34?????{
35?????????picList.Remove(g);
36?????}
37?}
38?
39?public?class?Line?:?Graphics
40?{
41?????public?Line(string?name)
42?????????:?base(name)
43?????{?}
44?
45?????public?override?void?Draw()
46?????{
47?????????Console.WriteLine("Draw?a"?+?_name.ToString());
48?????}
49?}
50?
51?public?class?Circle?:?Graphics
52?{
53?????public?Circle(string?name)
54?????????:?base(name)
55?????{?}
56?
57?????public?override?void?Draw()
58?????{
59?????????Console.WriteLine("Draw?a"?+?_name.ToString());
60?????}
61?}
62?
63?public?class?Rectangle?:?Graphics
64?{
65?????public?Rectangle(string?name)
66?????????:?base(name)
67?????{?}
68?
69?????public?override?void?Draw()
70?????{
71?????????Console.WriteLine("Draw?a"?+?_name.ToString());
72?????}
73?}
??? 這種方式屬于安全式的Composite模式,在這種方式下,雖然避免了前面所討論的錯誤,但是它也使得葉子節點和樹枝構件具有不一樣的接口。這種方式和透明式的Composite各有優劣,具體使用哪一個,需要根據問題的實際情況而定。通過Composite模式,客戶程序在調用Draw()的時候不用再去判斷復雜圖像元素中的子對象到底是基本圖像元素,還是復雜圖像元素,看一下簡單的客戶端調用:
?1?public?class?App?2?{
?3?????public?static?void?Main()
?4?????{
?5?????????Picture?root?=?new?Picture("Root");
?6?
?7?????????root.Add(new?Line("Line"));
?8?????????root.Add(new?Circle("Circle"));
?9?
10?????????Rectangle?r?=?new?Rectangle("Rectangle");
11?????????root.Add(r);
12?
13?????????root.Draw();
Composite模式實現要點:?? ? ??
??? 1.Composite模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉化“一對一”的關系,使得客戶代碼可以一致地處理對象和對象容器,無需關心處理的是單個的對象,還是組合的對象容器。
??? 2.將“客戶代碼與復雜的對象容器結構”解耦是Composite模式的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對象容器的復內部實現結構——發生依賴關系,從而更能“應對變化”。
??? 3.Composite模式中,是將“Add和Remove等和對象容器相關的方法”定義在“表示抽象對象的Component類”中,還是將其定義在“表示對象容器的Composite類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。這里有可能違背面向對象的“單一職責原則”,但是對于這種特殊結構,這又是必須付出的代價。ASP.NET控件的實現在這方面為我們提供了一個很好的示范。
??? 4.Composite模式在具體實現中,可以讓父對象中的子對象反向追溯;如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的24组合模式(Composite Pattern)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美债仍遇冷,十个国家七个抛,英国狠心抛售
- 下一篇: 茅台酒、冰淇淋全都卖:“i茅台”用户近1