設(shè)計模式之組合模式
設(shè)計模式專欄
1、什么是組合模式?
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
組合模式(Composite Pattern):將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu), 使得用戶對單個對象和組合對象的使用具有一致性。
說人話:用于處理樹形結(jié)構(gòu)數(shù)據(jù)。
2、組合模式定義

①、Component 抽象構(gòu)件角色
定義參加組合對象的共有方法和屬性,可以定義一些默認的行為或?qū)傩浴?/p>
②、Leaf 葉子節(jié)點
葉子對象,其下再也沒有其他的子節(jié)點,是遍歷的最小單位。
③、Composite 樹枝構(gòu)件
樹枝對象,作用是組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結(jié)構(gòu)。
3、組合模式通用代碼實現(xiàn)
/**
?*?個體和整體的抽象
?*/
public?abstract?class?Component?{
????//?個體和整體都有的共享
????public?void?doSomething(){
????????//?通用業(yè)務(wù)邏輯
????????System.out.println("通用業(yè)務(wù)邏輯");
????}
}
/**
?*?樹枝節(jié)點
?*/
public?class?Composite?extends?Component{
????//?構(gòu)件容器
????private?ArrayList?componentArrayList?=?new?ArrayList<>();
????//?增加一個葉子節(jié)點或者樹枝節(jié)點
????public?void?add(Component?component){
????????this.componentArrayList.add(component);
????}
????//?刪除一個葉子節(jié)點或者樹枝節(jié)點
????public?void?remove(Component?component){
????????this.componentArrayList.remove(component);
????}
????//?獲取分支下所有葉子節(jié)點和樹枝節(jié)點
????public?List?getChildren() {
????????return?this.componentArrayList;
????}
}
/**
?*?葉子節(jié)點
?*/
public?class?Leaf?extends?Component?{
????//?覆寫父類方法
????@Override
????public?void?doSomething()?{
????????//?葉子節(jié)點邏輯
????????System.out.println("葉子節(jié)點邏輯");
????}
}
測試:
public?class?ClientTest?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建一個根節(jié)點
????????Composite?root?=?new?Composite();
????????root.doSomething();
????????//?創(chuàng)建一個樹枝構(gòu)件
????????Composite?branch?=?new?Composite();
????????//?創(chuàng)建一個葉子節(jié)點
????????Leaf?leaf?=?new?Leaf();
????????//?串聯(lián)起來
????????root.add(branch);
????????branch.add(leaf);
????????display(root);
????}
????//?通過遞歸遍歷數(shù)
????public?static?void?display(Composite?root){
????????for(Component?c?:?root.getChildren()){
????????????if(c?instanceof?Leaf){?//?葉子節(jié)點
????????????????c.doSomething();
????????????}else{
????????????????display((Composite)?c);
????????????}
????????}
????}
}
這里我們在舉一個例子:
假設(shè)我們在開發(fā)一個 OA 系統(tǒng)(辦公自動化系統(tǒng))。公司的組織結(jié)構(gòu)包含部門和員工兩種數(shù)據(jù)類型。其中,部門又可以包含子部門和員工。
我們希望在內(nèi)存中構(gòu)建整個公司的人員架構(gòu)圖(部門、子部門、員工的隸屬關(guān)系),并且提供接口計算出部門的薪資成本(隸屬于這個部門的所有員工的薪資和)。

/**
?*?部門類和員工類的抽象類
?*/
public?abstract?class?HumanResource?{
????protected?long?id;
????protected?double?salary;
????public?HumanResource(long?id){
????????this.id?=?id;
????}
????public?long?getId(){
????????return?id;
????}
????public?abstract?double?calculateSalary();
}
public?class?Department?extends?HumanResource{
????private?List?subNodes?=?new?ArrayList<>();
????public?Department(long?id){
????????super(id);
????}
????@Override
????public?double?calculateSalary()?{
????????double?totalSalary?=?0d;
????????for?(HumanResource?hr?:?subNodes){
????????????totalSalary?+=?hr.calculateSalary();
????????}
????????this.salary?=?totalSalary;
????????return?totalSalary;
????}
????public?void?addSubNode(HumanResource?humanResource){
????????subNodes.add(humanResource);
????}
}
public?class?Employee?extends?HumanResource{
????public?Employee(long?id,double?salary){
????????super(id);
????????this.salary?=?salary;
????}
????@Override
????public?double?calculateSalary()?{
????????return?salary;
????}
}
測試:
public?class?PersonClientTest?{
????private?static?final?long?ORGANIZATION_ROOT_ID?=?1;
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建總部門
????????Department?root?=?new?Department(ORGANIZATION_ROOT_ID);
????????//?創(chuàng)建子部門
????????Department?branch?=?new?Department(2L);
????????//?創(chuàng)建員工
????????Employee?employee1?=?new?Employee(21L,2000);
????????Employee?employee2?=?new?Employee(22L,4000);
????????root.addSubNode(branch);
????????branch.addSubNode(employee1);
????????branch.addSubNode(employee2);
????????double?v?=?root.calculateSalary();
????????System.out.println(v);
????}
????private?void?buildOrganization(Department?department){
????????//?根據(jù)?部門id?查詢數(shù)據(jù)庫?所有下屬部門?id
????????//?List?subDepartmentIds?=?departmentRepo.getSubDepartmentIds(department.getId());
????????List?subDepartmentIds?=?new?ArrayList<>();
????????for?(Long?subDepartmentId?:?subDepartmentIds){
????????????Department?subDepartment?=?new?Department(subDepartmentId);
????????????department.addSubNode(subDepartment);
????????????buildOrganization(subDepartment);
????????}
????????//?根據(jù)部門id?查詢數(shù)據(jù)庫?其關(guān)聯(lián)員工所有?id
????????//?List?employeeIds?=?employeeRepo.getDepartmentEmployeeIds(department.getId());
????????List?employeeIds?=?new?ArrayList<>();
????????for?(Long?employeeId?:?employeeIds){
????????????//?根據(jù)?employeeId?查詢數(shù)據(jù)庫得到?salary
????????????//?假設(shè)為?1000
????????????double?salary?=?1000d;
????????????department.addSubNode(new?Employee(employeeId,salary));
????????}
????}
}
4、組合模式優(yōu)點
①、高層模塊調(diào)用簡單
一棵樹形機構(gòu)中的所有節(jié)點都是Component, 局部和整體對調(diào)用者來說沒有任何區(qū)別,也就是說, 高層模塊不必關(guān)心自己處理的是單個對象還是整個組合結(jié)構(gòu), 簡化了高層模塊的代碼。
②、節(jié)點自由增加
使用了組合模式后, ?如果想增加一個樹枝節(jié)點、 葉子節(jié)點都很容易, 只要找到它的父節(jié)點就成, 非常容易擴展, 符合開閉原則, 對以后的維護非常有利。
5、組合模式應用場景
只要是樹形結(jié)構(gòu),就可以考慮使用組合模式。
①、維護和展示部分-整體關(guān)系的場景, 如樹形菜單、 文件和文件夾管理。
②、從一個整體中能夠獨立出部分模塊或功能的場景
關(guān)于我
可樂是一個熱愛技術(shù)的Java程序猿,公眾號「IT可樂」定期分享有趣有料的精品原創(chuàng)文章!

非常感謝各位人才能看到這里,原創(chuàng)不易,文章如果有幫助可以關(guān)注、點贊、分享或評論,這都是對我的莫大支持!
愿你我人生盡量沒有遺憾的事,愿你我都能奔赴在各自想去的路上。
