1.前言
組合是一種整體與部分的關(guān)系,即對象與其內(nèi)部對象之間的關(guān)系征炼。通過之前的外觀模式析既,體會到對象內(nèi)部是可以很復(fù)雜的。最常見的情況便是谆奥,一個對象由多個對象組合而成眼坏,而部分參與組合的對象又是由多個對象組合而成,很像是樹狀結(jié)構(gòu)酸些。
這種結(jié)構(gòu)的對象該如何訪問呢宰译?作為開發(fā)人員肯定想忽略掉這些區(qū)別,以一種統(tǒng)一的方式來處理每個對象魄懂。
2.概念
組合模式將對象組合成樹形結(jié)構(gòu)以表示“整體-部分”的層次結(jié)構(gòu)沿侈,使得用戶對組合對象和單個對象的使用具有一致性。這種統(tǒng)一的風(fēng)格便是抽象的體現(xiàn)市栗,需通過抽象類或接口來定義缀拭,由各種對象去實現(xiàn)。
這種模式特別適合模塊化的結(jié)構(gòu)肃廓,即層級結(jié)構(gòu)明顯智厌,功能劃分細(xì)致,像公司的組織架構(gòu)盲赊,又或者計算機的文件系統(tǒng)。
3.場景
一個大的集團(tuán)公司通常都是由許多子公司組成敷扫,每個子公司都有自己的業(yè)務(wù)范圍哀蘑。但只要是公司,必然有不同的部門葵第,每個部門都有自己的工作目標(biāo)绘迁。只不過,總公司統(tǒng)籌所有子公司而已卒密。
4.寫法
// 1.定義訪問所有對象的共有接口
public abstract class Function {
protected String name;
// 2.實現(xiàn)缺省行為
public Function(String name) {
this.name = name;
}
public abstract void work();
}
// 1.實現(xiàn)組合對象及共有行為
public class Company extends Function {
private List<Function> mFunctions = new ArrayList<>();
public Company(String name) {
super(name);
}
@Override
public void work() {
System.out.println("公司名為 " + name);
if (mFunctions != null && !mFunctions.isEmpty()) {
for (Function function : mFunctions) {
function.work();
}
}
}
// 2.組合對象的特有行為
public void addFunction(Function function) {
mFunctions.add(function);
}
public void removeFunction(Function function) {
mFunctions.remove(function);
}
public Function getFunction(int index) {
return mFunctions.get(index);
}
}
// 1.實現(xiàn)單個對象及共有行為
public class Department extends Function {
public Department(String name) {
super(name);
}
@Override
public void work() {
System.out.println("部門名為 " + name);
}
// 2.可添加單個對象的特有行為
}
public class Client {
public static void main(String[] args) {
// 1.構(gòu)造集團(tuán)公司
Company group = new Company("XXX集團(tuán)");
Company company = new Company("XXX公司");
Department onlineShopping = new Department("網(wǎng)絡(luò)購物事業(yè)部");
Department mobile = new Department("移動事業(yè)部");
group.addFunction(company);
group.addFunction(onlineShopping);
group.addFunction(mobile);
// 2.構(gòu)造子公司
Company subcompany = new Company("XXX子公司");
Department localLife = new Department("本地生活事業(yè)部");
Department cloud = new Department("云產(chǎn)品事業(yè)部");
company.addFunction(subcompany);
company.addFunction(localLife);
company.addFunction(cloud);
group.work();
}
}
通過上面的代碼缀台,細(xì)心的朋友肯定會發(fā)現(xiàn)一個問題,那就是違背了依賴倒置原則哮奇。在Client類中直接使用Function的具體實現(xiàn)類膛腐,增加了代碼間的耦合度睛约,還忽視了Function的抽象作用。目前主流的開發(fā)方式就是面向接口編程哲身,除了把焦點放在接口的設(shè)計外辩涝,還應(yīng)注重接口的使用。
Function類無法使用勘天,關(guān)鍵是因為只聲明了公司和部門的共有方法怔揩,在實現(xiàn)類中才添加特有方法,對調(diào)用者屏蔽了具體實現(xiàn)的細(xì)節(jié)脯丝,這是安全的組合模式商膊。若希望Function類得到使用,可以讓其包括所有特有方法的聲明宠进,對外公開完整的訪問結(jié)構(gòu)晕拆,稱為透明的組合模式。
public abstract class Function {
protected String name;
public Function(String name) {
this.name = name;
}
public abstract void work();
// 添加Company特有方法的聲明
public abstract void addFunction(Function function);
public abstract void removeFunction(Function function);
public abstract Function getFunction(int index);
}
public class Department extends Function {
public Department(String name) {
super(name);
}
@Override
public void work() {
System.out.println("部門名為 " + name);
}
// 1.實現(xiàn)Company特有方法
@Override
public void addFunction(Function function) {
throw new UnsupportedOperationException("No function");
}
@Override
public void removeFunction(Function function) {
throw new UnsupportedOperationException("No function");
}
@Override
public Function getFunction(int index) {
throw new UnsupportedOperationException("No function");
}
// 2.可添加單個對象的特有行為
}
5.總結(jié)
在實際開發(fā)中砰苍,尤其是安卓開發(fā)潦匈,SDK提供的UI組件架構(gòu)設(shè)計就是使用組合模式。大家可以回顧一下赚导,TextView和ViewGroup繼承自View茬缩,而共有方法包括了onMeasure()
、onLayout()
和onDraw()
等吼旧。之所以這樣凰锡,除了看中組合模式便于訪問結(jié)構(gòu)樹中的節(jié)點外,還利于增刪節(jié)點圈暗,為樹形結(jié)構(gòu)的面向?qū)ο髮崿F(xiàn)提供靈活的解決方案掂为。