Mixin模式

了解Mixin模式

In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin’s methods depends on the language. Mixins are sometimes described as being “included” rather than “inherited”.

在面向对象程序语言中,mixin是一个类,这个类包含了一些其他类需要用的方法,但又不一定非要成为其他类的父类。
其他类如何获取mixin的方法取决于语言实现,Mixins有时候别成为”包含”而不是”继承”。

Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the “diamond problem”), or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods.

Mixins鼓励代码复用,可以避免含糊的多重继承,或者可以用于不支持多重继承的语言。mixin可以被视为接口已经实现的方法。

历史

Mixins first appeared in the Symbolics’ object-oriented Flavors system (developed by Howard Cannon), which was an approach to object-orientation used in Lisp Machine Lisp. The name was inspired by Steve’s Ice Cream Parlor in Somerville, Massachusetts. The owner of the ice cream shop offered a basic flavor of ice cream (vanilla, chocolate, etc.) and blended in a combination of extra items (nuts, cookies, fudge, etc.) and called the item a “mix-in”, his own trademarked term at the time.

Mixins第一次出现在Symbolics的面向对象口味系统中,是一个在lisp机器中的近似面向对象的系统。名字是受了
Steve的冰淇淋店的启发。冰淇淋的店主售卖一个基本口味的冰淇淋,混搭坚果,饼干,软糖什么的,这个物品被称为mix-in。

定义

Mixins are a language concept that allows a programmer to inject some code into a class. Mixin programming is a style of software development, in which units of functionality are created in a class and then mixed in with other classes.

Mixins是一个语言的概念,允许程序员将代码注入到一个类中。Mixin编程是一种软件开发模式,将单元内被创建的函数混搭到
其他类中。

A mixin class acts as the parent class, containing the desired functionality. A subclass can then inherit or simply reuse this functionality, but not as a means of specialization. Typically, the mixin will export the desired functionality to a child class, without creating a rigid, single “is a” relationship. Here lies the important difference between the concepts of mixins and inheritance, in that the child class can still inherit all the features of the parent class, but, the semantics about the child “being a kind of” the parent need not be necessarily applied.

一个mixin表现和父类差不多,包括需要的功能。子类可以继承或者简单复用功能,但是不一定要特别化。
通常,mixin会将功能输出给一个子类,不必建立一个死板的”is a”关系。这里说明mixins和继承的重大不同–
子类依旧可以继承父类的所有属性,但是,语义上,子类不一定要成为父类的类型。

好处

1.It provides a mechanism for multiple inheritance by allowing multiple classes to use the common functionality, but without the complex semantics of multiple inheritance.

2.Code reusability: Mixins are useful when a programmer wants to share functionality between different classes. Instead of repeating the same code over and over again, the common functionality can simply be grouped into a mixin and then inherited into each class that requires it.

3.Mixins allows inheritance and use of only the desired features from the parent class, not necessarily all of the features from the parent class.

1.不需要语义上的多重继承;
2.代码复用性好;
3.可以隐藏父类的方法,可以不全部暴露给子类。

Mixins only exist in multiple-inheritance languages.
Mixins只存在于多重继承的语言之中。

Python Mixin举例
使用继承:

实例一,单一父类继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ComparableMixin(object):
"""This class has methods which use `<=` and `==`,
but this class does NOT implement those methods."""
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self <= other and (self != other)
def __gt__(self, other):
return not self <= other
def __ge__(self, other):
return self == other or self > other
class Integer(ComparableMixin):
def __init__(self, i):
self.i = i
def __le__(self, other):
return self.i <= other.i
def __eq__(self, other):
return self.i == other.i
assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)
# It is possible to instantiate a mixin:
o = ComparableMixin()

这个实例也能使用functools的total_ordering方法

实例二,多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class HasMethod1(object):
def method(self):
return 1
class HasMethod2(object):
def method(self):
return 2
class UsesMethod10(object):
def usesMethod(self):
return self.method() + 10
class UsesMethod20(object):
def usesMethod(self):
return self.method() + 20
class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass
assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22
# Nothing prevents implementing the method
# on the base class like in Definition 1:
class C3_10(UsesMethod10):
def method(self):
return 3
assert C3_10().usesMethod() == 13

Python Mixin原理

有两个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> class foo:
... pass
...
>>> foo.__bases__
()
>>> class foobase:
... def hello(self):
... print "hello"
...
>>> obj=foo()
>>> obj.hello()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo instance has no attribute 'hello'
>>> foo.__bases__ +=(foobase,)
>>> foo.__bases__
(<class __main__.foobase at 0x10153de88>,)
>>> obj.hello()
hello

原理是,每个类都有一个bases属性,它是一个tuple,用来存放所有的基类。而且在运行中,可以动态改变。所以当我们向其中增加新的基类时,再次调用原来不存在的函数,由于基类中的函数已经存在了,所以这次成功了。

在使用Mix-in技术时,如果原来的类中存在与Mix类中同名的函数,那么Mix类中的函数不会运行。使用getattr()和setattr()函数可以进行函数替换。

注意事项:

1.Mixin必须责任单一,如果有多个功能,那就写多个Mixin类;
2.Mixin不依赖于子类的实现;
3.子类即便没有继承这个Mixin类,也照样可以工作;
4.多重继承时候,Mixin类应该在基本的父类之前。

关于第四点,可以进行如下实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BaseClass(object):
pass
class Mixin1(object):
def test(self):
print "Mixin1"
class Mixin2(object):
def test(self):
print "Mixin2"
class MyClass(BaseClass, Mixin1, Mixin2):
pass
obj = MyClass()
obj.test() # print out Mixin1, beuacuse in Python the class hierarchy is defined right to left
class MyClass(Mixin2, Mixin1, BaseClass):
pass
obj = MyClass()
obj.test() # print out Mixin2

参考:
https://en.wikipedia.org/wiki/Mixin
http://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful
http://wiki.woodpecker.org.cn/moin/IntroMixin
https://www.ianlewis.org/en/mixins-and-python