設計模式,結構模式之組合模式

1 概述

組合模式(Composite Pattern),是指把一類有共同抽象的對象組合在一起,形成樹狀結構,來表示部分-整體的關係。

2 組合模式

我們經常會遇到一些對象有相同的行爲,同時對象之間又有層級結構。像這種情況,使用組合模式可以使系統高內聚,低耦合。通過把類組合成樹形結構,調用者能夠以統一的方式對待單個對象和整體結構,忽略個體和整體之間的差異。實現組合模式一般需要:

  1. 定義一個接口/抽象類,並定義對象的公共行爲,其中包括維護對象間層次結構的方法。
  2. 定義對象的實體,實現接口/抽象類中的方法。
  3. 按樹形結構,將所有的實體組合在一起,形成一個整體。

3 案例

看一個例子。所有公司的員工,其實都是同一族的對象,有相似的屬性與行爲,同時,員工之間又有層級結構,很適合用組合模式來表示員工之間的關係:

public class Test {
    public static void main(String[] args) {
        // 構建層級結構
        Director director = new Director("Polly");
        Manager frontendManager = new Manager("Lilei");
        Manager backendManager = new Manager("Hanmeimei");
        Engineer jsEngineer = new Engineer("Lily");
        Engineer ueDesigner = new Engineer("Lucy");
        Engineer javaEngineer = new Engineer("Jim");
        Engineer dbAdmin = new Engineer("Kate");
        director.addChild(frontendManager);
        director.addChild(backendManager);
        frontendManager.addChild(jsEngineer);
        frontendManager.addChild(ueDesigner);
        backendManager.addChild(javaEngineer);
        backendManager.addChild(dbAdmin);

        // 如對待整體如對待單個對象一般
        director.work();
    }
}

// 定義了Employee的公共方法,同時定義了添加/刪除節點的方法
public interface Employee<T extends Employee> {
    String getName();
    Collection<T> getChildren() throws OperationNotSupportedException;
    void addChild(T employee) throws OperationNotSupportedException;
    void removeChild(T employee) throws OperationNotSupportedException;
    void work();
}
public class Director implements Employee<Manager> {
    private String name;
    private Collection<Manager> children = new ArrayList<>();
    Director(String name) {
        this.name = name;
    };
    @Override
    public String getName() {
        return name;
    }
    @Override
    public Collection getChildren() {
        return children;
    }
    @Override
    public void addChild(Manager employee) {
        children.add(employee);
    }
    @Override
    public void removeChild(Manager employee) {
        children.remove(employee);
    }
    // 循環調用子節點的方法
    @Override
    public void work() {
        System.out.println("Director " + name + " gives command to his subordinate...");
        for (Employee child : children) {
            child.work();
        }
    }
}
public class Manager implements Employee<Engineer> {
    private String name;
    private Collection<Engineer> children = new ArrayList<>();
    Manager(String name) {
        this.name = name;
    };
    @Override
    public String getName() {
        return name;
    }
    @Override
    public Collection getChildren() {
        return children;
    }
    @Override
    public void addChild(Engineer employee) {
        children.add(employee);
    }
    @Override
    public void removeChild(Engineer employee) {
        children.remove(employee);
    }
    // 循環調用子節點的方法
    @Override
    public void work() {
        System.out.println("Manager " + name + " gives command to his subordinates...");
        for (Employee child : children) {
            child.work();
        }
    }
}
public class Engineer implements Employee {
    private String name;
    Engineer(String name) {
        this.name = name;
    };
    @Override
    public String getName() { 
        return name; 
    }
    @Override
    public Collection getChildren() throws OperationNotSupportedException {
        throw new OperationNotSupportedException("No child under engineer.");
    }
    @Override
    public void addChild(Employee employee) throws OperationNotSupportedException {
        throw new OperationNotSupportedException("Can not add child for engineer.");
    }
    @Override
    public void removeChild(Employee employee) throws OperationNotSupportedException {
        throw new OperationNotSupportedException("Can not remove child for engineer.");
    }
    @Override
    public void work() {
        System.out.println("Engineer " + name + " is coding...");
    }
}

輸出:

Director Polly gives command to his subordinate...
Manager Lilei gives command to his subordinates...
Engineer Lily is coding...
Engineer Lucy is coding...
Manager Hanmeimei gives command to his subordinates...
Engineer Jim is coding...
Engineer Kate is coding...

uml

運用組合模式,我們可以用一致的行爲來操作整體與單個對象,顯著降低了系統的複雜度。同時,樹形結構也很易於擴展,方便日後的維護。

java.awt.Container就運用了組合模式,各個組件之間,其實是個樹形結構,Container是所有組件的根結點:

public class Container extends Component {
    private java.util.List<Component> component = new ArrayList<>();
    // 添加組件
    protected void add(Component comp, Object constraints, int index) {
        ...
        //index == -1 means add to the end.
        if (index == -1) {
            component.add(comp);
        } else {
            component.add(index, comp);
        }
        comp.parent = this;
        comp.setGraphicsConfiguration(thisGC);
        ...
    }
    // 移除組件
    public void remove(int index) {
        synchronized (getTreeLock()) {
            ...
            component.remove(index);
            comp.setGraphicsConfiguration(null);
            ...
        }
    }
    // 調用updateGraphicsData方法,以及子節點的updateGraphicsData方法
    boolean updateGraphicsData(GraphicsConfiguration gc) {
        boolean ret = super.updateGraphicsData(gc);
        for (Component comp : component) {
            if (comp != null) {
                ret |= comp.updateGraphicsData(gc);
            }
        }
        return ret;
    }
    ...
}

4 總結

當對象之間存在層級關係的時候,可以考慮使用組合模式,統一單個對象和組合對象,降低系統複雜度。

文中例子的github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章