python中的装饰器、装饰器模式_Python装饰器模式

标签: python 设计模式 装饰器模式

AAffA0nNPuCLAAAAAElFTkSuQmCC

引子

对于装饰器模式我正在一点一点的理解........

使用对象组合的方式,做到在运行时装饰对象,使用这种方式,将能够在不修改任何底层代码的情况下,给对象赋予新的职责

想当然的方法

以星巴兹咖啡举例子,星巴兹有几种固定种类的咖啡,种类及价格见下表

种类

价格

HouseBlend

1.99

DarkRoast

1.79

Decaf

2.99

Espreso

1.39

他们共同继承自一个Beverage的抽象类

AAffA0nNPuCLAAAAAElFTkSuQmCC

这种时候,可以使用下面的代码将他处理的很好,毕竟也就是几种咖啡罢了

#父类

class Beverage(object):

def __init__(self):

self.description = "Unknown description"

def getDescription(self):

return self.description

def cost(self):

pass

#子类

class HouseBlend(Beverage):

def __init__(self):

super(HouseBlend, self).__init__()

self.description = "HouseBlend coffee"

def cost(self):

return 1.99

#子类

class DarkRoast(Beverage):

def __init__(self):

super(DarkRoast, self).__init__()

self.description = "DarkRoast coffee"

def cost(self):

return 1.79

#子类

class Decaf(Beverage):

def __init__(self):

super(Decaf, self).__init__()

self.description = "Decaf coffee"

def cost(self):

return 2.99

#子类

class Espresso(Beverage):

def __init__(self):

super(Espresso, self).__init__()

self.description = "Espresso coffee"

def cost(self):

return 1.39

#实例

for item in [HouseBlend(), DarkRoast(), Decaf(), Espresso()]:

print("{0}: {1}".format(item.getDescription(), item.cost()))

一共是五个类,打印的结果如下

HouseBlend coffee: 1.99

DarkRoast coffee: 1.79

Decaf coffee: 2.99

Espresso coffee: 1.39

当购买咖啡时,顾客要求添加各种各样的调料,例如:奶,糖,豆浆,摩卡,奶泡........

星巴兹会根据添加调料的不同,重新计算最终的价格,所以订单系统需要考虑到这些调料部分,这个时候怎么办,继续增加类,无穷无尽的类,哪天牛奶要是价格涨价了,那我就需要将所有涉及牛奶的子类价格全部更新一遍,那这一天就是粘贴复制了

AAffA0nNPuCLAAAAAElFTkSuQmCC

class Beverage(object):

def __init__(self):

self.description = "Unknow description"

def getDescription(self):

return self.description

def cost(self):

pass

class HouseBlend(Beverage):

def __init__(self):

super(HouseBlend, self).__init__()

self.description = "HouseBlend coffee"

def cost(self):

return 1.99

#我需要在下面定义无穷无尽的子类

......

......

......

这样弄下类,也许会有成百上千各类。

怎么办

可以将所有的调料放到父类里面,Beverage里面计算每种调料的价格,子类最后将自己的价格加到这些增加的调料价格上,这样的话好像还是五个类

class Beverage(object):

#定义两个类属性,作为初始值

condiment = 0.0

description = ""

#定义一个dict,用来存放调料

def __init__(self, **condiments):

for k, v in condiments.items():

setattr(self, k , v)

#下面是判断哪些调料使用了,之后加上这种调料的价格和描述

if condiments[k] == "milk":

Beverage.condiment += 0.99

Beverage.description += " add milk"

if condiments[k] == "soy":

Beverage.condiment += 0.89

Beverage.description += " add soy"

#下面可以不停的增加调料种类

def getDescription(self):

return self.description + Beverage.description

#这里只写了一种咖啡种类,其余三种格式全部一致,这里省略了

class HouseBlend(Beverage):

def __init__(self, **condiments):

super(HouseBlend, self).__init__(**condiments)

self.description = "HouseBlend Coffee "

#cost现在就为调料与本身的价格之和

def cost(self):

return Beverage.condiment + 1.99

#这个实例里增加了milk调料,打印最终的价格和描述

hb = HouseBlend(condi1="milk", condi2="soy", condi3="soy")

print("{0}: {1}".format(hb.getDescription(), hb.cost()))

这么设计看似解决了类爆炸的问题,但是它违反了一个原则,这个原则就是

类应该对扩展开放,对修改关闭

此处,如果有新的调料加入,就必须去对Beverage类进行修改,这样做似乎违反了面向对象界的一些规则,但我个人觉得没有什么,我不是写代码的,不知道这个原则到了大型程序上是不是后果很严重,不过想想写出的类一增加功能,就需要去原来的基础上修改代码,确实不利于后续维护,扩展也许是最好的选择。

新的目标

