文档章节

设计模式03装饰者模式

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:18
字数 2443
阅读 1
收藏 0
点赞 0
评论 0

python对装饰器支持的非常好, 但是常见的python装饰器并不代表整个装饰者模式,它只是装饰者模式的一种实现的表现,模式一般试图在宏观上定义它的思想。下面列出Python装饰器的几种写法(这些代码是我一位朋友收集的,经过他同意我拿过来进行加工,这是他的博客

函数装饰器

1. 函数不带参数
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


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


if __name__ == '__main__':
    hello_world()


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!

装饰器内部原理

要点:
装饰器会在对象加载的时候就执行, 而不是在执行阶段才执行。为了更直观体现, 这里在加一个代码片段。

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


def decorator(fun):

    print('这里是加载和检查阶段: 测试装饰器是在执行阶段之前就已经运行')

    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


print('这里是还没检查到decorator是一个装饰器: 1')
@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str
print('这里是已经检查到decorator是一个装饰器: 2')

if __name__ == '__main__':
    # 取消执行这行代码
    # hello_world()

    print('这里是执行阶段')

# 运行结果
这里是还没检查到decorator是一个装饰器: 1
这里是加载和检查阶段: 测试装饰器是在执行阶段之前就已经运行
这里已经检查到decorator是一个装饰器: 2
这里是执行阶段

备注:
仔细看上面的思维脑图会发现加载了hello_world对象, 但是这个对象是实际上是一个decorator.<local>.wrapper这种东西,让它看起来正常一些,可以使用functools.wrap,这个不是很重要,我支持做个备注。

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


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


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


if __name__ == '__main__':
    # 没有加functools.wrap之前下面这行显示出来的结果是: wrapper
    # print(hello_world.__name__)

    # 加了functools.wrap之后下面这行显示出来的结果是: hello_world
    print(hello_world.__name__)


# 运行结果
hello_world
2. 类不带参数
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


@decorator
class HelloWorld(object):

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

if __name__ == '__main__':
    HelloWorld()


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!
3. 函数带参数
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper(*args, **kwargs):                   # add here
        print('装饰器在这里动态的增加一条打印记录.')
        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.")


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!
I'm Learning Python.
4. 类带参数
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper(*args, **kwargs):           # 调整这里
        print('装饰器在这里动态的增加一条打印记录.')
        return fun(*args, **kwargs)         # 调整这里

    return wrapper


@decorator
class HelloWorld(object):

    def __init__(self, message):            # 调整这里
        self.message = message              # 调整这里
        print(self.message) 

if __name__ == '__main__':
    HelloWorld('hello world!')           # 调整这里
5. 装饰器带参数
# -.- coding:utf-8 -.-


def decorator(status_code, msg=None):
    """

    :param status_code: 当传过来的状态码是400-500区间时, 
                         就抛出异常(不执行装饰对象行数). 
    :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, '没有权限访问')
def hello_world(name, age, message='Learning python'):
    print('information: ', name, age, message)


if __name__ == '__main__':
    hello_world('zt', '18', '学习Python')


# 运行结果
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: 没有权限访问

 
 

类装饰器

函数不带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self):
        self.func()


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


if __name__ == '__main__':
    hello_world()


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!

备注:
还是语法原则,当检查到@符号声明装饰器时,即便不运行任何代码,python也会去执行这个装饰器,并将这个hello_world当做参数传递给这个装饰器。

类不带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        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()


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
函数带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self, *args, **kwargs):        # 调整这里
        self.func(*args, **kwargs)              # 调整这里


@Decorator
def hello_world(message):                       # 调整这里
    _str = message                              # 调整这里
    print(_str)
    return _str


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


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
类带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self, *args, **kwargs):            # 调整这里
        self.func(*args, **kwargs)                   # 调整这里


@Decorator
class HelloWorld(object):

    def __init__(self, message):                    # 调整这里
        self.message = message                      # 调整这里
        print(self.message)

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


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
类装饰器带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, status_code, msg=None):
        print('类装饰器在这里动态的增加一条打印记录.')
        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='没有权限访问')
class HelloWorld(object):

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

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


# 运行结果
类装饰器在这里动态的增加一条打印记录.
Traceback (most recent call last):
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 17, in <module>
    @Decorator(status_code=401, msg='没有权限访问')
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 9, in __init__
    raise RuntimeError(msg)
RuntimeError: 没有权限访问


# 将status_code参数的值改为399, 再次运行
类装饰器在这里动态的增加一条打印记录.
hello world!

小结
上面就是所有的装饰器办法,你认为函数装饰器和类装饰器能去装饰一个类中的某个方法吗? 要不自己试一下?

 
 

装饰者模式

我阅读的书籍是Head First设计模式,书上的故事场景是围绕星巴克的咖啡品种和调料来动态计算价格; 例如某种类型的咖啡底价是0.9美元,加一种调料需要相应的增加一些价格,一杯咖啡可以随意组合多种调料(也允许对某一种调料加多份),最终计算出咖啡的价格。

代码
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 = ", 调味品"
        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())))


# 运行结果
星巴克意式浓缩咖啡   13.532
星巴克深度烘焙咖啡, 摩卡, 摩卡, 奶泡   10.132
星巴克首选咖啡, 豆奶, 摩卡, 奶泡    9.111999999999998

模式总结

如果采用python版本的装饰器,那么这个功能就很难写的出来,因为没有利用到继承、接口和组合的特性。
都是装饰器,在应对不同的场景是有不同的写法,像python版本的装饰器最好是用来解决单一的一次性目的(例如: 验证、条件筛选);而OOP式的装饰器就显得特别灵活,能不能写得好,写的合理不合理,特别需要个人的功底积累。

核心理念

在不改变原有代码的基础上为其封装额外的行为(wrap functionality with other functionality in order to affect outputs).

模式类型

结构模式

设计原则
  1. 类应该对扩展开放,对修改关闭。
  2. 动态计算(或者叫累计计算)。

 
 

参考

© 著作权归作者所有

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
JavaScript 中常见设计模式整理

开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知。 JavaScript 中...

牧云云 ⋅ 05/18 ⋅ 0

Java 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei ⋅ 05/26 ⋅ 0

Android 设计模式-装饰模式(Decorator Pattern)

定义 饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 UML结构图 Component:组件对象接口 Concre...

Code猎人 ⋅ 05/10 ⋅ 0

ES7 Decorator 装饰者模式

原作者:玄农 装饰模式 设计模式大家都有了解,网上有很多系列教程,比如 JS设计模式等等。 这里只分享 装饰者模式 以及在 如何使用 ES7 的 概念 装饰模式 v.s. 适配器模式 装饰模式和适配器...

_朴灵_ ⋅ 05/14 ⋅ 0

设计模式之修饰器模式详解(附源代码)

装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 这种模式创建了一个...

thorhill ⋅ 03/24 ⋅ 0

代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

前言 今天我来全面总结开发中最常用的设计模式 - 代理模式中的动态代理模式 其他设计模式介绍 1分钟全面了解“设计模式” 单例模式(Singleton) - 最易懂的设计模式解析 简单工厂模式(Sim...

Carson_Ho ⋅ 04/09 ⋅ 0

你需要了解的23种JavaScript设计模式

为什么要学习设计模式? 在许多访谈中,你可能会遇到很多面向对象编程中的接口,抽象类,代理和以及其他与设计模式相关的问题。 一旦了解了设计模式,它会让你轻松应对任何访谈,并可以在你的...

java高级架构牛人 ⋅ 06/02 ⋅ 0

设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu ⋅ 2017/11/02 ⋅ 0

JavaScript设计模式之观察者模式

前言 准备研究一下MVVM的一些东西,由于MVVM运用了观察者模式的思想,因此翻开了《JavaScript设计模式与开发实践》一书,将观察者模式学习了一遍,顺便有对一些常用的设计模式进行一些了解,...

Srtian ⋅ 05/22 ⋅ 0

设计模式.策略模式

策略模式跟抽象工厂非常相似,基本逻辑是根据需要实例化出需要用的类。不同的是策略模式需要调用者非常清晰的知道有哪些策略,各个策略的调用规则,而抽象工厂的话,需要知道有哪些类,找到调...

技术小胖子 ⋅ 2017/11/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

Spring JDBC使用方法

普通实现: 1、创建数据表customer。 可以使用任何数据库实现,在项目中要引入相应数据库驱动包并配置相应数据库连接。 2、创建Customer pojo。 Customer类的属性对应数据库的属性,除了为每...

霍淇滨 ⋅ 今天 ⋅ 0

Contos 7 安装Jenkins

Jenkins是一款能提高效率的软件,它能帮你把软件开发过程形成工作流,典型的工作流包括以下几个步骤 开发 提交 编译 测试 发布 有了Jenkins的帮助,在这5步中,除了第1步,后续的4步都是自动...

欧虞山 ⋅ 今天 ⋅ 0

revel

revel install go get github.com/revel/revelgo get github.com/revel/cmd create new app revel new git.oschina.net/zdglf/myapp run app revel run git.oschina.net/zdglf/myapp ot......

zdglf ⋅ 今天 ⋅ 0

49. Group Anagrams - LeetCode

Question 49. Group Anagrams Solution 思路:维护一个map,key是输入数组中的字符串(根据字符排好序) Java实现: public List<List<String>> groupAnagrams(String[] strs) { Map<Strin......

yysue ⋅ 今天 ⋅ 0

spring Email

使用spring发Email其实就是使用spring自己封装携带的一个javamail.JavaMailSenderImpl类而已。这个类可以当一个普通的java对象来使用,也可以通过把它配置变成spring Bean的方式然后注入使用...

BobwithB ⋅ 今天 ⋅ 0

spark 整理的一些知识

Spark 知识点 请描述spark RDD原理与特征? RDD全称是resilient distributed dataset(具有弹性的分布式数据集)。一个RDD仅仅是一个分布式的元素集合。在Spark中,所有工作都表示为创建新的...

tuoleisi77 ⋅ 今天 ⋅ 0

思考

时间一天天过感觉自己有在成长吗?最怕的是时光匆匆而过,自己没有收获!下面总结下最近自己的思考。 认识自己 认识另一个自己,人们常说要虚心听取别人意见和建议。然而人往往是很难做到的,...

hello_hp ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部