設(shè)計(jì)模式–組合模式

當(dāng)你腦海里出現(xiàn)一棵樹(shù)的時(shí)候

兒又生子,子又生孫峭沦,子子孫孫無(wú)窮匱也


所有的例子均來(lái)源與實(shí)際開(kāi)發(fā)項(xiàng)目

本節(jié)介紹組合模式的使用–商品結(jié)果排序評(píng)分系統(tǒng)

首先還是重復(fù)一下:設(shè)計(jì)模式是思路,而不是一味套用,如果業(yè)務(wù)場(chǎng)景和功能需求恰好吻合,那最好不過(guò)苗分;如果有偏差,一定要具體情況具體分析遍烦,更具實(shí)際場(chǎng)景選擇合適的模式類(lèi)型(注意俭嘁,是類(lèi)型,并不特定指某種模式服猪,有的時(shí)候一個(gè)場(chǎng)景多種模式都可以做)

本節(jié)所舉得例子為商品結(jié)果排序評(píng)分系統(tǒng),也就是很多項(xiàng)目中拐云,在比較重要任何事物查詢完畢后罢猪,會(huì)有一個(gè)排序過(guò)程,比如在淘寶上搜索完商品后那個(gè)商品列表的排序過(guò)程叉瘩。而且其復(fù)雜度當(dāng)然遠(yuǎn)遠(yuǎn)超過(guò)數(shù)據(jù)中SQL語(yǔ)句可以完成的程度(當(dāng)然不排除有公司把邏輯直接寫(xiě)入到存儲(chǔ)過(guò)程中膳帕,但是這種情況不做探討),所以需要一個(gè)完整的評(píng)分系統(tǒng)來(lái)對(duì)結(jié)果進(jìn)行排序薇缅,高分排前危彩,低分置后。


Ⅰ 【分析階段】———————————

a.首先分析業(yè)務(wù)功能(功能需求):類(lèi)似于上面所述的泳桦,就是對(duì)結(jié)果進(jìn)行排序汤徽,高分前,低分后灸撰。再深入到實(shí)現(xiàn)代碼層面谒府,就是多層次的權(quán)重分?jǐn)?shù)計(jì)算了,這個(gè)類(lèi)似大家學(xué)校里浮毯,最終期末總評(píng)分完疫,30%平時(shí)分,70%考試分一個(gè)道理债蓝;

b.然后分析擴(kuò)展性(非功能需求):主要變化會(huì)在哪壳鹤?當(dāng)然毫無(wú)疑問(wèn)就是:

評(píng)分的權(quán)重會(huì)發(fā)生變化:之前占80%的比例价捧,現(xiàn)在被削弱了祷愉,只占20%了与纽;評(píng)分的相互層次關(guān)系可能變化:之前這個(gè)評(píng)分是最低一級(jí)的了浇垦,現(xiàn)在在它下面又多了兩級(jí)更細(xì)化的評(píng)分;乃至評(píng)分的邏輯處理會(huì)發(fā)生變化…

好了兆沙,結(jié)合上面兩點(diǎn)欧芽,你會(huì)發(fā)現(xiàn),這有點(diǎn)一個(gè)“樹(shù)”的意思了葛圃,不是么千扔?請(qǐng)看下面這個(gè)圖


左邊一個(gè)一般的樹(shù)圖,類(lèi)似資源管理器的列表單库正,這個(gè)摘自《研磨設(shè)計(jì)模式》P392頁(yè)的組合模式章節(jié)配圖

右邊是我剛剛所說(shuō)的業(yè)務(wù)功能的分析圖曲楚,一個(gè)商品的綜合總評(píng)分分為“商品評(píng)分”與發(fā)布該商品的“店鋪評(píng)分”,分別占總評(píng)分的80%與20%褥符,然后其下面又分別有子評(píng)分標(biāo)準(zhǔn)龙誊,以此類(lèi)推,圖片應(yīng)該不難理解

這個(gè)時(shí)候喷楣,當(dāng)然需要你對(duì)組合模式還是有了了解趟大,至少要有印象,知道有這么一個(gè)模式可以作為備選铣焊,然后一比較逊朽,發(fā)現(xiàn)確實(shí)也湊巧,這個(gè)例子非常符合組合模式曲伊,而且看起來(lái)至少目前不需要做太大改動(dòng)叽讳,已經(jīng)可以滿足需求了。那就開(kāi)始設(shè)計(jì)吧坟募!


Ⅱ【設(shè)計(jì)階段】——————————————————-

