類圖:
精髓
- 樹形結(jié)構(gòu)效五,可以表現(xiàn)出對(duì)象的層次結(jié)構(gòu),與部分-整體的關(guān)系
- (精髓中的精髓)節(jié)點(diǎn)實(shí)現(xiàn)同一個(gè)公共接口瞒渠,使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性(如果組合對(duì)象和單個(gè)對(duì)象行為上沒什么區(qū)別(如文件夾和文件皮璧,行為就基本不一樣,如果只是一個(gè)純粹的樹狀結(jié)構(gòu)的任務(wù)執(zhí)行計(jì)劃遥巴,每個(gè)節(jié)點(diǎn)都是單個(gè)對(duì)象而已千康,只是執(zhí)行順序不同,就沒必要抽象公共接口了铲掐,判斷葉子節(jié)點(diǎn)就是判斷children字段是否為空)拾弃,抽象接口這一層也可以免去)....也正是因?yàn)樗泄?jié)點(diǎn)都屬于同一類,才能如此方便的構(gòu)造一個(gè)樹結(jié)構(gòu)出來摆霉。
tips:leaf與composite實(shí)現(xiàn)的共同接口盡量用抽象類實(shí)現(xiàn)豪椿,可以將一些具體類別不能實(shí)現(xiàn)的方法設(shè)置為默認(rèn)拋出異常(headfirst作者提倡以拋異常的方式處理奔坟,如果需要在調(diào)用處理時(shí)判斷節(jié)點(diǎn)類型,就與模式的抽象初衷違背了)
網(wǎng)上找的一個(gè)殺毒軟件殺毒例子
1.抽象leaf和組合對(duì)象的公共接口
abstract class AbstractFile {
public void add(AbstractFile file){
System.out.println("對(duì)不起搭盾,不支持該方法咳秉!");
}
public abstract void remove(AbstractFile file){
System.out.println("對(duì)不起,不支持該方法增蹭!");
}
public abstract AbstractFile getChild(int i){
System.out.println("對(duì)不起滴某,不支持該方法!");
}
public abstract void killVirus(); //所有類型都會(huì)進(jìn)行殺毒
}
2.分別實(shí)現(xiàn)每一種節(jié)點(diǎn)
- 圖片類型(<b>leaf</b>)
class ImageFile extends AbstractFile {
private String name;
public ImageFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("對(duì)不起滋迈,不支持該方法霎奢!");
}
public void remove(AbstractFile file) {
System.out.println("對(duì)不起,不支持該方法饼灿!");
}
public AbstractFile getChild(int i) {
System.out.println("對(duì)不起筷转,不支持該方法耙替!");
return null;
}
public void killVirus() {
//模擬殺毒
System.out.println("----對(duì)圖像文件'" + name + "'進(jìn)行殺毒");
}
}
- 文本文檔(<b>leaf</b>)
class TextFile extends AbstractFile {
private String name;
public TextFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("對(duì)不起,不支持該方法!");
}
public void remove(AbstractFile file) {
System.out.println("對(duì)不起王悍,不支持該方法!");
}
public AbstractFile getChild(int i) {
System.out.println("對(duì)不起睛挚,不支持該方法靠汁!");
return null;
}
public void killVirus() {
//模擬殺毒
System.out.println("----對(duì)文本文件'" + name + "'進(jìn)行殺毒");
}
}
- 視頻(<b>leaf</b>)
class VideoFile extends AbstractFile {
private String name;
public VideoFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("對(duì)不起,不支持該方法皆疹!");
}
public void remove(AbstractFile file) {
System.out.println("對(duì)不起疏橄,不支持該方法!");
}
public AbstractFile getChild(int i) {
System.out.println("對(duì)不起略就,不支持該方法捎迫!");
return null;
}
public void killVirus() {
//模擬殺毒
System.out.println("----對(duì)視頻文件'" + name + "'進(jìn)行殺毒");
}
}
- 文件夾(<b>composite</b>)
class Folder extends AbstractFile {
//定義集合fileList,用于存儲(chǔ)AbstractFile類型的成員
private ArrayList<AbstractFile> fileList=new ArrayList<AbstractFile>();
private String name;
public Folder(String name) {
this.name = name;
}
public void add(AbstractFile file) {
fileList.add(file);
}
public void remove(AbstractFile file) {
fileList.remove(file);
}
public AbstractFile getChild(int i) {
return fileList.get(i);
}
public void killVirus() { //關(guān)鍵代碼表牢。窄绒。。
System.out.println("****對(duì)文件夾'" + name + "'進(jìn)行殺毒"); //模擬殺毒
//遞歸調(diào)用成員構(gòu)件的killVirus()方法
for(AbstractFile obj : fileList) {
obj.killVirus();
}
}
}
3.客戶端測(cè)試類
class Client {
public static void main(String args[]) {
//針對(duì)抽象構(gòu)件編程
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;
folder1 = new Folder("Sunny的資料");
folder2 = new Folder("圖像文件");
folder3 = new Folder("文本文件");
folder4 = new Folder("視頻文件");
file1 = new ImageFile("小龍女.jpg");
file2 = new ImageFile("張無忌.gif");
file3 = new TextFile("九陰真經(jīng).txt");
file4 = new TextFile("葵花寶典.doc");
file5 = new VideoFile("笑傲江湖.rmvb");
folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);
//從“Sunny的資料”節(jié)點(diǎn)開始進(jìn)行殺毒操作
folder1.killVirus();
}
}
/*
刪除出結(jié)果:
****對(duì)文件夾'Sunny的資料'進(jìn)行殺毒
****對(duì)文件夾'圖像文件'進(jìn)行殺毒
----對(duì)圖像文件'小龍女.jpg'進(jìn)行殺毒
----對(duì)圖像文件'張無忌.gif'進(jìn)行殺毒
****對(duì)文件夾'文本文件'進(jìn)行殺毒
----對(duì)文本文件'九陰真經(jīng).txt'進(jìn)行殺毒
----對(duì)文本文件'葵花寶典.doc'進(jìn)行殺毒
****對(duì)文件夾'視頻文件'進(jìn)行殺毒
----對(duì)視頻文件'笑傲江湖.rmvb'進(jìn)行殺毒
*/
4.如果不使用組合模式怎么實(shí)現(xiàn)
文件夾:
//文件夾類
class Folder { //沒有實(shí)現(xiàn)共同的接口
private String name;
/*
需要定義各種類型的子節(jié)點(diǎn)的存儲(chǔ)崔兴,區(qū)別對(duì)待
*/
//定義集合folderList彰导,用于存儲(chǔ)Folder類型的成員
private ArrayList<Folder> folderList = new ArrayList<Folder>();
//定義集合imageList,用于存儲(chǔ)ImageFile類型的成員
private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();
//定義集合textList敲茄,用于存儲(chǔ)TextFile類型的成員
private ArrayList<TextFile> textList = new ArrayList<TextFile>();
public Folder(String name) {
this.name = name;
}
/*
以下的所有操作都必須區(qū)別對(duì)待
*/
//增加新的Folder類型的成員
public void addFolder(Folder f) {
folderList.add(f);
}
//增加新的ImageFile類型的成員
public void addImageFile(ImageFile image) {
imageList.add(image);
}
//增加新的TextFile類型的成員
public void addTextFile(TextFile text) {
textList.add(text);
}
//需提供三個(gè)不同的方法removeFolder()位谋、removeImageFile()和removeTextFile()來刪除成員,代碼省略
//需提供三個(gè)不同的方法getChildFolder(int i)折汞、getChildImageFile(int i)和getChildTextFile(int i)來獲取成員倔幼,代碼省略
/*
每一種類型都要單獨(dú)去調(diào)用殺毒
*/
public void killVirus() {
System.out.println("****對(duì)文件夾'" + name + "'進(jìn)行殺毒"); //模擬殺毒
//如果是Folder類型的成員,遞歸調(diào)用Folder的killVirus()方法
for(Folder obj : folderList) {
obj.killVirus();
}
//如果是ImageFile類型的成員爽待,調(diào)用ImageFile的killVirus()方法
for(ImageFile obj : imageList) {
obj.killVirus();
}
//如果是TextFile類型的成員损同,調(diào)用TextFile的killVirus()方法
for(TextFile obj : textList) {
obj.killVirus();
}
}
}
<b>當(dāng)然其他的原子節(jié)點(diǎn)也沒有實(shí)現(xiàn)公共接口</b>
下面看客戶端調(diào)用:
class Client {
public static void main(String args[]) {
Folder folder1,folder2,folder3;
folder1 = new Folder("Sunny的資料");
folder2 = new Folder("圖像文件");
folder3 = new Folder("文本文件");
ImageFile image1,image2;
image1 = new ImageFile("小龍女.jpg");
image2 = new ImageFile("張無忌.gif");
TextFile text1,text2;
text1 = new TextFile("九陰真經(jīng).txt");
text2 = new TextFile("葵花寶典.doc");
folder2.addImageFile(image1);
folder2.addImageFile(image2);
folder3.addTextFile(text1);
folder3.addTextFile(text2);
folder1.addFolder(folder2);
folder1.addFolder(folder3);
folder1.killVirus();
}
}
1.文件夾類Folder的設(shè)計(jì)和實(shí)現(xiàn)都非常復(fù)雜翩腐,需要定義多個(gè)集合存儲(chǔ)不同類型的成員,而且需要針對(duì)不同的成員提供增加膏燃、刪除和獲取等管理和訪問成員的方法茂卦,存在大量的冗余代碼,系統(tǒng)維護(hù)較為困難组哩;
2.由于系統(tǒng)沒有提供抽象層等龙,客戶端代碼必須有區(qū)別地對(duì)待充當(dāng)容器的文件夾Folder和充當(dāng)葉子的ImageFile和TextFile,無法統(tǒng)一對(duì)它們進(jìn)行處理伶贰;客戶端要構(gòu)造這個(gè)樹特別復(fù)雜蛛砰,只有folde類型才有add接口
3.系統(tǒng)的靈活性和可擴(kuò)展性差,如果需要增加新的類型的葉子和容器都需要對(duì)原有代碼進(jìn)行修改黍衙,例如如果需要在系統(tǒng)中增加一種新類型的視頻文件VideoFile泥畅,則必須修改Folder類的源代碼,否則無法在文件夾中添加視頻文件琅翻。