摘要:這種模式我們稱之為裝飾器模式。因為裝飾器模式是在給對象增加責任。以下情況適合使用裝飾器模式在不影響其他對象的情況下,以動態透明的方式給單個對象添加職責。
前言
本篇的裝飾器模式不是講解的python中的語法糖 @ 這個裝飾器。而是講解設計模式中的裝飾器模式。網上很多的實現都是基于java和c++的。本文則使用python來實現,其中有些實現可能在python并不需要那樣來寫的,但是思路都是一樣的。關于python @ 裝飾器的使用我之后會再寫一篇文章來介紹。
產生裝飾器模式的動機是什么?大家知道我們有的時候總是需要給給一個類或者一個對象增加一些行為。一般情況下使用繼承和關聯兩種方式來實現。其中使用關聯這種方式來實現并符合一定的設計規范的我們稱之為裝飾器模式。接下來我們首先會先介紹一下這兩種方法,然后用python代碼來分別實現這兩種方法并比較他們之間的差異。
繼承方式一般情況下我們都是通過繼承來給一個或一群類來添加方法,通過繼承使子類獲得了父類的行為。雖然繼承是一種適用廣泛的方法,但是繼承是一種靜態行為,在代碼編寫階段就已經固定,無法動態的控制一個類增加行為的種類和時間。
關聯關系我們通過將一個A對象嵌入另一個B對象里面,及將一個B對象里面的屬性的值設置為A對象。通過在調用A對象的動作前后添加行為來給A對象增加功能。這種模式我們稱之為裝飾器模式。
“裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任,換言之,客戶端并不會覺得對象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要創造更多子類的情況下,將對象的功能加以擴展。這就是裝飾模式的模式動機。”[1]
因為裝飾器模式是在給對象增加責任。所以裝飾器模式為對象結構型設計模式(對象是因為其是給對象而不是類增加責任,結構型模式就是描述如何將類或者對象結合在一起形成更大的結構,就像搭積木,可以通過簡單積木的組合形成復雜的、功能更為強大的結構)。
實例講解 代碼描述一個飲料店里面賣茶和咖啡。 并且有冰塊,糖和牛奶三種輔料可以添加。 我們可以計算出一共有14中組合產品。并且每增加一種飲料就要增加7種組合產品。
繼承方式 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即飲料和配料兩個抽象類,并分別實現了具體的構建類。當我們要產生一個產品的時候。通過繼承不同的具體構建類來實現。比如加冰加牛奶的茶。我們通過繼承茶、牛奶和冰塊三個類來實現。可以看出如果要實現所有的類那么我們需要14個子類來完成。支持多繼承的語言才能這樣實現如果是單繼承的語言則需要通過多級繼承來完成。不僅冗余度增加而且復雜的多級繼承關系是后期維護的淚。
關聯方式 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 是抽象構件類,它是具體構建類(*ConcreteComponent)和抽象裝飾器類(IngredientsDecorator)的父類,主要定義了具體構建類的業務方法。以及讓我們在調用的時候可以統一的處理裝飾前和裝飾后的對象。方便我們使用裝飾器類裝飾一個已經被裝飾的具體構建如加糖(加冰(咖啡))。在關聯關系中我們主要說一下這個部分。
這是一個關聯聚合關系。表示IngredientsDecorator是知道DrinkComponent類的存在的呢。這個大家可以這樣理解。你在實現Concretecomponet的時候是不需要考慮IngredinetsDecorator的存在,因為你不會調用它的,也不繼承它,也不知道你會被它調用。但是在設計實現ConcreteDecorator的時候你會在其屬性中保持一個對DrinkComponet類型的類的引用。并且你會調用她的方法。這樣你就要知道DrinkCompoent這個類里面都有什么方法及要知道DrinkComponent類的存在。在另一種設計模式橋接模式這種關系正好是相反的。我之后再來寫一篇關于橋接模式的介紹。
我們在代碼中可以看到在IngredientsDecorator中也有get_price 和 get_name兩種方法。這是為了保證在ConcreteComponent在被裝飾器后還是可以像沒有被裝飾那樣被調喲個。并且我們可以在調用的上面和下面添加功能以實現功能的增強。比如我們在代碼中是這樣寫的
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
我們在調用被修飾的類的前面增加了一個功能打印 "add milk"這件事,并在獲取了裝飾的產品價格后給架構增加了一個牛奶的價格并將其返回。
總結通過裝飾模式來擴展對象的功能比繼承模式更靈活。構建和裝飾器可以獨立擴展,新增功能不需要添加大量的子類。但是裝飾模式也產生了許多小對象,增加了排錯的難度。
以下情況適合使用裝飾器模式:
在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。
需要動態地給一個對象增加功能,這些功能也可以動態地被撤銷。
當不能采用繼承的方式對系統進行擴充或者采用繼承不利于系統擴展和維護時。不能采用繼承的情況主要有兩類:第一類是系統中存在大量獨立的擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長;第二類是因為類定義不能繼承(如final類).[1]
引用[1] http://design-patterns.readth...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44493.html
摘要:希望引以為戒鄭傳裝飾模式如果你了解,你肯定聽過裝飾器模式。在面向對象中,裝飾模式指動態地給一個對象添加一些額外的職責。就增加一些功能來說,裝飾模式比生成子類更為靈活。 漫談 如果作為一個Python入門,不了解Python裝飾器也沒什么,但是如果作為一個中級Python開發人員,如果再不對python裝飾器熟稔于心的話,那么可能并沒有量變積累到質變。 我以前也看過很多講python 裝...
摘要:作者按每天一個設計模式旨在初步領會設計模式的精髓,目前采用和兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式原文地址是每天一個設計模式之裝飾者模式歡迎關注個人技術博客。 作者按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前采用javascript和python兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式...
摘要:作者按每天一個設計模式旨在初步領會設計模式的精髓,目前采用和兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式原文地址是每天一個設計模式之裝飾者模式歡迎關注個人技術博客。 作者按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前采用javascript和python兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式...
閱讀 3471·2023-04-25 18:52
閱讀 2484·2021-11-22 15:31
閱讀 1224·2021-10-22 09:54
閱讀 3010·2021-09-29 09:42
閱讀 605·2021-09-26 09:55
閱讀 909·2021-09-13 10:28
閱讀 1098·2019-08-30 15:56
閱讀 2107·2019-08-30 15:55