類(lèi)圖設(shè)計(jì)如下岛蚤,以下圖片中上半部分摘自《研磨設(shè)計(jì)模式》P396 圖15.1 ,下面為我基于他的圖進(jìn)行的部分修改懈糯,即為我的組合模式的設(shè)計(jì)


是不是發(fā)現(xiàn)還是變化了不少涤妒,這就是我之前在第一篇文章設(shè)計(jì)模式–概述,看清本質(zhì)中所提到的觀點(diǎn)昂利,不用在乎一些改變届腐,而要關(guān)注設(shè)計(jì)模式的核心目標(biāo)是否達(dá)到

好了,繼續(xù)對(duì)上面的設(shè)計(jì)做一些說(shuō)明:

身份對(duì)應(yīng):

ScoreCalculator —-對(duì)應(yīng)—> Component蜂奸,只是在原來(lái)書(shū)中例子犁苏,component為接口,我這里進(jìn)一步改為了一個(gè)抽象類(lèi)扩所,因?yàn)槠渲羞€嵌入了一個(gè)模板模式的做法围详,我在代碼中會(huì)有說(shuō)明;

XXScoreCalculator — 對(duì)應(yīng) —> Composite , 其實(shí)就是具體的實(shí)現(xiàn)類(lèi)了

Leaf —- > ?? 在這里助赞,我刪去了Leaf這個(gè)角色买羞,為什么?因?yàn)樵跁?shū)中例子里雹食,Leaf作為葉子節(jié)點(diǎn)畜普,即樹(shù)結(jié)構(gòu)的最末端節(jié)點(diǎn)是不會(huì)有“子節(jié)點(diǎn)”存在的,所以設(shè)置了這一角色群叶,但是我這里由于各種計(jì)算分?jǐn)?shù)的規(guī)則在將來(lái)隨時(shí)可能由于業(yè)務(wù)的變更會(huì)要產(chǎn)生子節(jié)點(diǎn)吃挑,這樣就不可能把任何一個(gè)分?jǐn)?shù)計(jì)算規(guī)則指定為“無(wú)子節(jié)點(diǎn)”的類(lèi)型,所以我索性去掉了Leaf角色

具體實(shí)現(xiàn)街立,我結(jié)合代碼示例進(jìn)行說(shuō)明:


/**
* 評(píng)分計(jì)算基類(lèi)
* 這里我略掉了一些什么addChild,removeChild等方法的說(shuō)明舶衬,這個(gè)大家需要自行加入
*/
public abstract class ScoreCalculator {

    // list用于存放子類(lèi)
    // 至于我這里為什么還封裝到了一個(gè)Entry里,只是完全出于業(yè)務(wù)考慮赎离,因?yàn)榧纫乓?guī)則計(jì)算類(lèi)逛犹,又要放入配置文件導(dǎo)入的權(quán)重參數(shù)
    // 像這個(gè)就需要大家根據(jù)自己業(yè)務(wù)靈活處理
    protected List scoreEntryList;

    public int calculateScore(){

        int totalScore = 0;

        //先進(jìn)行子節(jié)點(diǎn)的分?jǐn)?shù)計(jì)算,對(duì)葉子節(jié)點(diǎn)來(lái)說(shuō)梁剔,由于沒(méi)有子節(jié)點(diǎn)虽画,這個(gè)代碼段就自然不會(huì)執(zhí)行了
        if(this.scoreEntryList!=null){
            int[] scoreArray = new int[scoreEntryList.size()];
            ScoreEntry entry = null;
            for (int i =0; i < scoreEntryList.size(); i++) {
                entry = scoreEntryList.get(i);
                //這里調(diào)用子節(jié)點(diǎn)的calculateScore(),遞歸!憾朴,也就是組合模式的核心思想之一
                scoreArray[i] = (int)(Math.round(entry.cal.calculateScore() * entry.weight));
                totalScore += scoreArray[i];
            }
        }

        //注意狸捕,這里就用了模板方法的思想,上面所做的一切都是不變的众雷,就是計(jì)算子節(jié)點(diǎn)的分?jǐn)?shù)然后匯總
        //那統(tǒng)計(jì)完子節(jié)點(diǎn)的了,自己還要進(jìn)一步做處理吧做祝?畢竟有些簡(jiǎn)單的加減乘除就解決了砾省,有些可還有復(fù)雜的邏輯
        totalScore = moreCalculate(totalScore);
        return totalScore;
    }

