建议 51:用 mixin 模式让程序更加灵活
建议 51:用 mixin 模式让程序更加灵活
模板方法模式就是在一个方法中定义一个算法的骨架,并将一些实现步骤延迟到子类中。模板方法可以使子类在不改变算法结构的情况下,重新定义算法中的某些步骤。
来看一个例子:
class People(object): def make_tea(self): teapot = self.get_teapot() teapot.put_in_tea() teapot.put_in_water() return teapot
显然get_teapot()方法并不需要预先定义,也就是说我们的基类不需要预先申明抽象方法,子类只需要继承 People 类并实现get_teapot(),这给调试代码带来了便利。
但我们又想到如果一个子类 StreetPeople 描述的是正走在街上的人,那这个类将不会实现get_teapot(),一调用make_tea()就会产生找不到get_teapot()的 AttributeError,所以此时程序员应该立马想到,随着需求的增多,越来越多的 People 子类会选择不喝茶而喝咖啡,或者是抽雪茄之类的,按照以上的思路,我们的代码只会变得越发难以维护。
所以我们希望能够动态生成不同的实例:
class UseSimpleTeapot(object):
def get_teapot(self):
return SimpleTeapot()
class UseKungfuTeapot(object):
def get_teapot(self):
return KungfuTeapot()
class OfficePeople(People, UseSimpleTeapot): pass
class HomePeople(People, UseSimpleTeapot): pass
class Boss(People, UseKungfuTeapot): pass
def simple_tea_people():
people = People()
people.__base__ += (UseSimpleTeapot,)
return people
def coffee_people():
people = People()
people.__base__ += (UseCoffeepot,)
def tea_and_coffee_people():
people = People()
people.__base__ += (UseSimpleTeapot, UserCoffeepot,)
return people
def boss():
people = People()
people.__base__ += (KungfuTeapot, UseCoffeepot, )
return people
以上代码的原理在于每个类都有一个__bases__属性,它是一个元组,用来存放所有的基类,作为动态语言,Python 中的基类可以在运行中可以动态改变。所以当我们向其中增加新的基类时,这个类就拥有了新的方法,这就是混入mixin。
利用这个技术我们可以在不修改代码的情况下就可以完成需求:
import mixins # 把员工需求定义在 Mixin 中放在 mixins 模块 def staff(): people = People() bases = [] for i in config.checked(): bases.append(getattr(maxins, i)) people.__base__ += tuple(bases) return people
mixin也是为了代码复用,有两个主要的使用场景:
- 你希望给一个类提供很多可选的特征(feature).
- 你希望在很多不同的类中使用一个特定的特征(feature).
二、
三、
Mixin模式是一种在python里经常使用的模式,适当合理的应用能够达到复用代码,合理组织代码结构的目的。
Python的Mixin模式可以通过多继承
的方式来实现, 举例来说,我们自定义一个简单的具有嵌套结构的数据容器:
class SimpleItemContainer(object):
def __init__(self, id, item_containers):
self.id = id
self.data = {}
for item in item_containers:
self.data[item.id] = item
SimpleItemContainer
通过python内置类型Dict
来存放数据,不过到目前为止想要访问对应的数据还是得直接调用里面的字典,没法像原生的字典一样方便的通过暴露出来的api访问数据。当然也可以从头开始把完整的Dictionary Interface
完全实现个遍,不过在每个自定义的类似的容器中都来一套肯定不行,这时候利用python内置的UserDict.DictMixin就是一个不错的方式:
from UserDict import DictMixin
class BetterSimpleItemContainer(object, DictMixin):
def __getitem__(self, id):
return self.data[id]
def __setitem__(self, id, value):
self.data[id] = value
def __delitem__(self, id):
del self.data[id]
def keys(self):
return self.data.keys()
通过实现最小的Dictionary Interface
,还有继承DictMixin
实现Mixin模式,我们就轻松获得了完整的原生字典的行为:下表语法,get
, has_keys
, iteritems
, itervalues
甚至还有iterable protocol implementation等一系列的方法和实现。
很多框架比如Django, Django rest framework里面就普遍用到了Mixin这种模式,定义api或者viewset的时候就能够通过多重继承的方式服用一些功能
当然,Mixin模式也不能滥用,至少他会污染你新定义的类,有时候还会带来MRO的问题;不过把一些基础和单一的功能比如一般希望通过interface/protocol
实现的功能放进Mixin模块里面还是不错的选择:
class CommonEqualityMixin(object):
def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
def __ne__(self, other):
return not self.__eq__(other)
class Foo(CommonEqualityMixin):
def __init__(self, item):
self.item = item
其实整个理解下来无非就是通过组合的方式获得更多的功能,有点像C#, java里面的interface,强调“it can”的意思,但相比起来简单多了,不需要显示的约束,而且mixin模块自带实现。在使用的时候一般把mixin的类放在父类的右边似乎也是为了强调这并不是典型的多继承,是一种特殊的多继承
,而是在继承了一个基类的基础上,顺带利用多重继承的功能给这个子类添点料,增加一些其他的功能。保证Mixin的类功能单一具体,混入之后,新的类的MRO树其实也会相对很简单,并不会引起混乱。
posted on 2018-02-11 13:28 myworldworld 阅读(242) 评论(0) 收藏 举报