組合模式(Composite)
在現實生活中洒宝,存在很多“部分-整體”的關系雁歌,例如知残,大學中的部門與學院、總公司中的部門與分公司乏盐、學習用品中的書與書包制恍、生活用品中的衣月艮與衣柜以及廚房中的鍋碗瓢盆等。在軟件開發(fā)中也是這樣何吝,例如鹃唯,文件系統(tǒng)中的文件與文件夾坡慌、窗體程序中的簡單控件與容器控件等。對這些簡單對象與復合對象的處理跪者,如果用組合模式來實現會很方便熄求。
組合模式的定義與特點
- 組合(Composite)模式的定義:又稱部分整體模式,是用于把一組相似的對象當作一個單一的對象柜蜈。組合模式依據樹形結構來組合對象指巡,用來表示部分以及整體層次隶垮。這種類型的設計模式屬于結構型模式狸吞,它創(chuàng)建了對象組的樹形結構指煎。這種模式創(chuàng)建了一個包含自己對象組的類便斥。該類提供了修改相同對象組的方式。
- 組合(Composite)模式的優(yōu)點:1.高層模塊調用簡單像街。 2.節(jié)點自由增加晋渺。
- 組合(Composite)模式的缺點:在使用組合模式時木西,其葉子和數值的聲明都是實現類,而不是接口吗讶,違反了依賴倒置原則叼丑。
組合模式的結構與實現
1.模式的結構
組合模式包含以下主要角色。
- 抽象構件(Component)角色:它的主要作用是為樹葉構件和樹枝構件聲明公共接口纵寝,并實現它們的默認行為星立。在透明式的組合模式中抽象構件還聲明訪問和管理子類的接口;在安全式的組合模式中不聲明訪問和管理子類的接口室奏,管理工作由樹枝構件完成劲装。
- 樹葉構件(Leaf)角色:是組合中的葉節(jié)點對象占业,它沒有子節(jié)點,用于實現抽象構件角色中 聲明的公共接口南蹂。
- 樹枝構件(Composite)角色:是組合中的分支節(jié)點對象念恍,它有子節(jié)點晚顷。它實現了抽象構件角色中聲明的接口该默,它的主要作用是存儲和管理子部件策彤,通常包含 Add()、Remove()叽赊、GetChild() 等方法必搞。
組合模式分為透明式的組合模式和安全式的組合模式恕洲。
(1) 透明方式:在該方式中,由于抽象構件聲明了所有子類中的全部方法霜第,所以客戶端無須區(qū)別樹葉對象和樹枝對象泌类,對客戶端來說是透明的。但其缺點是:樹葉構件本來沒有 Add()弹砚、Remove() 及 GetChild() 方法枢希,卻要實現它們(空實現或拋異常)苞轿,這樣會帶來一些安全性問題。其結構圖如圖 1 所示瑟俭。
(2) 安全方式:在該方式中秀睛,將管理子構件的方法移到樹枝構件中蹂安,抽象構件和樹葉構件沒有對子對象的管理方法,這樣就避免了上一種方式的安全性問題畜号,但由于葉子和分支有不同的接口允瞧,客戶端在調用時要知道樹葉對象和樹枝對象的存在,所以失去了透明性痹升。其結構圖如圖 2 所示畦韭。
2.模式的實現
假如要訪問集合 c0={leaf1,{leaf2,leaf3}} 中的元素艺配,其對應的樹狀圖如圖 3 所示。
下面給出透明式的組合模式的實現代碼皮钠,與安全式的組合模式的實現代碼類似麦轰,只要對其做簡單修改就可以了砖织。
package composite;
import java.util.ArrayList;
public class CompositePattern
{
public static void main(String[] args)
{
Component c0=new Composite();
Component c1=new Composite();
Component leaf1=new Leaf("1");
Component leaf2=new Leaf("2");
Component leaf3=new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
//抽象構件
interface Component
{
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
//樹葉構件
class Leaf implements Component
{
private String name;
public Leaf(String name)
{
this.name=name;
}
public void add(Component c){ }
public void remove(Component c){ }
public Component getChild(int i)
{
return null;
}
public void operation()
{
System.out.println("樹葉"+name+":被訪問镶苞!");
}
}
//樹枝構件
class Composite implements Component
{
private ArrayList<Component> children=new ArrayList<Component>();
public void add(Component c)
{
children.add(c);
}
public void remove(Component c)
{
children.remove(c);
}
public Component getChild(int i)
{
return children.get(i);
}
public void operation()
{
for(Object obj:children)
{
((Component)obj).operation();
}
}
}
程序運行結果如下:
樹葉1:被訪問茂蚓!
樹葉2:被訪問!
樹葉3:被訪問晾浴!
組合模式的實例
下面我們以公司的層級結構為例牍白,先看一下這個例子中該公司的層級結構(該例選自大話設計模式——程杰著)。
這種部分與整體的關系切省,我們就可以考慮使用組合模式帕胆,下面采用組合模式的透明模式對其實現,UML圖如下:
1. 具體公司類
此為樹枝節(jié)點芙盘,實現添加儒老、移除记餐、顯示和履行職責四種方法。
public class ConcreteCompany extends Company {
private List<Company> companyList = new ArrayList<Company>();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company company) {
this.companyList.add(company);
}
@Override
public void remove(Company company) {
this.companyList.remove(company);
}
@Override
public void display(int depth) {
//輸出樹形結構
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
//下級遍歷
for (Company component : companyList) {
component.display(depth + 1);
}
}
@Override
public void lineOfDuty() {
//職責遍歷
for (Company company : companyList) {
company.lineOfDuty();
}
}
}
2. 人力資源部
葉子節(jié)點,add和remove方法空實現钠怯。
public class HRDepartment extends Company {
public HRDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int depth) {
//輸出樹形結構的子節(jié)點
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name + " : 員工招聘培訓管理");
}
}
3. 財務部
葉子節(jié)點晦炊,add和remove方法空實現。
public class FinanceDepartment extends Company {
public FinanceDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int depth) {
//輸出樹形結構的子節(jié)點
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name + " : 公司財務收支管理");
}
}
4. Client客戶端
public class Client {
public static void main(String[] args) {
//總公司
ConcreteCompany root = new ConcreteCompany("北京總公司");
root.add(new HRDepartment("總公司人力資源部"));
root.add(new FinanceDepartment("總公司財務部"));
//分公司
ConcreteCompany company = new ConcreteCompany("上海華東分公司");
company.add(new HRDepartment("華東分公司人力資源部"));
company.add(new FinanceDepartment("華東分公司財務部"));
root.add(company);
//辦事處
ConcreteCompany company1 = new ConcreteCompany("南京辦事處");
company1.add(new HRDepartment("南京辦事處人力資源部"));
company1.add(new FinanceDepartment("南京辦事處財務部"));
company.add(company1);
ConcreteCompany company2 = new ConcreteCompany("杭州辦事處");
company2.add(new HRDepartment("杭州辦事處人力資源部"));
company2.add(new FinanceDepartment("杭州辦事處財務部"));
company.add(company2);
System.out.println("結構圖:");
root.display(1);
System.out.println("\n職責:");
root.lineOfDuty();
}
}
運行結果如下:
????????組合模式這樣就定義了包含人力資源部和財務部這些基本對象和分公司、辦事處等組合對象的類層次結構霞捡。
????????基本對象可以被組合成更復雜的組合對象薄疚,而這個組合對象又可以被組合街夭,這樣不斷地遞歸下去,客戶代碼中板丽,任何用到基本對象的地方都可以使用組合對象了。
????????這里用了透明模式猖辫,用戶不用關心到底是處理一個葉節(jié)點還是處理一個組合組件住册,也就用不著為定義組合而寫一些選擇判斷語句了瓮具。簡單點說就是組合模式可以讓客戶一致地使用組合結構和單個對象。
組合模式的應用場景
前面分析了組合模式的結構與特點叹阔,下面分析它適用的以下應用場景传睹。
1.在需要表示一個對象整體與部分的層次結構的場合欧啤。
2.要求對用戶隱藏組合對象與單個對象的不同,用戶可以用統(tǒng)一的接口使用組合結構中的所有對象的場合邢隧。
組合模式的擴展
如果對前面介紹的組合模式中的樹葉節(jié)點和樹枝節(jié)點進行抽象倒慧,也就是說樹葉節(jié)點和樹枝節(jié)點還有子節(jié)點,這時組合模式就擴展成復雜的組合模式了炫贤,如 Java AWT/Swing 中的簡單組件 JTextComponent 有子類 JTextField付秕、JTextArea,容器組件 Container 也有子類 Window俩垃、Panel汰寓。復雜的組合模式的結構圖如下圖所示有滑。