一、引言
在軟件開發過程中,我們經常會遇到處理簡單對象和復合對象的情況,例如對操作系統中目錄的處理就是這樣的一個例子,因為目錄可以包括單獨的文件,也可以包括文件夾,文件夾又是由文件組成的,由於簡單對象和復合對象在功能上區別,導致在操作過程中必須區分簡單對象和復合對象,這樣就會導致客戶調用帶來不必要的麻煩,然而作為客戶,它們希望能夠始終一致地對待簡單對象和復合對象。然而組合模式就是解決這樣的問題。下面讓我們看看組合模式是怎樣解決這個問題的。
二、組合模式的詳細介紹
2.1 組合模式的定義
組合模式允許你將對象組合成樹形結構來表現”部分-整體“的層次結構,使得客戶以一致的方式處理單個對象以及對象的組合。下面我們用繪制的例子來詳細介紹組合模式,圖形可以由一些基本圖形元素組成(如直線,圓等),也可以由一些復雜圖形組成(由基本圖形元素組合而成),為了使客戶對基本圖形和復雜圖形的調用保持一致,我們使用組合模式來達到整個目的。
組合模式實現的最關鍵的地方是——簡單對象和復合對象必須實現相同的接口。這就是組合模式能夠將組合對象和簡單對象進行一致處理的原因。
2.2 組合模式的實現
舉例:
家族譜的編寫:
男性:可傳宗接代,也有權利把一些人剔除族譜。
女性:記錄到家譜中,但不能傳宗接代。
理解:每一個小家庭中,爸爸媽媽和我,都是爸爸做主,可踢出我跟媽媽中的任何一個,也可增加任何一個。組件模式中的組件可以是單獨一個對象組成,也可以是多個組件組成(一個家庭,甚至一個家庭的多級延續);
類圖:
族員共性代碼:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113//// <summary>
/// //族人 抽象出來的族人共性
/// </summary>
public
abstract
class
Father
{
//族人的姓名
protected
string
name =
string
.Empty;
public
string
Name
{
get
{
return
name;
}
}
//增加後代
public
abstract
void
Add(Father boy);
//逐出家譜
public
abstract
void
Remove(Father boy);
//定義所有族人,做個簡介
public
abstract
void
Intro();
}家族成員代碼
//男性後代
public
class
Boy : Father
{
//構造函數
public
Boy() { }
public
Boy(
string
Name)
{
this
.name = Name;
}
List<Father> myFamily =
new
List<Father>();
//自我簡介
public
override
void
Intro()
{
Console.WriteLine(
"我是:{0};"
, Name);
foreach
(Father f
in
myFamily)
{
f.Intro();
}
}
//增加後代
public
override
void
Add(Father boy)
{
myFamily.Add(boy);
}
//逐出家譜
public
override
void
Remove(Father boy)
{
myFamily.Remove(boy);
}
}
//女性後代
public
class
Gril : Father
{
//構造函數
public
Gril() { }
public
Gril(
string
Name)
{
this
.name = Name;
}
//自我簡介
public
override
void
Intro()
{
Console.WriteLine(
"我是:{0};"
, Name);
}
//不能添加
public
override
void
Add(Father store)
{
throw
new
NotImplementedException();
}
//不能刪除
public
override
void
Remove(Father store)
{
throw
new
NotImplementedException();
}
}客戶端代碼:
public
static
void
Main()
{
//爺爺取老婆
Boy yeye =
new
Boy(
"爺爺"
);
Gril nainai =
new
Gril(
"奶奶"
);
yeye.Add(nainai);
//爺爺要孩子
Boy baba =
new
Boy(
"爸爸"
);
Gril gugu =
new
Gril(
"姑姑"
);
yeye.Add(gugu);
yeye.Add(baba);
//爸爸要我
Boy me =
new
Boy(
"me"
);
baba.Add(me);
//我要孩子
Boy son =
new
Boy(
"son"
);
me.Add(son);
//爺爺的大家庭,族譜做介紹
yeye.Intro();
Console.Read();
}
2.3組合模式的類圖
看完了上面,讓我們具體看看組合模式的類圖來理清楚組合模式中類之間的關系。
透明式的組合模式類圖:
組合模式中涉及到三個角色:
三、組合模式的優缺點
優點:
組合模式使得客戶端代碼可以一致地處理對象和對象容器,無需關系處理的單個對象,還是組合的對象容器。
將”客戶代碼與復雜的對象容器結構“解耦。
可以更容易地往組合對象中加入新的構件。
缺點:使得設計更加復雜。客戶端需要花更多時間理清類之間的層次關系。(這個是幾乎所有設計模式所面臨的問題)。
注意的問題:
有時候系統需要遍歷一個樹枝結構的子構件很多次,這時候可以考慮把遍歷子構件的結構存儲在父構件裡面作為緩存。
客戶端盡量不要直接調用樹葉類中的方法(在我上面實現就是這樣的,創建的是一個樹枝的具體對象,應該使用GraphicscomplexGraphics = new ComplexGraphics("一個復雜圖形和兩條線段組成的復雜圖形");),而是借用其父類(Graphics)的多態性完成調用,這樣可以增加代碼的復用性。
四、組合模式的使用場景
在以下情況下應該考慮使用組合模式:
五、組合模式在.Net中的應用
組合模式在.NET 中最典型的應用就是應用與WinForms和Web的開發中,在.Net類庫中,都為這兩個平台提供了很多現有的控件,然而System.Windows.Forms.dll中System.Windows.Forms.Control類就應用了組合模式,因為控件包括Label、TextBox等這樣的簡單控件,同時也包括GroupBox、DataGrid這樣復合的控件,每個控件都需要調用OnPaint方法來進行控件顯示,為了表示這種對象之間整體與部分的層次結構,微軟把Control類的實現應用了組合模式(確切地說應用了透明式的組合模式)。
六、總結
到這裡組合模式的介紹就結束了,組合模式解耦了客戶程序與復雜元素內部結構,從而使客戶程序可以向處理簡單元素一樣來處理復雜元素。