設計模式系列之「組合模式」

小Y科普:家譜又稱族譜、宗譜等。它以記載父系家族世系、人物為中心,由正史中的帝王本紀及王侯列傳、年表等演變而來。是一種特殊的文獻,就其內容而言,是中國五千年文明史中具有平民特色的文獻,記載的是同宗共祖血緣集團世系人物和事跡等方面情況的歷史圖籍。

Now,how to 實現(xiàn) 小J's 族譜。

一、小J族譜簡略版

從最頂層的第一代J開始,一代代往下記錄下去,這很明顯就是一個樹狀結構,現(xiàn)在小Y要做的就是通過最合適的方式把小J的族譜圖遍歷出來。

二、樹狀圖的拆解利器-組合模式

1.組合模式的定義

組合模式也叫合成模式,有時又叫做部分-整體模式,主要是用來描述部分與整體的關系,將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。

2.組合模式的角色介紹(組合模式有兩種實現(xiàn):安全模式和透明模式)

  • Component抽象構件角色
    定義參加組合對象的共有方法和屬性,可以定義一些默認的行為或屬性。

  • Leaf葉子構件
    Leaf葉子構件葉子對象,其下再也沒有其他的分支,也就是遍歷的最小單位。

  • Composite樹枝構件
    樹枝對象,它的作用是組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結構。組合模式的重點就在樹枝構件。

3.組合模式的使用場景

  • 只要是樹形結構或者只要是要體現(xiàn)局部和整體的關系的時候,而且這種關系還可能比較深,就要考慮一下組合模式。

  • 從一個整體中能夠獨立出部分模塊或功能的場景。

  • 維護和展示部分-整體關系的場景。

4.安全模式和透明模式的具體實現(xiàn)

(1)安全模式

①抽象構件

public abstract class Component {
    //個體和整體都具有
    public void operation(){
        //編寫業(yè)務邏輯
    }
}

②樹枝構件

public class Composite extends Component {
    //構件容器
    private List<Component> componentArrayList = new ArrayList<Component>();
    //增加一個葉子構件或樹枝構件
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    //刪除一個葉子構件或樹枝構件
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    //獲得分支下的所有葉子構件和樹枝構件
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}

③樹葉構件

public class Leaf extends Component {
    /*
    * 可以覆寫父類方法
    * public void operation(){
    *
    * }
    */
}

④Client

public class Client {
    public static void main(String[] args) {
        //創(chuàng)建一個根節(jié)點
        Composite root = new Composite();
        root.operation();
        //創(chuàng)建一個樹枝構件
        Composite branch = new Composite();
        //創(chuàng)建一個葉子節(jié)點
        Leaf leaf = new Leaf();
        //建立整體
        root.add(branch);
        branch.add(leaf);
    }

    //通過遞歸遍歷樹
    public static void showTree(Composite root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //葉子節(jié)點
                c.operation();
            }else{ //樹枝節(jié)點
                showTree((Composite)c);
            }
        }
    }
}

(2)透明模式

①抽象構件

public abstract class Component {
    //個體和整體都具有
    public void operation(){
        //編寫業(yè)務邏輯
    }
    //增加一個葉子構件或樹枝構件
    public abstract void add(Component component);
    //刪除一個葉子構件或樹枝構件
    public abstract void remove(Component component);
    //獲得分支下的所有葉子構件和樹枝構件
    public abstract List<Component> getChildren();
}

②樹枝構件

public class Composite extends Component {
    //構件容器
    private ArrayList<Component> componentArrayList = new ArrayList<Component>();
    //增加一個葉子構件或樹枝構件
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    //刪除一個葉子構件或樹枝構件
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    //獲得分支下的所有葉子構件和樹枝構件
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}

③樹葉構件

public class Leaf extends Component {

    public void add(Component component){
        //空實現(xiàn)
    }

    public void remove(Component component){
        //空實現(xiàn)
    }

    public List<Component> getChildren(){
        //空實現(xiàn)
    }
}

④Client

public class Client {

    public static void main(String[] args) {
        //創(chuàng)建一個根節(jié)點
        Composite root = new Composite();
        root.operation();
        //創(chuàng)建一個樹枝構件
        Composite branch = new Composite();
        //創(chuàng)建一個葉子節(jié)點
        Leaf leaf = new Leaf();
        //建立整體
        root.add(branch);
        branch.add(leaf);
    }

    //通過遞歸遍歷樹
    public static void showTree(Component root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //葉子節(jié)點
                c.operation();
            }else{ //樹枝節(jié)點
                showTree(c);
            }
        }
    }
}

4.安全模式和透明模式的區(qū)別

  • 安全模式在抽象組件中只定義一些默認的行為或屬性,它是把樹枝節(jié)點和樹葉節(jié)點徹底分開;透明模式是把用來組合使用的方法放到抽象類中,不管葉子對象還是樹枝對象都有相同的結構,通過判斷確認是葉子節(jié)點還是樹枝節(jié)點,如果處理不當,這個會在運行期出現(xiàn)問題,不是很建議的方式。

  • 安全模式與依賴倒置原則沖突;透明模式的好處就是它基本遵循了依賴倒轉原則,方便系統(tǒng)進行擴展。

  • 安全模式在遍歷樹形結構的的時候需要進行強制類型轉換;在透明模式下,遍歷整個樹形結構是比較容易的,不用進行強制類型轉換。

三、通過安全模式實現(xiàn)遍歷小J的族譜

①抽象構件抽象族員類

public abstract class PersonMode {
    //人名
    private String name;
    //性別
    private String sex;
    //年齡
    private int age;

