設(shè)計模式系列之「組合模式」

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

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

一、小J族譜簡略版

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

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

1.組合模式的定義

組合模式也叫合成模式,有時又叫做部分-整體模式结蟋,主要是用來描述部分與整體的關(guān)系,將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)渔彰,使得用戶對單個對象和組合對象的使用具有一致性嵌屎。

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

  • Component抽象構(gòu)件角色
    定義參加組合對象的共有方法和屬性,可以定義一些默認的行為或?qū)傩浴?/p>

  • Leaf葉子構(gòu)件
    Leaf葉子構(gòu)件葉子對象恍涂,其下再也沒有其他的分支宝惰,也就是遍歷的最小單位。

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

3.組合模式的使用場景

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

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

  • 維護和展示部分-整體關(guān)系的場景拐邪。

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

(1)安全模式

①抽象構(gòu)件

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

②樹枝構(gòu)件

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

③樹葉構(gòu)件

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)建一個樹枝構(gòu)件
        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)透明模式

①抽象構(gòu)件

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

②樹枝構(gòu)件

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

③樹葉構(gòu)件

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)建一個樹枝構(gòu)件
        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ū)別

  • 安全模式在抽象組件中只定義一些默認的行為或?qū)傩晕恳悖前褬渲?jié)點和樹葉節(jié)點徹底分開;透明模式是把用來組合使用的方法放到抽象類中扎阶,不管葉子對象還是樹枝對象都有相同的結(jié)構(gòu)汹胃,通過判斷確認是葉子節(jié)點還是樹枝節(jié)點,如果處理不當(dāng)东臀,這個會在運行期出現(xiàn)問題着饥,不是很建議的方式。

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

  • 安全模式在遍歷樹形結(jié)構(gòu)的的時候需要進行強制類型轉(zhuǎn)換谤逼;在透明模式下贵扰,遍歷整個樹形結(jié)構(gòu)是比較容易的,不用進行強制類型轉(zhuǎn)換流部。

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

①抽象構(gòu)件抽象族員類

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;
    }
}

②樹葉構(gòu)件

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

③樹枝構(gòu)件

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);
            }
        }
    }
}

場景類負責(zé)樹狀結(jié)構(gòu)的建立,并可以通過遞歸方式遍歷整個樹枝冀。

輸出的結(jié)果為:

姓名:第一代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)點

高層模塊調(diào)用簡單。局部和整體對調(diào)用者來說沒有任何區(qū)別果漾,也就是說球切,高層模塊不必關(guān)心自己處理的是單個對象還是整個組合結(jié)構(gòu),簡化了高層模塊的代碼绒障。

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

2.缺點

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

五、總結(jié)

只要是樹形結(jié)構(gòu)或者只要是要體現(xiàn)局部和整體的關(guān)系的時候嚷炉,而且這種關(guān)系還可能比較深渊啰,,就要考慮使用組合模式申屹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绘证,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哗讥,更是在濱河造成了極大的恐慌嚷那,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杆煞,死亡現(xiàn)場離奇詭異魏宽,居然都是意外死亡,警方通過查閱死者的電腦和手機决乎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門队询,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人构诚,你說我怎么就攤上這事蚌斩。” “怎么了范嘱?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵送膳,是天一觀的道長。 經(jīng)常有香客問我丑蛤,道長叠聋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任受裹,我火速辦了婚禮碌补,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棉饶。我一直安慰自己脑慧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布砰盐。 她就那樣靜靜地躺著,像睡著了一般坑律。 火紅的嫁衣襯著肌膚如雪岩梳。 梳的紋絲不亂的頭發(fā)上囊骤,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音冀值,去河邊找鬼也物。 笑死,一個胖子當(dāng)著我的面吹牛列疗,可吹牛的內(nèi)容都是我干的滑蚯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼抵栈,長吁一口氣:“原來是場噩夢啊……” “哼告材!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起古劲,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤斥赋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后产艾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疤剑,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年闷堡,在試婚紗的時候發(fā)現(xiàn)自己被綠了隘膘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡杠览,死狀恐怖弯菊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倦零,我是刑警寧澤误续,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站扫茅,受9級特大地震影響蹋嵌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜葫隙,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一栽烂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恋脚,春花似錦腺办、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至船响,卻和暖如春躬拢,著一層夾襖步出監(jiān)牢的瞬間躲履,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工聊闯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留工猜,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓菱蔬,卻偏偏與公主長得像篷帅,于是被迫代替她去往敵國和親洒忧。 傳聞我的和親對象是個殘疾皇子左胞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容