这样看来有了新的目标,就是将上面的代码改成扩展的,而不是修改的,这样只需要每次写一个新的调料放上去就行。

答案就是装饰器模式,每一种调料都是一个个装饰材料,而四种咖啡是被装饰对象,需要增加哪种调料,就把这个调料的装饰花环套在咖啡的脖子上,直到咖啡已经喝不了为止。

定义

动态地将责任附加到对象上,若有扩展功能,装饰者提供了比继承更具有弹性的替代方案

先看Beverage类,他是所有类的基类,定义了getDescription和cost方法

class Beverage(object):

def __init__(self):

self.description = "Unknown Beverage"

def getDescription(self):

return self.description

def cost(self):

pass

定义四个咖啡组件,这就是要被装饰者装饰的被装饰者,在这里定义了两个,其余两个格式都是一致的,只要更改一下description的内容和价格即可

class HouseBlend(Beverage):

def __init__(self):

super(HouseBlend, self).__init__()

self.description = "HouseBlend"

def cost(self):

return 1.99

class Espresso(Beverage):

def __init__(self):

super(Espresso, self).__init__()

self.description = "Espresso"

def cost(self):

return 1.39

def getDescription(self):

return self.description

接下来定义装饰者类,他们用来装饰被装饰者,在这里自然就是一堆堆的调料了

#这个类是装饰者的类的基类,其实没有也无所谓,因为所有的类都是`Beverage`类型

class CondimentDecorator(Beverage):

def getDescription(self):

pass

#这是调料milk

class Milk(CondimentDecorator):

#这里传入被装饰者,可以是咖啡组件,也可以是已经被mocha或其他调料装饰过的咖啡组件,主要

#看装饰者在装饰过程中的位置

def __init__(self, beverage):

super(Milk, self).__init__()

self.beverage = beverage

def getDescription(self):

return self.beverage.getDescription() + ", Milk"

def cost(self):

return self.beverage.cost() + .2

#这是调料mocha

class Mocha(CondimentDecorator):

#这里传入被装饰者,可以是咖啡组件,也可以是已经被milk或其他调料装饰过的咖啡组件,主要

#看装饰者在装饰过程中的位置

def __init__(self, beverage):

super(Mocha, self).__init__()

self.beverage = beverage

#这里自然是之前的组件描述加上这种调料的描述

def getDescription(self):

return self.beverage.getDescription() + ", Mocha"

#那这里自然也就是之前的组件的价格和这种调料的价格了

def cost(self):

return self.beverage.cost() + .3

#这后面你可以增加无穷无尽的调料

实现

#先建立一个咖啡组件的实例

beverage = HouseBlend()

#先被milk装饰一遍

beverage = Milk(beverage)

#再被mocha装饰一遍

beverage = Mocha(beverage)

#后面可以接着写,被其他别的调料接着装饰

print("{0}: {1}".format(beverage.getDescription(), "%.3f" % beverage.cost()))

输出看看

HouseBlend, Milk, Mocha: 2.490

现在再来看看下面这张图,应该理解的深刻一些

AAffA0nNPuCLAAAAAElFTkSuQmCC

回过头来看这些代码,好像变多了,但是解决了之前那个问题,如果有新的调料加入,不需要更改任何代码,只要增加就可以了,实现了扩展但不改变。

想来个大杯或者小杯怎么办

大杯和小杯的调料不能一个价格,当然我希望如此,那么接下来怎么改进代码,其实只要在基类里面增加一个跟size有关系的方法就行

class Beverage(object):

def __init__(self):

self.description = "Unknown Beverage"

def getDescription(self):

return self.description

def cost(self):

pass

def getSize(self):

return self.size

#设置一下杯子尺寸

def setSize(self, cupSize):

self.size = cupSize

之后将装饰者和被装饰者里面cost方法上增加一些判断即可,毕竟对价格产生直接影响

被装饰者

class HouseBlend(Beverage):

def __init__(self):

super(HouseBlend, self).__init__()

self.description = "HouseBlend"

def cost(self):

if self.getSize() == "TALL":

return 1.99

elif self.getSize() == "GRANDE":

return 2.99

elif self.getSize() == "VENTI":

return 3.99

装饰者

class Milk(CondimentDecorator):

def __init__(self, beverage):

super(Milk, self).__init__()

self.beverage = beverage

def getDescription(self):

return self.beverage.getDescription() + ", Milk"

def cost(self):

if self.beverage.getSize() == "TALL":

return self.beverage.cost() + .2

elif self.beverage.getSize() == "GRANDE":

return self.beverage.cost() + .25

elif self.beverage.getSize() == "VENTI":

return self.beverage.cost() + .30

结尾

python有一个装饰器功能,作用和这个模式是一样的,用这种方法实现应该更直接一些,但我还不会。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值