python设计模式-装饰器模式

前言

本篇的装饰器模式不是讲解的python中的语法糖 @ 这个装饰器。而是讲解设计模式中的装饰器模式。网上很多的实现都是基于java和c++的。本文则使用python来实现,其中有些实现可能在python并不需要那样来写的,但是思路都是一样的。关于python @ 装饰器的使用我之后会再写一篇文章来介绍。

产生装饰器模式的动机是什么?

大家知道我们有的时候总是需要给给一个类或者一个对象增加一些行为。一般情况下使用继承和关联两种方式来实现。其中使用关联这种方式来实现并符合一定的设计规范的我们称之为装饰器模式。接下来我们首先会先介绍一下这两种方法,然后用python代码来分别实现这两种方法并比较他们之间的差异。

继承方式

一般情况下我们都是通过继承来给一个或一群类来添加方法,通过继承使子类获得了父类的行为。虽然继承是一种适用广泛的方法,但是继承是一种静态行为,在代码编写阶段就已经固定,无法动态的控制一个类增加行为的种类和时间。

关联关系

我们通过将一个A对象嵌入另一个B对象里面,及将一个B对象里面的属性的值设置为A对象。通过在调用A对象的动作前后添加行为来给A对象增加功能。这种模式我们称之为装饰器模式。

“装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。”[1]

因为装饰器模式是在给对象增加责任。所以装饰器模式为对象结构型设计模式(对象是因为其是给对象而不是类增加责任,结构型模式就是描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构)。

实例讲解

代码描述

一个饮料店里面卖茶和咖啡。 并且有冰块,糖和牛奶三种辅料可以添加。 我们可以计算出一共有14中组合产品。并且每增加一种饮料就要增加7种组合产品。

继承方式

UML类图

《python设计模式-装饰器模式》

代码

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类图

《python设计模式-装饰器模式》

代码

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)的父类,主要定义了具体构建类的业务方法。以及让我们在调用的时候可以统一的处理装饰前和装饰后的对象。方便我们使用装饰器类装饰一个已经被装饰的具体构建如加糖(加冰(咖啡))。在关联关系中我们主要说一下这个部分。
《python设计模式-装饰器模式》

这是一个关联聚合关系。表示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://segmentfault.com/a/1190000012064562
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