組合(Composite)模式,又叫做樹形模式,主要用來處理樹形結構資料。是將一組物件組織成樹形結構,以表示一種“部分-整體”的層次結構。讓客戶端可以統一單個物件和組合物件的處理邏輯。
一、組合模式介紹
組合模式通過以樹形結構來表示“部分-整體”,使得使用者對葉物件和組合物件的使用具有一致性。也就是說在組合模式中,整個樹形結構的物件都屬於同一種型別,使用者可以對葉物件和組合物件統一處理。
1.1 組合模式分類
組合模式主要有透明式和安全式兩種分類,下面來分別說明
1.1.1 透明式組合模式
在該方式中,抽象構件宣告瞭所有子類中的全部方法,這樣實現抽象構件介面的所有子類都具備了全部方法,這樣的好處是葉節點和枝節點對於外界沒有任何區別,它們具備了完全一致的介面。但是對於葉節點有些本身不具備的方法,就可能會有安全隱患(空指標異常等)。其結構類圖如下所示:
Component
:抽象構件,為葉節點和樹枝節點宣告公共介面,以及訪問和管理子類的介面Composite
:樹枝構件,組合中的分支節點物件,作用是儲存和管理子部件Leaf
:樹葉構件,組合中的葉節點物件,用於繼承和實現抽象構件Client
:客戶端
1.1.2 安全式組合模式
前面提到透明式組合模式中,因為抽象構件宣告所有子類方法,有可能會造成安全問題。所以在安全式中,將管理葉節點的方法轉移到樹枝構件中,抽象構件和樹葉構件沒有對子物件的管理方法,這樣就避免了透明式組合模式中的安全問題。但是由於樹葉和樹枝構件有不同的介面,因此在使用時,就不能將兩種構件一概而論,對於客戶端呼叫方而言,就失去了透明性。其結構類圖如下所示:
Component
:抽象構件,為葉節點和樹枝節點宣告公共介面,沒有訪問和管理子類的介面Composite
:樹枝構件,組合中的分支節點物件,作用是儲存和管理子部件Leaf
:樹葉構件,組合中的葉節點物件,沒有對子類的管理方法Client
:客戶端
1.2 組合模式實現
根據上面的類圖,可以實現如下程式碼:
1.2.1 透明式組合模式實現
/**
* @description: 透明式抽象構件
* @author: wjw
* @date: 2022/4/3
*/
public interface Component {
/**公共操作方法**/
void operation();
/**
* 新增構件
* @param c 組合模式中的構件
*/
void add(Component c);
/**
* 移除構件
* @param c 組合模式中的構件
*/
void remove(Component c);
/**
* 獲得子物件
* @param t 子物件序號
* @return 子物件
*/
Component getChild(int t);
}
/**
* @description: 樹枝節點
* @author: wjw
* @date: 2022/4/3
*/
public class Composite implements Component{
private ArrayList<Component> children = new ArrayList<>();
@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
@Override
public void add(Component c) {
children.add(c);
}
@Override
public void remove(Component c) {
children.remove(c);
}
@Override
public Component getChild(int t) {
return children.get(t);
}
}
/**
* @description: 樹葉節點
* @author: wjw
* @date: 2022/4/3
*/
public class Leaf implements Component{
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("我是樹葉節點:" + name);
}
@Override
public void add(Component c) {
}
@Override
public void remove(Component c) {
}
@Override
public Component getChild(int t) {
return null;
}
}
/**
* @description: 客戶端類
* @author: wjw
* @date: 2022/4/3
*/
public class Client {
public static void main(String[] args) {
Component component = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
component.add(leaf1);
component.add(leaf2);
component.operation();
component.getChild(1).operation();
//這裡樹葉構件能呼叫add方法就會造成安全隱患
leaf1.add(leaf1);
}
}
客戶端執行結果:
我是樹葉節點:1
我是樹葉節點:2
我是樹葉節點:2
1.2.2 安全式組合模式實現
/**
* @description: 安全式抽象構件
* @author: wjw
* @date: 2022/4/3
*/
public interface Component {
/**公共操作方法**/
void operation();
}
/**
* @description: 樹枝節點
* @author: wjw
* @date: 2022/4/3
*/
public class Composite implements Component{
private ArrayList<Component> children = new ArrayList<>();
@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int t) {
return children.get(t);
}
}
/**
* @description: 樹葉節點
* @author: wjw
* @date: 2022/4/3
*/
public class Leaf implements Component{
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("我是樹葉節點:" + name);
}
}
/**
* @description: 客戶端類
* @author: wjw
* @date: 2022/4/3
*/
public class Client {
public static void main(String[] args) {
Composite composite = new Composite();
Leaf leaf1 = new Leaf("1");
Leaf leaf2 = new Leaf("2");
composite.add(leaf1);
composite.add(leaf2);
composite.operation();
}
}
客戶端測試結果:
我是樹葉節點:1
我是樹葉節點:2
二、組合模式應用場景
組合模式常見的應用場景主要是出現樹形結構的地方,比如檔案目錄,公司人員架構圖等等
2.1 公司人員架構
比如按照部門和員工組織成樹形結構,可以統一處理薪資:
/**
* @description: 人力資源抽象構件
* @author: wjw
* @date: 2022/4/3
*/
public abstract class HumanResource {
protected long id;
protected double salary;
public HumanResource(long id) {
this.id = id;
}
public long getId() {
return id;
}
/**
* 計算工資
* @return 工資結果
*/
public abstract double calculateSalary();
}
/**
* @description: 部門樹枝構件
* @author: wjw
* @date: 2022/4/3
*/
public class Department extends HumanResource{
private List<HumanResource> humanResources = new ArrayList<>();
public Department(long id) {
super(id);
}
@Override
public double calculateSalary() {
double totalSalary = 0;
for (HumanResource humanResource : humanResources) {
totalSalary += humanResource.calculateSalary();
}
this.salary = totalSalary;
return totalSalary;
}
public void addHumanResource(HumanResource humanResource) {
humanResources.add(humanResource);
}
}
/**
* @description: 員工樹葉構件
* @author: wjw
* @date: 2022/4/3
*/
public class Employee extends HumanResource{
public Employee(long id, double salary) {
super(id);
this.salary = salary;
}
@Override
public double calculateSalary() {
return salary;
}
}
參考資料
《設計模式之美》
http://c.biancheng.net/view/1373.html
《Java 設計模式》
《設計模式:可複用物件導向軟體的基礎》