摘要:這種模式我們稱之為裝飾器模式。因?yàn)檠b飾器模式是在給對象增加責(zé)任。以下情況適合使用裝飾器模式在不影響其他對象的情況下,以動態(tài)透明的方式給單個對象添加職責(zé)。
前言
本篇的裝飾器模式不是講解的python中的語法糖 @ 這個裝飾器。而是講解設(shè)計(jì)模式中的裝飾器模式。網(wǎng)上很多的實(shí)現(xiàn)都是基于java和c++的。本文則使用python來實(shí)現(xiàn),其中有些實(shí)現(xiàn)可能在python并不需要那樣來寫的,但是思路都是一樣的。關(guān)于python @ 裝飾器的使用我之后會再寫一篇文章來介紹。
產(chǎn)生裝飾器模式的動機(jī)是什么?大家知道我們有的時候總是需要給給一個類或者一個對象增加一些行為。一般情況下使用繼承和關(guān)聯(lián)兩種方式來實(shí)現(xiàn)。其中使用關(guān)聯(lián)這種方式來實(shí)現(xiàn)并符合一定的設(shè)計(jì)規(guī)范的我們稱之為裝飾器模式。接下來我們首先會先介紹一下這兩種方法,然后用python代碼來分別實(shí)現(xiàn)這兩種方法并比較他們之間的差異。
繼承方式一般情況下我們都是通過繼承來給一個或一群類來添加方法,通過繼承使子類獲得了父類的行為。雖然繼承是一種適用廣泛的方法,但是繼承是一種靜態(tài)行為,在代碼編寫階段就已經(jīng)固定,無法動態(tài)的控制一個類增加行為的種類和時間。
關(guān)聯(lián)關(guān)系我們通過將一個A對象嵌入另一個B對象里面,及將一個B對象里面的屬性的值設(shè)置為A對象。通過在調(diào)用A對象的動作前后添加行為來給A對象增加功能。這種模式我們稱之為裝飾器模式。
“裝飾模式以對客戶透明的方式動態(tài)地給一個對象附加上更多的責(zé)任,換言之,客戶端并不會覺得對象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要創(chuàng)造更多子類的情況下,將對象的功能加以擴(kuò)展。這就是裝飾模式的模式動機(jī)?!盵1]
因?yàn)檠b飾器模式是在給對象增加責(zé)任。所以裝飾器模式為對象結(jié)構(gòu)型設(shè)計(jì)模式(對象是因?yàn)槠涫墙o對象而不是類增加責(zé)任,結(jié)構(gòu)型模式就是描述如何將類或者對象結(jié)合在一起形成更大的結(jié)構(gòu),就像搭積木,可以通過簡單積木的組合形成復(fù)雜的、功能更為強(qiáng)大的結(jié)構(gòu))。
實(shí)例講解 代碼描述一個飲料店里面賣茶和咖啡。 并且有冰塊,糖和牛奶三種輔料可以添加。 我們可以計(jì)算出一共有14中組合產(chǎn)品。并且每增加一種飲料就要增加7種組合產(chǎn)品。
繼承方式 UML類圖 代碼class DrinkComponent(object): def get_price(self): pass def get_name(self): pass class TeaConcreteComponent(DrinkComponent): def __init__(self): self.__name = "Tea" self.__price = 2 def get_price(self): return self.__price def get_name(self): return self.__name class CoffeeConcreteComponent(DrinkComponent): def __init__(self): self.__name = "coffee" self.__price = 3 def get_price(self): return self.__price def get_name(self): return self.__name class IngredientsComponent(object): pass class IceConcreteComponent(IngredientsComponent): def add_ice_price(self): return 0.3 def add_ice_name(self): return "+Ice" class SugerConcreteComponent(IngredientsComponent): def add_suger_price(self): return 0.5 def add_suger_name(self): return "+Suger" class MilkConcreteComponent(IngredientsComponent): def add_milk_price(self): return 1 def add_milk_name(self): return "+Milk" class Tea_Milk(TeaConcreteComponent, MilkConcreteComponent): def get_price(self): return TeaConcreteComponent.get_price(self) + MilkConcreteComponent.add_milk_price(self) def get_name(self): return TeaConcreteComponent.get_name(self) + MilkConcreteComponent.add_milk_name(self) class Tea_Milk_Ice(TeaConcreteComponent, MilkConcreteComponent, IceConcreteComponent): def get_price(self): return TeaConcreteComponent.get_price(self) + MilkConcreteComponent.add_milk_price(self) + IceConcreteComponent.add_ice_price(self) def get_name(self): return TeaConcreteComponent.get_name(self) + MilkConcreteComponent.add_milk_name(self) + IceConcreteComponent.add_ice_name(self) if __name__ == "__main__": tea_milk = Tea_Milk() print tea_milk.get_name() print tea_milk.get_price() tea_milk_ice = Tea_Milk_Ice() print tea_milk_ice.get_name() print tea_milk_ice.get_price()說明
從圖和代碼中看首先我們定義了DrinkComponent和IngredientsComponent即飲料和配料兩個抽象類,并分別實(shí)現(xiàn)了具體的構(gòu)建類。當(dāng)我們要產(chǎn)生一個產(chǎn)品的時候。通過繼承不同的具體構(gòu)建類來實(shí)現(xiàn)。比如加冰加牛奶的茶。我們通過繼承茶、牛奶和冰塊三個類來實(shí)現(xiàn)??梢钥闯鋈绻獙?shí)現(xiàn)所有的類那么我們需要14個子類來完成。支持多繼承的語言才能這樣實(shí)現(xiàn)如果是單繼承的語言則需要通過多級繼承來完成。不僅冗余度增加而且復(fù)雜的多級繼承關(guān)系是后期維護(hù)的淚。
關(guān)聯(lián)方式 UML類圖 代碼class DrinkComponent(object): def get_price(self): pass def get_name(self): pass class TeaConcreteComponent(DrinkComponent): def __init__(self): self.__name = "Tea" self.__price = 2 def get_price(self): return self.__price def get_name(self): return self.__name class CoffeeConcreteComponent(DrinkComponent): def __init__(self): self.__name = "coffee" self.__price = 3 def get_price(self): return self.__price def get_name(self): return self.__name class IngredientsDecorator(DrinkComponent): def __init__(self, drink_component): self.drink_component = drink_component def get_price(self): pass def get_name(self): pass class IceConcreteDecorator(IngredientsDecorator): def get_price(self): return self.drink_component.get_price() + self.add_ice_price() def add_ice_price(self): return 0.3 def get_name(self): return self.drink_component.get_name() + self.add_ice_name() def add_ice_name(self): return "+Ice" class SugerConcreteDecorator(IngredientsDecorator): def get_price(self): return self.drink_component.get_price() + self.add_suger_price() def add_suger_price(self): return 0.5 def get_name(self): return self.drink_component.get_name() + self.add_suger_name() def add_suger_name(self): return "+Suger" class MilkConcreteDecorator(IngredientsDecorator): def get_price(self): return self.drink_component.get_price() + self.add_milk_price() def add_milk_price(self): return 1 def get_name(self): return self.drink_component.get_name() + self.add_milk_name() def add_milk_name(self): return "+Milk" if __name__ == "__main__": tea_milk = MilkConcreteDecorator(TeaConcreteComponent()) print tea_milk.get_name() print tea_milk.get_price() tea_milk_ice = IceConcreteDecorator(MilkConcreteDecorator(TeaConcreteComponent())) print tea_milk_ice.get_name() print tea_milk_ice.get_price()說明
DrinkComponent 是抽象構(gòu)件類,它是具體構(gòu)建類(*ConcreteComponent)和抽象裝飾器類(IngredientsDecorator)的父類,主要定義了具體構(gòu)建類的業(yè)務(wù)方法。以及讓我們在調(diào)用的時候可以統(tǒng)一的處理裝飾前和裝飾后的對象。方便我們使用裝飾器類裝飾一個已經(jīng)被裝飾的具體構(gòu)建如加糖(加冰(咖啡))。在關(guān)聯(lián)關(guān)系中我們主要說一下這個部分。
這是一個關(guān)聯(lián)聚合關(guān)系。表示IngredientsDecorator是知道DrinkComponent類的存在的呢。這個大家可以這樣理解。你在實(shí)現(xiàn)Concretecomponet的時候是不需要考慮IngredinetsDecorator的存在,因?yàn)槟悴粫{(diào)用它的,也不繼承它,也不知道你會被它調(diào)用。但是在設(shè)計(jì)實(shí)現(xiàn)ConcreteDecorator的時候你會在其屬性中保持一個對DrinkComponet類型的類的引用。并且你會調(diào)用她的方法。這樣你就要知道DrinkCompoent這個類里面都有什么方法及要知道DrinkComponent類的存在。在另一種設(shè)計(jì)模式橋接模式這種關(guān)系正好是相反的。我之后再來寫一篇關(guān)于橋接模式的介紹。
我們在代碼中可以看到在IngredientsDecorator中也有g(shù)et_price 和 get_name兩種方法。這是為了保證在ConcreteComponent在被裝飾器后還是可以像沒有被裝飾那樣被調(diào)喲個。并且我們可以在調(diào)用的上面和下面添加功能以實(shí)現(xiàn)功能的增強(qiáng)。比如我們在代碼中是這樣寫的
def get_price(self): return self.drink_component.get_price() + self.add_milk_price()
我們也可以將其改寫為這樣
def get_price(self): print "add Milk" price = self.drink_component.get_price() new_price = price + self.add_milk_price() return new_price
我們在調(diào)用被修飾的類的前面增加了一個功能打印 "add milk"這件事,并在獲取了裝飾的產(chǎn)品價格后給架構(gòu)增加了一個牛奶的價格并將其返回。
總結(jié)通過裝飾模式來擴(kuò)展對象的功能比繼承模式更靈活。構(gòu)建和裝飾器可以獨(dú)立擴(kuò)展,新增功能不需要添加大量的子類。但是裝飾模式也產(chǎn)生了許多小對象,增加了排錯的難度。
以下情況適合使用裝飾器模式:
在不影響其他對象的情況下,以動態(tài)、透明的方式給單個對象添加職責(zé)。
需要動態(tài)地給一個對象增加功能,這些功能也可以動態(tài)地被撤銷。
當(dāng)不能采用繼承的方式對系統(tǒng)進(jìn)行擴(kuò)充或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時。不能采用繼承的情況主要有兩類:第一類是系統(tǒng)中存在大量獨(dú)立的擴(kuò)展,為支持每一種組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長;第二類是因?yàn)轭惗x不能繼承(如final類).[1]
引用[1] http://design-patterns.readth...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/44493.html
摘要:希望引以為戒鄭傳裝飾模式如果你了解,你肯定聽過裝飾器模式。在面向?qū)ο笾校b飾模式指動態(tài)地給一個對象添加一些額外的職責(zé)。就增加一些功能來說,裝飾模式比生成子類更為靈活。 漫談 如果作為一個Python入門,不了解Python裝飾器也沒什么,但是如果作為一個中級Python開發(fā)人員,如果再不對python裝飾器熟稔于心的話,那么可能并沒有量變積累到質(zhì)變。 我以前也看過很多講python 裝...
摘要:作者按每天一個設(shè)計(jì)模式旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用和兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶?shí)現(xiàn)方式原文地址是每天一個設(shè)計(jì)模式之裝飾者模式歡迎關(guān)注個人技術(shù)博客。 作者按:《每天一個設(shè)計(jì)模式》旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用javascript和python兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶?shí)現(xiàn)方式...
摘要:作者按每天一個設(shè)計(jì)模式旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用和兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶?shí)現(xiàn)方式原文地址是每天一個設(shè)計(jì)模式之裝飾者模式歡迎關(guān)注個人技術(shù)博客。 作者按:《每天一個設(shè)計(jì)模式》旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用javascript和python兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶?shí)現(xiàn)方式...
閱讀 3531·2023-04-25 18:52
閱讀 2551·2021-11-22 15:31
閱讀 1301·2021-10-22 09:54
閱讀 3077·2021-09-29 09:42
閱讀 661·2021-09-26 09:55
閱讀 992·2021-09-13 10:28
閱讀 1181·2019-08-30 15:56
閱讀 2166·2019-08-30 15:55