    //抽象方法,讓子類(lèi)自己去實(shí)現(xiàn)
    protected abstract int moreCalculate(int currentTotalScore);

}

public class ScoreEntry {
    //規(guī)則計(jì)算類(lèi)
    public ScoreCalculator cal;
    //本規(guī)則在上一級(jí)規(guī)則中所占的比重(0~1)
    public float weight;
    //本規(guī)則在計(jì)算后的結(jié)果,整型(0~10000)
    public int score;
}

對(duì)于具體的實(shí)現(xiàn)類(lèi)來(lái)說(shuō)混槐,就比較清晰了编兄,實(shí)現(xiàn)該實(shí)現(xiàn)的方法就好了


/*
* 店鋪分?jǐn)?shù)計(jì)算規(guī)則,其還帶有子類(lèi)規(guī)則A1,A2
* 至于商品分?jǐn)?shù)計(jì)算規(guī)則的思路就差不多了声登,這里就只舉此一個(gè)
*/

public class ShopCalculator extends ScoreCalculator {

        //..構(gòu)造方法等等略
        @Override
        protected int moreCalculate(int currentTotalScore) {

            //進(jìn)行具體的操作狠鸳,比如這些分?jǐn)?shù)還要進(jìn)一步結(jié)合店鋪的某種其他信息做檢查處理
            //然后做相應(yīng)的增減,最后返回?cái)?shù)據(jù)至上一層悯嗓,向上一層統(tǒng)計(jì)分?jǐn)?shù)節(jié)點(diǎn)“呈遞”自己的最終結(jié)果
            currentTotalScore = doSth();
            return currentTotalScore;
        }
}

不知道大家是否能夠順利理解件舵,畢竟這個(gè)例子是實(shí)際項(xiàng)目,邏輯相對(duì)復(fù)雜脯厨,篇幅問(wèn)題我也只能選擇核心代碼給出铅祸,所以難免會(huì)看著有點(diǎn)困難,沒(méi)關(guān)系,重點(diǎn)看我有注釋的那幾行便可临梗,若有問(wèn)題可以回帖發(fā)問(wèn)~

好了涡扼,繼續(xù)還有一點(diǎn)收尾,那既然這棵樹(shù)“種好了”盟庞,那怎么來(lái)使用呢吃沪?設(shè)計(jì)模式不僅僅要便于擴(kuò)展,也要便于使用什猖,那我們就來(lái)看看如何搭建起這個(gè)樹(shù)結(jié)構(gòu)票彪,也就是上面畫(huà)圖中,那個(gè)Client怎么調(diào)用了

//提醒卸伞,大家在看上面第二幅圖時(shí)抹镊,《研磨設(shè)計(jì)模式》書(shū)中圖里有addChild方法,怎么樣荤傲?想到了怎么建立樹(shù)了嗎垮耳?
//對(duì)吧,其實(shí)很簡(jiǎn)單遂黍,new出節(jié)點(diǎn)终佛,然后通過(guò)addChild建立起雙方的父子關(guān)系便可了

//總分統(tǒng)計(jì)
ScoreCalculator totalCal = new TotalCalculator();
//商品統(tǒng)計(jì),數(shù)字是在上級(jí)中的所占權(quán)重
ScoreCalculator goodsCal = new GoodsCalculator(0.8);
//店鋪統(tǒng)計(jì)
ScoreCalculator shopCal = new ShopCalculator(0.2);
//A1統(tǒng)計(jì)
ScoreCalculator a1Cal = new A1Calculator(0.3);
//A2統(tǒng)計(jì)
ScoreCalculator a2Cal = new A2Calculator(0.7);
...(略)

//好了雾家,把相互的關(guān)系整理起來(lái)铃彰!
goodsCal.addChild(a1Cal);
goodsCal.addChild(a2Cal);
...

totalCal.addChild(goodsCal);
totalCal.addChild(shopCal);

//最后,執(zhí)行計(jì)算就OK了芯咧!無(wú)窮的遞歸就在這一刻開(kāi)始
int finalScore = totalCal.calculateScore();

Ⅲ【核查階段】———————————————————–

好了牙捉,到這里為止,這個(gè)樣例算是分析完了敬飒,只是還查了一點(diǎn)邪铲,什么呢?檢查无拗!

就是檢查這個(gè)最終的開(kāi)發(fā)結(jié)果是不是達(dá)到的之前的設(shè)計(jì)目的:

