《一天一模式》— 組合模式

1、組合模式的概念

組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性。掌握組合模式的重點是要理解清楚 「部分/總體」 還有 」單個對象「 與 "組合對象" 的含義。 java

聽懂了這句話就不用往下看了,說明你會了。bash

聽不懂我以爲也正常,若是用一句話能學會就沒人看書了。像我這種笨人,都是學會了一個模式,而後往它的定義上套。函數

2、何時使用組合模式

  • 須要某種樹形結構,能夠容納菜單、子菜單和菜單項;
  • 須要可以在各個菜單之間隨意遊走,同級的菜單類型能夠不一樣;
  • 須要可以在遍歷時,隨意遍歷各個菜單;

也就是說,樹形結構中的菜單,子菜單是容器,菜單項是內容。this

組合模式是要讓容器和內容具備一致性,創造出遞歸結構,而且在某一個場景下,容器和內容能夠無差異使用,圖中第二層既能夠是Leaf,也能夠是Node。spa

常見的場景有:部門樹、功能菜單樹、字典樹等等,在遇到可遞歸的樹形結構需求時,均可以考慮一下是否須要使用組合模式。code

3、怎麼使用組合模式

以一個需求爲例進行說明,仍是汽車相關的。component

汽車廠商將汽車分爲品牌、車系、車型、他們以前是屬性關係,品牌下有多個車系,車系下有多個車型,具體例子:對象

品牌是奔馳。blog

奔馳下有多個車系,A級,C級,E級。繼承

每一個車系下有多個車型,A級下有A180,A200,A220。

需求是,打印出全部的車系和車型,在打印時進行名稱美化,傳入車系和車型均可以完成名稱美化。

3.1 不使用組合模式帶來的弊端

不使用組合模式來建立這個結構。

  • 首先建立一個品牌對象(Brand),裏面有一個車系(Series)的ArrayList集合;
  • 而後建立一個車系對象(Series),裏面有一個車型(Model)的ArrayList集合;
  • 最後建立一個車系對象(Model);

一個典型的一對多關係,具體代碼以下:

/**
 * 品牌
 */
public class Brand {

