Python與設計模式--裝飾器模式

一、快餐點餐系統

又提到了那個快餐點餐系統,不過今天我們只以其中的一個類作爲主角:飲料類。首先,回憶下飲料類:

class Beverage():
    name = ""
    price = 0.0
    type = "BEVERAGE"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name


class coke(Beverage):
    def __init__(self):
        self.name = "coke"
        self.price = 4.0


class milk(Beverage):
    def __init__(self):
        self.name = "milk"
        self.price = 5.0

除了基本配置,快餐店賣可樂時,可以選擇加冰,如果加冰的話,要在原價上加0.3元;賣牛奶時,可以選擇加糖,如果加糖的話,要原價上加0.5元。怎麼解決這樣的問題?可以選擇裝飾器模式來解決這一類的問題。首先,定義裝飾器類:

class drinkDecorator():
    def getName(self):
        pass
    def getPrice(self):
        pass

class iceDecorator(drinkDecorator):
    def __init__(self,beverage):
        self.beverage=beverage
    def getName(self):
        return self.beverage.getName()+" +ice"
    def getPrice(self):
        return self.beverage.getPrice()+0.3
    
class sugarDecorator(drinkDecorator):
    def __init__(self,beverage):
        self.beverage=beverage
    def getName(self):
        return self.beverage.getName()+" +sugar"
    def getPrice(self):
        return self.beverage.getPrice()+0.5

構建好裝飾器後,在具體的業務場景中,就可以與飲料類進行關聯。以可樂+冰爲例,示例業務場景如下:

if  __name__=="__main__":
    coke_cola=coke()
    print "Name:%s"%coke_cola.getName()
    print "Price:%s"%coke_cola.getPrice()
    ice_coke=iceDecorator(coke_cola)
    print "Name:%s" % ice_coke.getName()
    print "Price:%s" % ice_coke.getPrice()

'''
打印結果如下:


Name:coke
Price:4.0
Name:coke +ice
Price:4.3
'''

二、裝飾器模式

裝飾器模式定義如下:動態地給一個對象添加一些額外的職責。在增加功能方面,裝飾器模式比生成子類更爲靈活。

裝飾器模式和上一節說到的代理模式非常相似,可以認爲,裝飾器模式就是代理模式的一個特殊應用,兩者的共同點是都具有相同的接口,不同點是側重對主題類的過程的控制,而裝飾模式則側重對類功能的加強或減弱。
上一次說到,JAVA中的動態代理模式,是實現AOP的重要手段。而在Python中,AOP通過裝飾器模式實現更爲簡潔和方便。
先來解釋一下什麼是AOP。AOP即Aspect Oriented Programming,中文翻譯爲面向切面的編程,它的含義可以解釋爲:如果幾個或更多個邏輯過程中(這類邏輯過程可能位於不同的對象,不同的接口當中),有重複的操作行爲,就可以將這些行爲提取出來(即形成切面),進行統一管理和維護。舉例子說,系統中需要在各個地方打印日誌,就可以將打印日誌這一操作提取出來,作爲切面進行統一維護。
從編程思想的關係來看,可以認爲AOP和OOP(面向對象的編程)是並列關係,二者是可以替換的,也可以結合起來用。實際上,在Python語言中,是天然支持裝飾器的,如下例:

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

@log
def now():
    print '2016-12-04'
if  __name__=="__main__":
    now()

'''

打印如下:


call now():
2016-12-04
'''


log接口就是裝飾器的定義,而Python的@語法部分則直接支持裝飾器的使用。
如果要在快餐點餐系統中打印日誌,該如何進行AOP改造呢?可以藉助類的靜態方法或者類方法來實現:

class LogManager:
    @staticmethod
    def log(func):
        def wrapper(*args):
            print "Visit Func %s"%func.__name__
            return func(*args)
        return wrapper

在需要打印日誌的地方直接@LogManager.log,即可打印出訪問的日誌信息。如,在beverage類的函數前加上@LogManager.log,場景類保持不變,則打印結果如下:


Visit Func getName
Name:coke
Visit Func getPrice
Price:4.0
Visit Func getName
Name:coke +ice
Visit Func getPrice
Price:4.3

 

三、裝飾器模式的優點和應用場景

優點:

1、裝飾器模式是繼承方式的一個替代方案,可以輕量級的擴展被裝飾對象的功能;
2、Python的裝飾器模式是實現AOP的一種方式,便於相同操作位於不同調用位置的統一管理。

應用場景:

1、需要擴展、增強或者減弱一個類的功能,如本例。

 

四、裝飾器模式的缺點

1、多層裝飾器的調試和維護有比較大的困難。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章