资讯专栏INFORMATION COLUMN

python设计模式-装饰器模式

Yuqi / 993人阅读

摘要:这种模式我们称之为装饰器模式。因为装饰器模式是在给对象增加责任。以下情况适合使用装饰器模式在不影响其他对象的情况下,以动态透明的方式给单个对象添加职责。

前言

本篇的装饰器模式不是讲解的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...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44493.html

相关文章

  • Python装饰vs装饰模式

    摘要:希望引以为戒郑传装饰模式如果你了解,你肯定听过装饰器模式。在面向对象中,装饰模式指动态地给一个对象添加一些额外的职责。就增加一些功能来说,装饰模式比生成子类更为灵活。 漫谈 如果作为一个Python入门,不了解Python装饰器也没什么,但是如果作为一个中级Python开发人员,如果再不对python装饰器熟稔于心的话,那么可能并没有量变积累到质变。 我以前也看过很多讲python 装...

    stackvoid 评论0 收藏0
  • Python装饰

    摘要:一引用书流畅的书二基本概念问题装饰器是什么解答严格来说,装饰器只是语法糖,装饰器是可调用的对象,可以像常规的可调用对象那样调用,特殊的地方是装饰器的参数是一个函数问题装饰器有什么特性解答装饰器有个特性,一是可以把被装饰的函数替换成其他函数, 一, 引用 [书] 流畅的Python [书] Effective Python 二, 基本概念 showImg(https://segme...

    aisuhua 评论0 收藏0
  • 每天一个设计模式装饰模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式原文地址是每天一个设计模式之装饰者模式欢迎关注个人技术博客。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式...

    brianway 评论0 收藏0
  • 每天一个设计模式装饰模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式原文地址是每天一个设计模式之装饰者模式欢迎关注个人技术博客。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式...

    shleyZ 评论0 收藏0
  • Python装饰

    摘要:此篇紧接上篇装饰器装饰器模式,上篇主要是简述了装饰器模式,跟的装饰器。再来看装饰器的实现。这时步轻松搞定装饰器中的例子还是精妙的。最近学数据结构与算法,写些装饰器用来看程序执行时间,真是再方便不过了 此篇紧接上篇 Python装饰器vs装饰器模式,上篇主要是简述了装饰器模式,跟Python的装饰器。 再来看Python装饰器的实现。这里我推荐一篇文章(译)-12步轻松搞定python装...

    sunsmell 评论0 收藏0

发表评论

0条评论

Yuqi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<