設(shè)計模式03裝飾者模式

python對裝飾器支持的非常好, 但是常見的python裝飾器并不代表整個裝飾者模式命满,它只是裝飾者模式的一種實現(xiàn)的表現(xiàn)耻蛇,模式一般試圖在宏觀上定義它的思想蜗侈。下面列出Python裝飾器的幾種寫法(這些代碼是我一位朋友收集的,經(jīng)過他同意我拿過來進行加工搬瑰,這是他的博客

函數(shù)裝飾器

1. 函數(shù)不帶參數(shù)
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper():
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun()

    return wrapper


@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world()


# 運行結(jié)果
裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
裝飾器內(nèi)部原理

要點:
裝飾器會在對象加載的時候就執(zhí)行, 而不是在執(zhí)行階段才執(zhí)行款票。為了更直觀體現(xiàn), 這里在加一個代碼片段。

# -.- coding:utf-8 -.-


def decorator(fun):

    print('這里是加載和檢查階段: 測試裝飾器是在執(zhí)行階段之前就已經(jīng)運行')

    def wrapper():
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun()

    return wrapper


print('這里是還沒檢查到decorator是一個裝飾器: 1')
@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str
print('這里是已經(jīng)檢查到decorator是一個裝飾器: 2')

if __name__ == '__main__':
    # 取消執(zhí)行這行代碼
    # hello_world()

    print('這里是執(zhí)行階段')

# 運行結(jié)果
這里是還沒檢查到decorator是一個裝飾器: 1
這里是加載和檢查階段: 測試裝飾器是在執(zhí)行階段之前就已經(jīng)運行
這里已經(jīng)檢查到decorator是一個裝飾器: 2
這里是執(zhí)行階段

備注:
仔細看上面的思維腦圖會發(fā)現(xiàn)加載了hello_world對象, 但是這個對象是實際上是一個decorator.<local>.wrapper這種東西泽论,讓它看起來正常一些艾少,可以使用functools.wrap,這個不是很重要翼悴,我支持做個備注缚够。

# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun()

    return wrapper


@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    # 沒有加functools.wrap之前下面這行顯示出來的結(jié)果是: wrapper
    # print(hello_world.__name__)

    # 加了functools.wrap之后下面這行顯示出來的結(jié)果是: hello_world
    print(hello_world.__name__)
    

# 運行結(jié)果
hello_world
2. 類不帶參數(shù)
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun()

    return wrapper


@decorator
class HelloWorld(object):

    def __init__(self):
        self.message = 'hello world!'
        print(self.message)

if __name__ == '__main__':
    HelloWorld()


# 運行結(jié)果
裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
3. 函數(shù)帶參數(shù)
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper(*args, **kwargs):                   # add here
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun(*args, **kwargs)                 # add here

    return wrapper


@decorator
def hello_world(introduce):                         # add here
    _str = 'hello world!\n{}'.format(introduce)     # invoke here       
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world("I'm Learning Python.")


# 運行結(jié)果
裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
I'm Learning Python.
4. 類帶參數(shù)
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper(*args, **kwargs):           # 調(diào)整這里
        print('裝飾器在這里動態(tài)的增加一條打印記錄.')
        return fun(*args, **kwargs)         # 調(diào)整這里

    return wrapper


@decorator
class HelloWorld(object):

    def __init__(self, message):            # 調(diào)整這里
        self.message = message              # 調(diào)整這里
        print(self.message) 

if __name__ == '__main__':
    HelloWorld('hello world!')           # 調(diào)整這里

5. 裝飾器帶參數(shù)
# -.- coding:utf-8 -.-


def decorator(status_code, msg=None):
    """
    
    :param status_code: 當傳過來的狀態(tài)碼是400-500區(qū)間時, 
                         就拋出異常(不執(zhí)行裝飾對象行數(shù)). 
    :param msg: 拋異常時顯示的信息.
    :return: 
    """
    def wrapper_(fun):
        def wrapper__(*args, **kwargs):
            if 400 <= status_code <= 500:
                raise RuntimeError(msg)
            fun(*args, **kwargs)
        return wrapper__

    return wrapper_


@decorator(401, '沒有權(quán)限訪問')
def hello_world(name, age, message='Learning python'):
    print('information: ', name, age, message)


if __name__ == '__main__':
    hello_world('zt', '18', '學習Python')


# 運行結(jié)果
Traceback (most recent call last):
  File "C:/Users/xx/_03_decorator_with_arguments.py", line 29, in <module>
    hello_world('zt', '18', '學習Python')
  File "C:/Users/xx/_03_decorator_with_arguments.py", line 16, in wrapper__
    raise RuntimeError(msg)
RuntimeError: 沒有權(quán)限訪問

?
?

類裝飾器

函數(shù)不帶參數(shù)
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('類裝飾器在這里動態(tài)的增加一條打印記錄.')
        self.func = func

    def __call__(self):
        self.func()


@Decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world()
    
    
# 運行結(jié)果
類裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!

備注:
還是語法原則,當檢查到@符號聲明裝飾器時,即便不運行任何代碼谍椅,python也會去執(zhí)行這個裝飾器误堡,并將這個hello_world當做參數(shù)傳遞給這個裝飾器。

類不帶參數(shù)
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('類裝飾器在這里動態(tài)的增加一條打印記錄.')
        self.func = func

    def __call__(self):
        self.func()


@Decorator
class HelloWorld(object):

    def __init__(self):
        self.message = 'hello world!'
        print(self.message)

if __name__ == '__main__':
    HelloWorld()


# 運行結(jié)果
類裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
函數(shù)帶參數(shù)
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('類裝飾器在這里動態(tài)的增加一條打印記錄.')
        self.func = func

    def __call__(self, *args, **kwargs):        # 調(diào)整這里
        self.func(*args, **kwargs)              # 調(diào)整這里


@Decorator
def hello_world(message):                       # 調(diào)整這里
    _str = message                              # 調(diào)整這里
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world('hello world!')


# 運行結(jié)果
類裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
類帶參數(shù)
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('類裝飾器在這里動態(tài)的增加一條打印記錄.')
        self.func = func

    def __call__(self, *args, **kwargs):            # 調(diào)整這里
        self.func(*args, **kwargs)                   # 調(diào)整這里


@Decorator
class HelloWorld(object):

    def __init__(self, message):                    # 調(diào)整這里
        self.message = message                      # 調(diào)整這里
        print(self.message)

if __name__ == '__main__':
    HelloWorld('hello world!')


# 運行結(jié)果
類裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!
類裝飾器帶參數(shù)
# coding: utf-8


class Decorator(object):

    def __init__(self, status_code, msg=None):
        print('類裝飾器在這里動態(tài)的增加一條打印記錄.')
        if 400 <= status_code <= 500:
            raise RuntimeError(msg)

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper


@Decorator(status_code=401, msg='沒有權(quán)限訪問')
class HelloWorld(object):

    def __init__(self, message):
        self.message = message
        print(self.message)

if __name__ == '__main__':
    HelloWorld(message='hello world!')


# 運行結(jié)果
類裝飾器在這里動態(tài)的增加一條打印記錄.
Traceback (most recent call last):
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 17, in <module>
    @Decorator(status_code=401, msg='沒有權(quán)限訪問')
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 9, in __init__
    raise RuntimeError(msg)
RuntimeError: 沒有權(quán)限訪問


# 將status_code參數(shù)的值改為399, 再次運行
類裝飾器在這里動態(tài)的增加一條打印記錄.
hello world!

小結(jié)
上面就是所有的裝飾器辦法雏吭,你認為函數(shù)裝飾器和類裝飾器能去裝飾一個類中的某個方法嗎锁施? 要不自己試一下?

?
?

裝飾者模式

我閱讀的書籍是Head First設(shè)計模式思恐,書上的故事場景是圍繞星巴克的咖啡品種和調(diào)料來動態(tài)計算價格; 例如某種類型的咖啡底價是0.9美元,加一種調(diào)料需要相應(yīng)的增加一些價格膊毁,一杯咖啡可以隨意組合多種調(diào)料(也允許對某一種調(diào)料加多份)胀莹,最終計算出咖啡的價格。

代碼
from __future__ import print_function


class DollarToRMB(object):

    @staticmethod
    def convert(dollar):
        return dollar * 6.8


################################################################################
# Beverages                                                                    #
################################################################################
class Beverage(object):

    def __init__(self):
        self._description = '飲料'
        self._price = 0

    @property
    def description(self):
        return self._description

    @property
    def price(self):
        return DollarToRMB.convert(self._price)

    def cost(self):
        raise NotImplementedError


class HouseBlend(Beverage):

    def __init__(self):
        super(HouseBlend, self).__init__()
        self._description = "星巴克首選咖啡"
        self._price = 0.89

    def cost(self):
        return self.price


class DarkRoast(Beverage):

    def __init__(self):
        super(DarkRoast, self).__init__()
        self._description = "星巴克深度烘焙咖啡"
        self._price = .99

    def cost(self):
        return self.price


class Espresso(Beverage):

    def __init__(self):
        super(Espresso, self).__init__()
        self._description = "星巴克意式濃縮咖啡"
        self._price = 1.99

    def cost(self):
        return self.price


class Decaf(Beverage):

    def __init__(self):
        super(Decaf, self).__init__()
        self._description = "星巴克不含咖啡因的咖啡"
        self._price = 1.05

    def cost(self):
        return self.price


################################################################################
# Condiment decorators                                                         #
################################################################################
class CondimentDecorator(Beverage):

    def __init__(self, beverage):
        super(CondimentDecorator, self).__init__()
        self._description = ", 調(diào)味品"
        self._price = 0
        self.beverage = beverage

    def description(self):
        raise NotImplementedError

    def cost(self):
        raise NotImplementedError


class Milk(CondimentDecorator):

    def __init__(self, beverage):
        super(Milk, self).__init__(beverage)
        self._description = ", 牛奶"
        self._price = .10

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Mocha(CondimentDecorator):

    def __init__(self, beverage):
        super(Mocha, self).__init__(beverage)
        self._description = ", 摩卡"
        self._price = .20

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Soy(CondimentDecorator):

    def __init__(self, beverage):
        super(Soy, self).__init__(beverage)
        self._description = ", 豆奶"
        self._price = .15

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Whip(CondimentDecorator):

    def __init__(self, beverage):
        super(Whip, self).__init__(beverage)
        self._description = ", 奶泡"
        self._price = .10

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


if __name__ == '__main__':
    beverage = Espresso()
    print('{!s:<50}{}'.format(beverage.description, str(beverage.cost())))

    beverage2 = DarkRoast()
    beverage2 = Mocha(beverage2)
    beverage2 = Mocha(beverage2)
    beverage2 = Whip(beverage2)
    print('{!s:<50}{}'.format(beverage2.description, str(beverage2.cost())))

    beverage3 = HouseBlend()
    beverage3 = Soy(beverage3)
    beverage3 = Mocha(beverage3)
    beverage3 = Whip(beverage3)
    print('{!s:<50}{}'.format(beverage3.description,  str(beverage3.cost())))


# 運行結(jié)果
星巴克意式濃縮咖啡   13.532
星巴克深度烘焙咖啡, 摩卡, 摩卡, 奶泡   10.132
星巴克首選咖啡, 豆奶, 摩卡, 奶泡    9.111999999999998

模式總結(jié)

如果采用python版本的裝飾器婚温,那么這個功能就很難寫的出來描焰,因為沒有利用到繼承、接口和組合的特性栅螟。
都是裝飾器荆秦,在應(yīng)對不同的場景是有不同的寫法,像python版本的裝飾器最好是用來解決單一的一次性目的(例如: 驗證力图、條件篩選)步绸;而OOP式的裝飾器就顯得特別靈活,能不能寫得好吃媒,寫的合理不合理瓤介,特別需要個人的功底積累。

核心理念

在不改變原有代碼的基礎(chǔ)上為其封裝額外的行為(wrap functionality with other functionality in order to affect outputs).

模式類型

結(jié)構(gòu)模式

設(shè)計原則
  1. 類應(yīng)該對擴展開放赘那,對修改關(guān)閉刑桑。
  2. 動態(tài)計算(或者叫累計計算)。

?
?

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末募舟,一起剝皮案震驚了整個濱河市祠斧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拱礁,老刑警劉巖琢锋,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呢灶,居然都是意外死亡吩蔑,警方通過查閱死者的電腦和手機财剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門江兢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宜咒,你說我怎么就攤上這事∽嘎Γ” “怎么了仆潮?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長遣臼。 經(jīng)常有香客問我性置,道長,這世上最難降的妖魔是什么揍堰? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任鹏浅,我火速辦了婚禮,結(jié)果婚禮上屏歹,老公的妹妹穿的比我還像新娘隐砸。我一直安慰自己,他們只是感情好蝙眶,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布季希。 她就那樣靜靜地躺著,像睡著了一般幽纷。 火紅的嫁衣襯著肌膚如雪式塌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天友浸,我揣著相機與錄音峰尝,去河邊找鬼。 笑死收恢,一個胖子當著我的面吹牛境析,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播派诬,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼劳淆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了默赂?” 一聲冷哼從身側(cè)響起沛鸵,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缆八,沒想到半個月后曲掰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡奈辰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年栏妖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奖恰。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡吊趾,死狀恐怖宛裕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情论泛,我是刑警寧澤揩尸,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站屁奏,受9級特大地震影響岩榆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坟瓢,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一勇边、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧折联,春花似錦粒褒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谊囚。三九已至怕享,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镰踏,已是汗流浹背函筋。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奠伪,地道東北人跌帐。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像绊率,于是被迫代替她去往敵國和親谨敛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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

  • 要點: 函數(shù)式編程:注意不是“函數(shù)編程”滤否,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍盍忱辍傩浴?..
    victorsungo閱讀 1,510評論 0 6
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)藐俺,斷路器炊甲,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 前言 人生苦多,快來 Kotlin 欲芹,快速學習Kotlin卿啡! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,211評論 9 118
  • 1 場景問題# 1.1 復(fù)雜的獎金計算## 考慮這樣一個實際應(yīng)用:就是如何實現(xiàn)靈活的獎金計算菱父。 獎金計算是相對復(fù)雜...
    七寸知架構(gòu)閱讀 4,007評論 4 67
  • 內(nèi)置函數(shù)Python解釋器內(nèi)置了許多功能和類型,總是可用的颈娜。他們是按字母順序列在這里剑逃。 abs(x)返回一個數(shù)的絕...
    uangianlap閱讀 1,239評論 0 0