<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我的漫漫程序之旅

    專注于JavaWeb開發
    隨筆 - 39, 文章 - 310, 評論 - 411, 引用 - 0
    數據加載中……

    裝飾者模式(Decorator)淺談與剖析

    Central Perk的名字因為《老友記》而享譽全球,他們的分店幾乎開遍世界各地。他們發展的實在是太快了,所以他們此時正在急于實現一套由計算機管理的自動化記賬系統。在第一次研究了他們的需求以后,開發者設計了如下圖的類結構:

           Beverage是所有飲料的基類;cost()是抽象方法,所有子類都需要定義它們自己的cost()實現來返回特定飲料的價錢;description變量也是在子類里賦值的,表示特定飲料的描述信息,getDescription()方法可以返回這個描述;  

           除了咖啡以為,Central Perk還提供豐富的調味品,比如:煉乳、巧克力、砂糖、牛奶等,而且這些調味品也是要單獨按份收費的,所以調味品也是訂單系統中重要的一部分。

           于是,考慮到調味品的管理,開發者又有了下面這樣的類結構:

           看了上面的類圖,你一定有話要說!是的!這簡直是太恐怖了,好像是整個類圖都要爆炸了一樣,而且以后隨便增加一種調味品,繼承于Beverage的子類還會翻倍!(因為理論上可能的咖啡種類數 = 咖啡類別數×調味品類別數) 我的神啊!全球變暖后連咖啡都“沸騰”了嗎?還是我們在研制某種自殺式炸彈裝置!(方便攜帶,不宜察覺,居家旅游必備,只要將幾種調味品混合到一起,就能產生驚人的爆炸力!不錯啊!J)  

           上面的情況絕對是不能容忍的,于是開發者們經過討論,又提出了下面的設計方案件:

           如圖所示,這是改進后的Beverage基類。首先在基類里增加了表示是否包含特定調味品的布爾變量,如milk, soy等,然后提供了一些has(get)set方法來設置這些布爾值;其次在Beverage類里實現cost()方法來計算調味品的價錢。所有咖啡子類將仍然覆蓋cost()方法,只是這次它們需要同時調用基類的cost()方法,以便獲得咖啡加上調味品后的總價。  

           看上去似乎這是一個不錯的設計,那么下面我們再來給Beverage增加子類,如下圖所示:

          

     

           基類的cost()方法將計算所有調味品的價錢(當然是只包括布爾值為true的調味品),子類里的cost()方法將擴展其功能,以包含特定類型飲料的價錢。

           OK! 現在我們似乎已經有了一個看上去還不錯的設計,那么Central Perk的這個記賬系統就按這個設計來實現就萬事大吉了嗎?等一下,還是讓我們先從以前學習過的“找到系統中變化的部分,將變化的部分同其它穩定的部分隔開。”這個設計原則出發,重新推敲一下這個設計。

    那么對于一家咖啡店來說,都有那些變化點呢?調味品的品種和價格會變嗎?咖啡的品種和價格會變嗎?咖啡和調味品的組合方式會變嗎?YES!對于一家咖啡店來說,這些方面肯定會經常發生改變的!那么,當這些改變發生的時候,我們的記賬系統要如何應對呢? 如果調味品發生改變,那么我們只能從代碼的層次重新調整Beverage基類,這太糟糕了;如果咖啡發生改變,我們可以增加或刪除一個子類即可,這個似乎還可以忍受;那么咖啡和調味品的組合方式發生改變呢?如果顧客點了一杯純黑咖啡外加兩份砂糖和一份巧克力,或者顧客點了一杯脫咖啡因咖啡(Decaf)外加三份煉乳和一份砂糖呢?我倒!突然意識到,上面的設計根本不支持組合一份以上同種調味品的情況,因為基類里的布爾值只能記錄是否包含某種調味品,而并不能表示包含幾份,連基本的功能需求都沒有滿足,看來這些開發者可以卷鋪蓋滾蛋了!(似乎他們改行去做炸彈更合適!)

           好吧!讓我們來接手這個設計!我們已經分析了前面設計的失敗之處,我們應該實現支持調味品的品種和價格任意改變而不需要修改已有代碼的設計;我們還要實現支持咖啡品種和價格任意改變而不需要修改已有代碼的設計(這點上面的設計通過繼承算是實現了);還有就是支持咖啡和調味品的品種和份數任意組合而不需要修改已有代碼的設計;還有就是代碼重用越多越好了,內聚越高越好了,耦合越低越好了;(還有最重要的,報酬越高越好啦!)

           看來我們要實現的目標還真不少,那么我們到底該怎么做呢?說實話,我現在也不知道!我們需要先去拜訪一下今天的主角—裝飾者模式,看看她能給我們帶來什么驚喜吧!

    這就是裝飾者模式

    我們還是先看一下官方的定義:

    The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. (裝飾者模式可以動態地給一個對象增加其他職責。就擴展對象功能來說,裝飾者模式比生成子類更為靈活。)

    這里我們要重點注意那個dynamically(動態的),什么是動態?靜態又是什么?這是我們要重點區分的地方,后面我們還會專門討論這個問題。下面先看看裝飾者模式的類圖和順序圖:

    Component(被裝飾對象基類)  

    l         定義對象的接口,可以給這些對象動態增加職責;

    ConcreteComponent(具體被裝飾對象)

    l         定義具體的對象,Decorator可以給它增加額外的職責;

    Decorator(裝飾者抽象類)

    l         維護一個指向Component實例的引用,并且定義了與Component一致的接口;

    ConcreteDecorator(具體裝飾者)

    l         具體的裝飾對象,給內部持有的具體被裝飾對象增加具體的職責;

           怎么樣?大家都看懂了吧!看懂了我就不解釋了!— 呵呵,開玩笑的!

    我們先來說說上面提到的動態和靜態的問題,所謂動態是說可以在系統運行時(RunTime)動態給對象增加其它職責而不需要修改代碼或重新編譯;所謂靜態是說必須通過調整代碼(DesignTime)才能給對象增加職責,而且系統還需要重新編譯;從具體技術層面來說,對象的組合和繼承正好對應于前面的動態和靜態,因為通過對象組合建立的交互關系不是在代碼中(DesignTime)固定死的,而是在運行時(RunTime)動態組合的;而通過繼承建立的關系是僵硬的難以改變的,因為它是在代碼中(DesignTime)固定死了的,根本不存在運行時(RunTime)改變的可能。換個角度說:我們應該多使用對象組合來保持系統的運行時擴展性,盡量少使用繼續,因為繼承讓程序變得僵硬!這句話聽著是不是很熟悉啊?恩!這就是我們前面文章里提過多次的一個設計原則:Favor composition over inheritance.(優先使用對象組合,而非類繼承),更多的就不需要再解釋了吧?

           那么回到裝飾者模式,跟前面介紹過的模式一樣,裝飾者同樣是一個很簡單的模式,特別是畫出類圖和順序圖之后,一切都很清楚明了。這里只有一個地方需要特殊強調一下:Decorator是裝飾者模式里非常特殊的一個類,它既繼承于ComponentIS A關系】,又維護一個指向Component實例的引用【HAS A關系】,換個角度來說,DecoratorComponent之間,既有動態組合關系又有靜態繼承關系,WHY? 這里為什么要這么來設計?上面我們說過,組合的好處是可以在運行時給對象增加職責,DecoratorHAS AComponent的目的是讓ConcreteDecorator可以在運行時動態給ConcreteComponent增加職責,這一點相對來說還比較好理解;那么Decorator繼承于Component的目的是什么?在這里,繼承的目的只有一個,那就是可以統一裝飾者和被裝飾者的接口,換個角度來說,不管是ConcretComponent還是ConcreteDecorator,它們都是 Component,用戶代碼可以把它們統一看作Component來處理,這樣帶來的更深一層的好處就是,裝飾者對象對被裝飾者對象的功能職責擴展對用戶代碼來說是完全透明的,因為用戶代碼引用的都是Component,所以就不會因為被裝飾者對象在被裝飾后,引用它的用戶代碼發生錯誤,實際上不會有任何影響,因為裝飾前后,用戶代碼引用的都是Component類型的對象,這真是太完美了!裝飾者模式通過繼承實現統一了裝飾者和被裝飾者的接口,通過組合獲得了在運行時動態擴展被裝飾者對象的能力。

    我們再舉個生活中的例子,俗話說“人在衣著馬在鞍”,把這就話用裝飾者模式的語境翻譯一下,“人通過漂亮的衣服裝飾后,男人變帥了,女人變漂亮了;”。對應上面的類圖,這里人對應于ConcreteComponent,而漂亮衣服則對應于ConcreteDecorator;換個角度來說,人和漂亮衣服組合在一起【HAS A】,有了帥哥或美女,但是他們還是人【IS A】,還要做人該做的事情,但是可能會對異性更有吸引力了(擴展功能)    

           現在我們已經認識了裝飾者模式,知道了動態關系和靜態關系是怎么回事,是時候該解決咖啡店的問題了,從裝飾者模式的角度來考慮問題,咖啡和調味品的關系應該是:咖啡是被裝飾對象而調味品是裝飾者,咖啡和調味品可以任意組合,但是不管怎么組合,咖啡還是咖啡!原來這么簡單啊!具體看下面的類圖:

           如圖所示,Beverage還是所有飲料的基類,它對應于裝飾者模式類圖里的Component,是所有被裝飾對象的基類;HouseBlend, DarkRoast, Espresso, Decaf是具體的飲料(咖啡)種類,對應于前面的ConcreteComponent,即是具體的被裝飾對象;CondimentDecorator對應于前面的Decorator,是裝飾者的抽象類;而MilkMochaSoyWhip則都是具體的調味品,對于前面的ConcreteDecorator,也就是具體的裝飾者。下面我們通過具體的代碼再進一步理解一下基于裝飾者模式的記賬系統的實現。 

    posted on 2008-05-25 10:06 々上善若水々 閱讀(738) 評論(1)  編輯  收藏 所屬分類: 設計模式

    評論

    # re: 裝飾者模式(Decorator)淺談與剖析  回復  更多評論   

    所謂動態是說可以在系統運行時(RunTime)動態給對象增加其它職責而不需要修改代碼或重新編譯

    怎么動態擴展功能,通過IOC?請教
    2012-07-23 21:49 | shangtang
    主站蜘蛛池模板: 亚洲AV无码一区东京热| 亚洲成色www久久网站夜月| 亚洲国产精品日韩| 亚洲日产韩国一二三四区| 少妇中文字幕乱码亚洲影视| 亚洲中文字幕久在线| 无码天堂亚洲国产AV| aaa毛片免费观看| 24小时免费看片| 国产精品视_精品国产免费| 亚洲欧洲日本在线| 亚洲va在线va天堂va不卡下载| 国产精品高清视亚洲精品| 国产成人综合久久精品亚洲| a级毛片高清免费视频就| 欧洲一级毛片免费| 亚洲av无码国产精品色在线看不卡| 亚洲成色www久久网站夜月| 亚洲一区二区三区乱码在线欧洲| 添bbb免费观看高清视频| 东方aⅴ免费观看久久av | 久别的草原电视剧免费观看| 在线观看免费高清视频| 亚洲欧洲久久av| 亚洲乱码一二三四区乱码| 伊人久久国产免费观看视频| 99久久免费国产香蕉麻豆| 国产精品亚洲综合一区| 亚洲人成电影青青在线播放| 一级特黄a免费大片| 嫖丰满老熟妇AAAA片免费看| 国产偷窥女洗浴在线观看亚洲| 亚洲午夜成激人情在线影院| 一级中文字幕乱码免费| 男人的好看免费观看在线视频| 亚洲色成人中文字幕网站| 亚洲成a人片在线观看天堂无码| 久久青草精品38国产免费| 日批日出水久久亚洲精品tv| 亚洲欧洲国产综合| 中文字幕免费在线观看动作大片|