1. 程式人生 > 實用技巧 >大話設計模式讀書筆記(組合模式)

大話設計模式讀書筆記(組合模式)

人物:小菜,大鳥

事件:小菜遇到了一個難題,他的公司開發了一個OA系統,然後甲方希望在分公司推廣,共用同一套系統,於是大鳥推出了組合模式,傳給了小菜,解決了問題。


組合模式:

1.簡述了組合模式,闡述了原理,實現,還有部分疑問答疑

2.用組合模式解決小菜的問題例項

組合模式

1.概念:將物件組合成樹形結構以表示'部分-整體'的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性

2.結構圖:

3.程式碼如下:

Component類,是組合中的物件宣告介面,在適當情況下,實現所有類共有的預設行為。宣告一個介面用於訪問和管理Component的子部件:

public abstract
class Component { protected String name; public Component(String name) { this.name = name; } public abstract void add(Component c); public abstract void remove(Component c); public abstract void display(int depth); }

Leaf類,在組合中表示葉節點物件,葉節點沒有子節點,所以add和remove實現沒有意義,如下的實現實現方式可以消除葉節點和枝節點物件在抽象層次的區別,它們就可以具備完全一致的介面:

@Slf4j
public class Leaf extends Component {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public void add(Component c) {
        log.info("cannot add to a leaf");
    }

    @Override
    public void remove(Component c) {
        log.info("cannot remove from a leaf");
    }

    @Override
    
public void display(int depth) { log.info(depth + name); } }

Composite類,定義枝節點行為,用來儲存子部件,在Component介面中實現與子部件有關的操作,比如進行add和remove:

@Slf4j
public class Composite extends Component {
    private List<Component> children = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void add(Component c) {
        children.add(c);
    }

    @Override
    public void remove(Component c) {
        children.remove(c);
    }

    @Override
    public void display(int depth) {
        log.info(depth + name);
        for (Component component : children) {
            component.display(depth + 2);
        }
    }
}

客戶端程式碼:

public class ComponentClient {
    public static void main(String[] args) {
        Component root = new Composite("root");
        root.add(new Leaf("Leaf A"));
        root.add(new Leaf("Leaf B"));

        Component comp = new Composite("Composite X");
        comp.add(new Leaf("Leaf XA"));
        comp.add(new Leaf("Leaf XB"));

        root.add(comp);

        Component comp2 = new Composite("Composite XY");
        comp2.add(new Leaf("Leaf XYA"));
        comp2.add(new Leaf("Leaf XYB"));
        comp.add(comp2);

        root.add(new Leaf("Leaf C"));

        Leaf leaf = new Leaf("Leaf D");
        root.add(leaf);
        root.remove(leaf);
        root.display(1);
    }
}

輸出結果:

1root
3Leaf A
3Leaf B
3Composite X
5Leaf XA
5Leaf XB
5Composite XY
7Leaf XYA
7Leaf XYB
3Leaf C

展示如下:

4.透明方式與安全方式

(1)問:葉子節點不是不能再分枝麼,為什麼Leaf類中還有add和remove

答:現在的處理方式叫做透明方式。這樣在Component中會宣告所有用來管理子物件的方法,其中包括add,remove等,這樣Component介面的所有子類都具備了add和remove,所以好處就是使得葉子節點和枝節點對於外界就沒有區別了,它們具備完全一致的行為介面,當然問題也很明顯,因為Leaf類本身不具備add和remove,所以這兩個方法的實現沒有意義。

(2)問:在1問中所說的問題,那我不希望做無用功,可以add和remove方法麼?

答:可以,那就需要安全方式。就不在Component介面中實現add和remove方法,而是在Composite中實現,這樣雖然解決了這個問題,但由於不夠透明,所以樹葉樹枝類不再具有相同的介面,導致客戶端的呼叫需要加入判斷,帶來了不便。

5.什麼時候選擇用組合模式呢?

答:當需求中是體現整體和部分層次的結構時,以及你希望使用者可以忽略組合物件和單個物件的不同,統一地使用組合結構中的所有物件時,就該考慮用組合模式了。

結合組合模式實現公司OA系統

1.程式碼結構圖:

2.程式碼實現:

公司類,抽閒類或介面:

public abstract class Company {
    protected String name;

    public Company(String name) {
        this.name = name;
    }

    /**
     * 增加
     *
     * @param c
     */
    public abstract void add(Company c);

    /**
     * 移除
     *
     * @param c
     */
    public abstract void remove(Company c);

    /**
     * 展示
     *
     * @param depth
     */
    public abstract void display(int depth);

    /**
     * 履行職責
     */
    public abstract void lineOfDuty();

}

ConcreteCompany類,具體公司類,實現介面,樹枝節點:

@Slf4j
public class ConcreteCompany extends Company {
    private List<Company> children = new ArrayList<>();

    public ConcreteCompany(String name) {
        super(name);
    }

    @Override
    public void add(Company c) {
        children.add(c);
    }

    @Override
    public void remove(Company c) {
        children.remove(c);
    }

    @Override
    public void display(int depth) {
        log.info(depth + name);
        for (Company component : children) {
            component.display(depth + 2);
        }
    }

    @Override
    public void lineOfDuty() {
        for (Company component : children) {
            component.lineOfDuty();
        }
    }
}

HRDepartment類和FinanceDepartment類,人力資源部類和財務部類,葉子節點:

@Slf4j
public class HRDepartment extends Company {
    public HRDepartment(String name) {
        super(name);
    }

    @Override
    public void add(Company c) {
    }

    @Override
    public void remove(Company c) {
    }

    @Override
    public void display(int depth) {
        log.info(depth + name);
    }

    @Override
    public void lineOfDuty() {
        log.info("{}員工招聘培訓機構", name);
    }
}
@Slf4j
public class FinanceDepartment extends Company {
    public FinanceDepartment(String name) {
        super(name);
    }

    @Override
    public void add(Company c) {
    }

    @Override
    public void remove(Company c) {
    }

    @Override
    public void display(int depth) {
        log.info(depth + name);
    }

    @Override
    public void lineOfDuty() {
        log.info("{}公司財務收支管理", name);
    }
}

客戶端呼叫:

@Slf4j
public class ComponentClient {
    public static void main(String[] args) {
        ConcreteCompany root = new ConcreteCompany("北京總公司");
        root.add(new HRDepartment("總公司人力資源部"));
        root.add(new FinanceDepartment("總公司財務部"));

        ConcreteCompany comp = new ConcreteCompany("上海滑動分公司");
        comp.add(new HRDepartment("華東分公司人力資源部"));
        comp.add(new FinanceDepartment("華東分公司財務部"));
        root.add(comp);

        ConcreteCompany comp1 = new ConcreteCompany("南京辦事處");
        comp1.add(new HRDepartment("南京辦事處人力資源部"));
        comp1.add(new FinanceDepartment("南京辦事處財務部"));
        comp.add(comp1);

        ConcreteCompany comp2 = new ConcreteCompany("杭州辦事處");
        comp2.add(new HRDepartment("杭州辦事處人力資源部"));
        comp2.add(new FinanceDepartment("杭州辦事處財務部"));
        comp.add(comp2);

        log.info("結構圖:");
        root.display(1);

        log.info("職責為:");
        root.lineOfDuty();
    }
}

組合模式的好處:

(1)使用者不關心處理的是葉子節點還是組合元件,這樣就不用定義組合而寫判斷語句了

(2)基本物件可以被組合成複雜的組合物件,而組合物件又可以被組合,這樣可以一直遞迴下去,這樣任何用到基礎物件的地方就都可以用到組合物件了。