PS1句:終究還是選擇CSDN來整理發表這幾年的知識點,該文章平行遷移到CSDN。由于CSDN也支持MarkDown語法了,牛逼啊!
【工匠若水 http://blog.csdn.net/yanbober】 瀏覽前1篇《設計模式(結構型)之橋接模式(Bridge Pattern)》 http://blog.csdn.net/yanbober/article/details/45366781
組合模式又叫做部份-整體模式,使我們在樹型結構的問題中模糊簡單元素和復雜元素的概念,客戶程序可以像處理簡單元素1樣來處理復雜的元素,從而使得客戶程序與復雜元素的內部結構解耦。組合模式可以優化處理遞歸或分級數據結構。有許多關于分級數據結構的例子,使得組合模式非常有用武之地。
概念: 組合多個對象構成樹形結構以表示具有“整體―部份”關系的層次結構。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有1致性,組合模式又可以稱為“整體―部份”(Part-Whole)模式,它是1種對象結構型模式。
重點: 組合模式結構重要核心模塊:
抽象構件(Component)
組合中的對象聲明接口,在適當的情況下實現所有類共有接口的默許行動。聲明1個接口用于訪問和管理Component子部件。
樹葉構件(Leaf)
在組合中表示樹的葉子結點對象,葉子結點沒有子結點。
容器構件(Composite)
定義有枝節點的部件,在Component接口中實現與子部件有關操作,如增加(add)和刪除(remove)等。
核心:組合模式的關鍵是定義1個抽象構件類,它既可以代表Leaf,又可以代表Composite,而客戶端針對該抽象構件類進行編程,不必知道它到底表示的是葉子還是容器,可以對其進行統1處理。同時容器對象與抽象構件類之間還建立1個聚合關聯關系,在容器對象中既可以包括葉子,也能夠包括容器,以此實現遞歸組合,構成1個樹形結構。
在具有整體和部份的層次結構中,希望通過1種方式疏忽整體與部份的差異,客戶端可以1致地對待它們。
在1個使用面向對象語言開發的系統中需要處理1個樹形結構。
在1個系統中能夠分離出葉子對象和容器對象,而且它們的類型不固定,需要增加1些新的類型。
假定問題環境:
就拿Android開發中最多見的1種情形來分析吧。我們有1種需求是遍歷全部cache文件夾下的所有文件及文件夾,打印出來(實際指定不是打印,而是邏輯操作,這里演示而已)。以下我們先不使用組合模式。
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
class MediaFile {
private String mName;
private String mDescription;
public MediaFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "MediaFile# Name="+mName+", Description="+mDescription;
}
}
class OfficeFile {
private String mName;
private String mDescription;
public OfficeFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "OfficeFile# Name="+mName+", Description="+mDescription;
}
}
class PackageFile {
private String mName;
private String mDescription;
public PackageFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "PackageFile# Name="+mName+", Description="+mDescription;
}
}
class BaseFolder {
private String mName;
private String mDescription;
private List<BaseFolder> mBaseFolderList = new ArrayList<>();
private List<MediaFile> mMediaFileList = new ArrayList<>();
private List<OfficeFile> mOfficeFileList = new ArrayList<>();
private List<PackageFile> mPackageFileList = new ArrayList<>();
public BaseFolder(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
public void addBaseFolder(BaseFolder baseFolder) {
mBaseFolderList.add(baseFolder);
}
public void addMediaFile(MediaFile mediaFile) {
mMediaFileList.add(mediaFile);
}
public void addOfficeFile(OfficeFile officeFile) {
mOfficeFileList.add(officeFile);
}
public void addPackageFile(PackageFile packageFile) {
mPackageFileList.add(packageFile);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("*****BaseFolder# enter folder is:"+mName+"
");
for (BaseFolder baseFolder : mBaseFolderList) {
builder.append(baseFolder.toString()+"
");
}
for (MediaFile mediaFile : mMediaFileList) {
builder.append(mediaFile.toString()+"
");
}
for (OfficeFile officeFile : mOfficeFileList) {
builder.append(officeFile.toString()+"
");
}
for (PackageFile packageFile : mPackageFileList) {
builder.append(packageFile.toString()+"
");
}
return builder.toString();
}
}
public class Main {
public static void main(String[] args) {
BaseFolder folder = new BaseFolder("cache", "app cache dir.");
MediaFile mediaFile = new MediaFile("test.png", "test picture.");
folder.addMediaFile(mediaFile);
mediaFile = new MediaFile("haha.mp4", "test video.");
folder.addMediaFile(mediaFile);
PackageFile packageFile = new PackageFile("yanbo.apk", "an android application.");
folder.addPackageFile(packageFile);
BaseFolder childDir = new BaseFolder("word", "office dir.");
OfficeFile officeFile = new OfficeFile("rrrr.doc", "office doc file.");
childDir.addOfficeFile(officeFile);
folder.addBaseFolder(childDir);
System.out.println(folder.toString());
}
}
有了如上實現那末問題來了。。。
如上代碼你會發現客戶端代碼過量地依賴于容器對象復雜的內部實現結構,容器對象內部實現結構的變化將引發客戶代碼的頻繁變化,帶來了代碼保護復雜、可擴大性差等弊端。組合模式的引入將在1定程度上解決這些問題。
準么辦?升個級唄,用1下組合模式。
升個級吧
給上面代碼來個重構? 那就依照組合模式結構重要核心模塊來對上面代碼升級重構1把吧。
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
abstract class AbstractComponent {
public abstract void add(AbstractComponent c);
public abstract AbstractComponent get(int index);
public abstract String getString();
}
class MediaFile extends AbstractComponent {
private String mName;
private String mDescription;
public MediaFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "MediaFile# Name="+mName+", Description="+mDescription;
}
}
class OfficeFile extends AbstractComponent {
private String mName;
private String mDescription;
public OfficeFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "OfficeFile# Name="+mName+", Description="+mDescription;
}
}
class PackageFile extends AbstractComponent {
private String mName;
private String mDescription;
public PackageFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "PackageFile# Name="+mName+", Description="+mDescription;
}
}
class BaseFolder extends AbstractComponent {
private String mName;
private String mDescription;
private List<AbstractComponent> abstractComponents = new ArrayList<>();
public BaseFolder(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
abstractComponents.add(c);
}
@Override
public AbstractComponent get(int index) {
return abstractComponents.get(index);
}
@Override
public String getString() {
StringBuilder builder = new StringBuilder("*****BaseFolder# enter folder is:"+mName+"
");
for (AbstractComponent baseFolder : abstractComponents) {
builder.append(baseFolder.getString()+"
");
}
return builder.toString();
}
}
public class Main {
public static void main(String[] args) {
AbstractComponent folder = new BaseFolder("cache", "app cache dir.");
AbstractComponent mediaFile = new MediaFile("test.png", "test picture.");
folder.add(mediaFile);
mediaFile = new MediaFile("haha.mp4", "test video.");
folder.add(mediaFile);
AbstractComponent packageFile = new PackageFile("yanbo.apk", "an android application.");
folder.add(packageFile);
BaseFolder childDir = new BaseFolder("word", "office dir.");
AbstractComponent officeFile = new OfficeFile("rrrr.doc", "office doc file.");
childDir.add(officeFile);
folder.add(childDir);
System.out.println(folder.getString());
}
}
上例通過組合模式代碼具有了良好的可擴大性,在增加新的文件類型時,不必修改現有類庫代碼,只需增加1個新的文件類作為AbstractFile類的子類便可(不用在BaseFolder中再去修改添加),但是由于在AbstractFile中聲明了大量用于管理和訪問成員構件的方法,例如add(),我們需要在新增的文件類中實現這些方法,提供對應的毛病提示和異常處理。這下你就明白了,其實如上代碼還可以繼續優化的,把每一個子類實現的毛病提示和異常處理寫入抽象的基類中作為默許處理,這樣也能夠減少代碼重復。哈哈。
當發現需求中是體現部份與整體層次結構時,和你希望用戶可以疏忽組合對象與單個對象的不同,統1地使用組合結構中的所有對象時,就應當斟酌組合模式了。
組合模式優點以下:
組合模式缺點以下:
【工匠若水 http://blog.csdn.net/yanbober】 繼續瀏覽《設計模式(結構型)之裝潢者模式(Decorator Pattern)》 http://blog.csdn.net/yanbober/article/details/45395747
下一篇 列表 環 判定 初始位置