a.業(yè)務(wù)功能是否滿足带到? 當(dāng)然滿足了,是按照權(quán)重計(jì)算英染,分層遞歸網(wǎng)上走揽惹,層層按權(quán)重統(tǒng)計(jì)匯總,最終出總結(jié)果

b.擴(kuò)展性是否良好四康? 沒(méi)問(wèn)題搪搏,當(dāng)有新的規(guī)則出現(xiàn)時(shí),創(chuàng)建一個(gè)新的繼承自ScoreCalculator的類(lèi)箭养,然后實(shí)現(xiàn)moreCalculator方法進(jìn)行自身邏輯處理即可慕嚷;

c.能否處理業(yè)務(wù)變化? 首先,每個(gè)規(guī)則之間業(yè)務(wù)處理完全分開(kāi)喝检,權(quán)重修改加個(gè)個(gè)配置文件即可搞定嗅辣,變更修改不會(huì)對(duì)其他業(yè)務(wù)構(gòu)成影響;然后當(dāng)有節(jié)點(diǎn)層次關(guān)系發(fā)生變化時(shí)挠说,在Client調(diào)用中進(jìn)行變更c(diǎn)hild層次結(jié)構(gòu)即可

可見(jiàn)澡谭,目的都算是達(dá)到了,這個(gè)模式的使用就算是合格了损俭!

在這里蛙奖,大家發(fā)現(xiàn)我其中用上了模板模式,其實(shí)在這個(gè)以組合模式為核心的模塊里里外外還包圍著很多其他模式來(lái)進(jìn)行輔助杆兵,這里我為了避免影響都沒(méi)有寫(xiě)出來(lái)了雁仲,有點(diǎn)像變形金剛合體一樣。這依舊說(shuō)明了一點(diǎn):

模式為思想琐脏,方法不固定攒砖,靈活變通使用才是硬道理!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末日裙,一起剝皮案震驚了整個(gè)濱河市吹艇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昂拂,老刑警劉巖受神,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異格侯,居然都是意外死亡鼻听,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)联四,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)精算,“玉大人,你說(shuō)我怎么就攤上這事碎连。” “怎么了驮履?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵鱼辙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我玫镐,道長(zhǎng)倒戏,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任恐似,我火速辦了婚禮杜跷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己葛闷,他們只是感情好憋槐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著淑趾,像睡著了一般阳仔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扣泊,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天近范,我揣著相機(jī)與錄音,去河邊找鬼延蟹。 笑死评矩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阱飘。 我是一名探鬼主播斥杜,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俯萌!你這毒婦竟也來(lái)了果录?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤咐熙,失蹤者是張志新(化名)和其女友劉穎弱恒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棋恼,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡返弹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爪飘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片义起。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖师崎,靈堂內(nèi)的尸體忽然破棺而出默终,到底是詐尸還是另有隱情,我是刑警寧澤犁罩,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布齐蔽,位于F島的核電站,受9級(jí)特大地震影響床估,放射性物質(zhì)發(fā)生泄漏含滴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一丐巫、第九天 我趴在偏房一處隱蔽的房頂上張望谈况。 院中可真熱鬧勺美,春花似錦、人聲如沸碑韵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泼诱。三九已至坛掠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間治筒,已是汗流浹背屉栓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耸袜,地道東北人友多。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像堤框,于是被迫代替她去往敵國(guó)和親域滥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是組合模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 優(yōu)點(diǎn)和缺點(diǎn) 適用環(huán)境 模式應(yīng)用 一蜈抓、...
    w1992wishes閱讀 881評(píng)論 0 2
  • 繼承是is-a的關(guān)系启绰。組合和聚合有點(diǎn)像,有些書(shū)上沒(méi)有作區(qū)分沟使,都稱(chēng)之為has-a委可,有些書(shū)上對(duì)其進(jìn)行了較為嚴(yán)格區(qū)分,組...
    時(shí)待吾閱讀 450評(píng)論 0 1
  • 介紹 這篇主要講述設(shè)計(jì)模式中的組合模式腊嗡。組合模式又叫部分整體模式着倾,是用于把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象。組合模...
    東西的南北閱讀 332評(píng)論 0 1
  • 定義 屬于對(duì)象的結(jié)構(gòu)模式燕少,有時(shí)又叫做“部分——整體”模式卡者。組合模式將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體和部分的關(guān)...
    步積閱讀 3,316評(píng)論 2 7
  • 組合模式將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示"部分-整體"的層次結(jié)構(gòu)客们。組合模式是的用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性崇决。...
    Mitchell閱讀 373評(píng)論 0 0