    public PersonMode(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    //個人信息
    public String getPersonInfo(){
        String info="姓名:"+name+"\t性別:"+sex+"\t年齡:"+age;
        return info;
    }
}

②樹葉構件

public class PersonLeaf extends PersonMode {
    //寫一個構造函數(shù)
    public PersonLeaf(String name, String sex, int age) {
        super(name, sex, age);
    }
}

③樹枝構件

public class PersonBranch extends PersonMode {
    private List<PersonMode> personModeList=new ArrayList<>();

    public PersonBranch(String name, String sex, int age) {
        super(name, sex, age);
    }

    public void addPerson(PersonMode person){
        this.personModeList.add(person);
    }

    public List<PersonMode> getPersonModeList(){
        return this.personModeList;
        }
}

④Client

public class Client {
    public static void main(String[] args) {
        /**
         * 組裝小J的族譜
        */
        PersonBranch personBranch=getPersonInfo();
        showTree(personBranch);
    }

    private static PersonBranch getPersonInfo(){
        //第一代J
        PersonBranch OneJ=new PersonBranch("第一代J","男",150);
        //第一代J的三個兒子
        PersonBranch JA=new PersonBranch("JA","男",70);
        PersonBranch JB=new PersonBranch("JB","男",60);
        PersonBranch JC=new PersonBranch("JC","男",50);
        //JA的三個兒子
        PersonBranch JA1=new PersonBranch("JA1","男",40);
        PersonBranch JA2=new PersonBranch("JA2","男",30);
        PersonBranch JA3=new PersonBranch("JA3","男",45);
        //JB的兩個兒子
        PersonBranch JB1=new PersonBranch("JB1","男",40);
        PersonBranch JB2=new PersonBranch("JB2","男",30);
        //JC的兒子小J
        PersonBranch xiao_J=new PersonBranch("xiao_J","男",20);
        //JA1三個兒子
        PersonBranch JA1_1=new PersonBranch("JA1_1","男",18);
        PersonBranch JA1_2=new PersonBranch("JA1_2","男",16);
        PersonBranch JA1_3=new PersonBranch("JA1_3","男",20);
        //JA3三個兒子
        PersonBranch JA3_1=new PersonBranch("JA3_1","男",16);
        PersonBranch JA3_2=new PersonBranch("JA3_2","男",20);
        PersonBranch JA3_3=new PersonBranch("JA3_3","男",18);

        //開始組裝樹狀族譜
        //組裝第一代J下的三個兒子
        OneJ.addPerson(JA);
        OneJ.addPerson(JB);
        OneJ.addPerson(JC);
        //組裝JA的三個兒子
        JA.addPerson(JA1);
        JA.addPerson(JA2);
        JA.addPerson(JA3);
        //組裝JB的兩個兒子
        JB.addPerson(JB1);
        JB.addPerson(JB2);
        //組裝JC的兒子
        JC.addPerson(xiao_J);
        //組裝JA1的三個兒子
        JA1.addPerson(JA1_1);
        JA1.addPerson(JA1_2);
        JA1.addPerson(JA1_3);
        //組裝JA3的三個兒子
        JA3.addPerson(JA3_1);
        JA3.addPerson(JA3_2);
        JA3.addPerson(JA3_3);

        return OneJ;
    }

    //通過遞歸遍歷樹
    private static void showTree(PersonBranch root){
        System.out.println(root.getPersonInfo());
        for(PersonMode c:root.getPersonModeList()){
            if(c instanceof PersonLeaf){ //葉子節(jié)點
                System.out.println(c.getPersonInfo());
            }else{ //樹枝節(jié)點
                showTree((PersonBranch) c);
            }
        }
    }
}

場景類負責樹狀結構的建立,并可以通過遞歸方式遍歷整個樹。

輸出的結果為:

姓名:第一代J 性別:男 年齡:150
姓名:JA 性別:男 年齡:70
姓名:JA1 性別:男 年齡:40
姓名:JA1_1 性別:男 年齡:18
姓名:JA1_2 性別:男 年齡:16
姓名:JA1_3 性別:男 年齡:20
姓名:JA2 性別:男 年齡:30
姓名:JA3 性別:男 年齡:45
姓名:JA3_1 性別:男 年齡:16
姓名:JA3_2 性別:男 年齡:20
姓名:JA3_3 性別:男 年齡:18
姓名:JB 性別:男 年齡:60
姓名:JB1 性別:男 年齡:40
姓名:JB2 性別:男 年齡:30
姓名:JC 性別:男 年齡:50
姓名:xiao_J 性別:男 年齡:20

四、組合模式優(yōu)缺點

1.優(yōu)點

高層模塊調用簡單。局部和整體對調用者來說沒有任何區(qū)別,也就是說,高層模塊不必關心自己處理的是單個對象還是整個組合結構,簡化了高層模塊的代碼。

節(jié)點自由增加。使用了組合模式后,我們可以看看,如果想增加一個樹枝節(jié)點、樹葉節(jié)點十分簡單,只要找到它的父節(jié)點就成,非常容易擴展,符合開閉原則,對以后的維護非常有利。

2.缺點

組合模式有一個非常明顯的缺點,在上面的場景類可以看到樹枝樹葉直接使用了實現(xiàn)類,這在面向接口編程上是很不恰當?shù)?,與依賴倒置原則沖突,它限制了你接口的影響范圍。

五、總結

只要是樹形結構或者只要是要體現(xiàn)局部和整體的關系的時候,而且這種關系還可能比較深,,就要考慮使用組合模式。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容