24组合模式(Composite Pattern)
動(dòng)機(jī)(Motivate):
??? 組合模式有時(shí)候又叫做部分-整體模式,它使我們樹(shù)型結(jié)構(gòu)的問(wèn)題中,模糊了簡(jiǎn)單元素和復(fù)雜元素的概念,客戶程序可以向處理簡(jiǎn)單元素一樣來(lái)處理復(fù)雜元素,從而使得客戶程序與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦。
意圖(Intent):
????將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
??? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? -----------《設(shè)計(jì)模式》GOF
結(jié)構(gòu)圖(Struct):
??? ?? ?? ?? ?? ?? ?? ?? ???
生活中的例子:
??? ?? ?? ?? ?? ?? ?? ??????????????????????
適用性:???
??? 1.你想表示對(duì)象的部分-整體層次結(jié)構(gòu)
??? 2.你希望用戶忽略組合對(duì)象與單個(gè)對(duì)象的不同,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象。
代碼實(shí)現(xiàn):
????這里我們用繪圖這個(gè)例子來(lái)說(shuō)明Composite模式,通過(guò)一些基本圖像元素(直線、圓等)以及一些復(fù)合圖像元素(由基本圖像元素組合而成)構(gòu)建復(fù)雜的圖形樹(shù)。在設(shè)計(jì)中我們對(duì)每一個(gè)對(duì)象都配備一個(gè)Draw()方法,在調(diào)用時(shí),會(huì)顯示相關(guān)的圖形。可以看到,這里復(fù)合圖像元素它在充當(dāng)對(duì)象的同時(shí),又是那些基本圖像元素的一個(gè)容器。先看一下基本的類結(jié)構(gòu)圖:
??? ?? ?? ???
圖中橙色的區(qū)域表示的是復(fù)合圖像元素。
示意性代碼:
?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?????????//返回所有的子對(duì)象
25?????}
26?}
而其他作為樹(shù)枝構(gòu)件,實(shí)現(xiàn)代碼如下:
?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?}
??? 現(xiàn)在我們要 對(duì)該圖像元素進(jìn)行處理:在客戶端程序中,需要判斷返回對(duì)象的具體類型到底是基本圖像元素,還是復(fù)合圖像元素。如果是復(fù)合圖像元素,我們將要用遞歸去處理, 然而這種處理的結(jié)果卻增加了客戶端程序與復(fù)雜圖像元素內(nèi)部結(jié)構(gòu)之間的依賴,那么我們?nèi)绾稳ソ怦钸@種關(guān)系呢?我們希望的是客戶程序可以像處理基本圖像元素一 樣來(lái)處理復(fù)合圖像元素,這就要引入Composite模式了,需要把對(duì)于子對(duì)象的管理工作交給復(fù)合圖像元素,為了進(jìn)行子對(duì)象的管理,它必須提供必要的Add(),Remove()等方法,類結(jié)構(gòu)圖如下:
示意代碼:
?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模式后,客戶端程序不再依賴于復(fù)合圖像元素的內(nèi)部實(shí)現(xiàn)了。然而,我們程序中仍然存在著問(wèn)題,因?yàn)?/span>Line,Rectangle,Circle已經(jīng)沒(méi)有了子對(duì)象,它是一個(gè)基本圖像元素,因此Add(),Remove()的方法對(duì)于它來(lái)說(shuō)沒(méi)有任何意義,而且把這種錯(cuò)誤不會(huì)在編譯的時(shí)候報(bào)錯(cuò),把錯(cuò)誤放在了運(yùn)行期,我們希望能夠捕獲到這類錯(cuò)誤,并加以處理,稍微改進(jìn)一下我們的程序:
?
?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?????????//拋出一個(gè)我們自定義的異常
14?????}
15?????public?override?void?Remove(Graphics?g)
16?????{
17?????????//拋出一個(gè)我們自定義的異常
18?????}
19?}
??? 這樣改進(jìn)以后,我們可以捕獲可能出現(xiàn)的錯(cuò)誤,做進(jìn)一步的處理。上面的這種實(shí)現(xiàn)方法屬于透明式的Composite模式,如果我們想要更安全的一種做法,就需要把管理子對(duì)象的方法聲明在樹(shù)枝構(gòu)件Picture類里面,這樣如果葉子節(jié)點(diǎn)Line,Rectangle,Circle使用這些方法時(shí),在編譯期就會(huì)出錯(cuò),看一下類結(jié)構(gòu)圖:
示意代碼:
?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模式,在這種方式下,雖然避免了前面所討論的錯(cuò)誤,但是它也使得葉子節(jié)點(diǎn)和樹(shù)枝構(gòu)件具有不一樣的接口。這種方式和透明式的Composite各有優(yōu)劣,具體使用哪一個(gè),需要根據(jù)問(wèn)題的實(shí)際情況而定。通過(guò)Composite模式,客戶程序在調(diào)用Draw()的時(shí)候不用再去判斷復(fù)雜圖像元素中的子對(duì)象到底是基本圖像元素,還是復(fù)雜圖像元素,看一下簡(jiǎn)單的客戶端調(diào)用:
?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模式實(shí)現(xiàn)要點(diǎn):?? ? ??
??? 1.Composite模式采用樹(shù)形結(jié)構(gòu)來(lái)實(shí)現(xiàn)普遍存在的對(duì)象容器,從而將“一對(duì)多”的關(guān)系轉(zhuǎn)化“一對(duì)一”的關(guān)系,使得客戶代碼可以一致地處理對(duì)象和對(duì)象容器,無(wú)需關(guān)心處理的是單個(gè)的對(duì)象,還是組合的對(duì)象容器。
??? 2.將“客戶代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)”解耦是Composite模式的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對(duì)象容器的復(fù)內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)——發(fā)生依賴關(guān)系,從而更能“應(yīng)對(duì)變化”。
??? 3.Composite模式中,是將“Add和Remove等和對(duì)象容器相關(guān)的方法”定義在“表示抽象對(duì)象的Component類”中,還是將其定義在“表示對(duì)象容器的Composite類”中,是一個(gè)關(guān)乎“透明性”和“安全性”的兩難問(wèn)題,需要仔細(xì)權(quán)衡。這里有可能違背面向?qū)ο蟮摹皢我宦氊?zé)原則”,但是對(duì)于這種特殊結(jié)構(gòu),這又是必須付出的代價(jià)。ASP.NET控件的實(shí)現(xiàn)在這方面為我們提供了一個(gè)很好的示范。
??? 4.Composite模式在具體實(shí)現(xiàn)中,可以讓父對(duì)象中的子對(duì)象反向追溯;如果父對(duì)象有頻繁的遍歷需求,可使用緩存技巧來(lái)改善效率。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的24组合模式(Composite Pattern)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 美债仍遇冷,十个国家七个抛,英国狠心抛售
- 下一篇: 第六节: EF高级属性(二) 之延迟加载