    private List<Series> series = new ArrayList<Series>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Series s) {
        series.add(s);
    }

    public void remove(Series s) {
        series.remove(s);
    }

    public Series getChild(int i) {
        return series.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Series> iterator = series.iterator();
        while(iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 車系
 */
public class Series {

    private List<Model> model = new ArrayList<Model>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Model m) {
        model.add(m);
    }

    public void remove(Model m) {
        model.remove(m);
    }

    public Model getChild(int i) {
        return model.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Model> iterator = model.iterator();
        while (iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 車型
 */
public class Model {

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void print() {
        System.out.println(getName());
    }

}

/**
 * 運行輸出
 * 奔馳
 * 奔馳A系
 * A100
 * A200
 * 奔馳C系
 * C100
 * C200
 */
public class Client {

    public static void main(String[] args) {
        Brand brand = new Brand("奔馳");
        Series series = new Series("奔馳A系");
        brand.add(series);
        Model a100 = new Model("A100");
        Model a200 = new Model("A200");
        series.add(a100);
        series.add(a200);
        Series series1 = new Series("奔馳C系");
        brand.add(series1);
        Model c100 = new Model("C100");
        Model c200 = new Model("C200");
        series1.add(c100);
        series1.add(c200);
        brand.print();
    }

}

需求是,打印出全部的車系和車型,在打印時進行名稱美化,傳入車系和車型均可以完成名稱美化。

第一個弊端 — 通用性差

這裏要寫這個函數就有問題,因爲車系和車型的類型不一樣,因此要單獨編寫函數,實現基本一致:

public String beautiful(Series series) {
    return "★" + series.getName() + "★";
}

public String beautiful(Model model) {
    return "★" + model.getName() + "★";
}

第二個弊端 — 結構不靈活

如今需求有變動:

車系是一個樹形結構,也就是說Series對象的子對象中,能夠存放車型,也能夠存放車系。

回顧一下Series的代碼:

public class Series {

    private List<Model> model = new ArrayList<Model>();

    ......

}

用於存放子對象的集合,泛型類型是Model,不能保存Series對象。

3.2 使用組合模式帶來解決這個問題

直接上類圖,使用組合模式後的類圖以下:

回顧概念:

  • 將對象組合成樹形結構以表示「部分-總體」的層次結構
  • 組合模式使得用戶對單個對象和組合對象的使用具備一致性

如今,品牌、車系、車型都繼承自組件(Component)。品牌中的集合,泛型類型是Component,車系中的集合泛型類型也是Component。這樣就作到了概念中的兩點,對象仍是樹形結構來表示部分和總體。同時,因爲對象自己繼承自Component,它內部的子對象集合也是Component,因此作到了一致性。

下面是組合模式下的代碼:

/**
 * 組件對象
 */
public abstract class Component {

    public String getName() {
        throw new UnsupportedOperationException();
    }

    public void add(Component component) {
        throw new UnsupportedOperationException();
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }

    public Component getChild(int i) {
        throw new UnsupportedOperationException();
    }

    public void print() {
        throw new UnsupportedOperationException();
    }

    public String beautiful(Component component) {
        return "★" + component.getName() + "★";
    }

}

/**
 * 品牌,繼承自Component
 */
public class Brand extends Component {

    // 內部不在是存放具體類型,如今改爲了Component類型,繼承自Component的對象均可以加入
    private List<Component> series = new ArrayList<Component>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Component component) {
        series.add(component);
    }

    public void remove(Component component) {
        series.remove(component);
    }

    public Component getChild(int i) {
        return series.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Component> iterator = series.iterator();
        while(iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 車系,繼承自Component
 */
public class Series extends Component {

    // 內部不在是存放具體類型,如今改爲了Component類型,繼承自Component的對象均可以加入
    private List<Component> model = new ArrayList<Component>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Component component) {
        model.add(component);
    }

    public void remove(Component component) {
        model.remove(component);
    }

    public Component getChild(int i) {
        return model.get(i);
    }

    public void print() {
        System.out.println(beautiful(this));
        Iterator<Component> iterator = model.iterator();
        while (iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 車型
 */
public class Model extends Component {

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void print() {
        System.out.println(beautiful(this));
    }

}

組合模式的使用方式:

public class Client {

    public static void main(String[] args) {
        // 建立品牌
        Component brand = new Brand("奔馳");
        // 建立車系A
        Component series = new Series("奔馳A系");
        // 把車系A加到品牌
        brand.add(series);
        // 建立兩個車型
        Component a100 = new Model("A100");
        Component a200 = new Model("A200");
        series.add(a100);
        series.add(a200);

        // 建立車系C
        Component series1 = new Series("奔馳C系");
        // 加到品牌
        brand.add(series1);

        // 在建立2個車型添加到車系C
        Component c100 = new Model("C100");
        Component c200 = new Model("C200");
        series1.add(c100);
        series1.add(c200);
        // 這裏!在車型c100和c200平級的節點,建立一個子車系
        Component series2 = new Series("奔馳C系運動款");
        series1.add(series2);
        // 在子車系下建立2個車型
        Component c100s = new Model("C100S");
        Component c200s = new Model("C200S");
        series2.add(c100s);
        series2.add(c200s);
        // 輸出整個的樹
        brand.print();
    }

}

// 輸出結果
奔馳
★奔馳A系★
★A100★
★A200★
★奔馳C系★
★C100★
★C200★
★奔馳C系運動款★
★C100S★
★C200S★

用組合模式解決了需求中的2個點:

1.使用一個函數操做車系,車型

// 可使用一個方式來操做車系、車型、品牌等對象,只要是這個樹結構下的節點都可以操做
public String beautiful(Component component) {
    return "★" + component.getName() + "★";
}

2.隨時能夠添加子節點

// 在建立2個車型添加到車系C
Component c100 = new Model("C100");
Component c200 = new Model("C200");
series1.add(c100);
series1.add(c200);
// 這裏!在車型c100和c200平級的節點,建立一個子車系
Component series2 = new Series("奔馳C系運動款");

輸出結果是:

★奔馳C系★(車系)
  |-★C100★(車型)
  |-★C200★(車型)
  |-★奔馳C系運動款★(車系)
     |-★C100S★(車型)
     |-★C200S★(車型)

樹的任意一級能夠存聽任意的對象(只要是繼承自Component),這種方式解決了第二個需求。

4、總結

組合模式,個人一個簡單理解是,在兩個場景下應該去考慮使用:

  1. 當你的對象結構是樹,樹的各個層級並非有一個對象組合而成的,但這些對象都有相同的業務操做(上面的例子中的display方法);
  2. 面相後續的需求,有可能每一層保存的對象類型也不必定相同(例如上面的例子中,車系下不僅有車型,還有子車系);

組合模式的作法就很簡單了,主要也是兩點:

  1. 樹上的全部節點都繼承一個Component對象(抽象對象);
  2. 對象種的ArrayList的泛型對象使用Component;

以上就是組合模式的一些理解, 有不足之處請你們矯正,謝謝。

相關文章
相關標籤/搜索