前言
在开发程序的过程中,尤其是在使用各个成熟的工具时,经常会接触到Mixin的概念。初见时完全不理解是什么含义,在网上查找了一些资料,总结一下,如有错误请评论指正。
什么是Mixin?
Mixin即“混入”,也可以是MixIn(和迷信区分开)或Mix-in,在笔者当前的理解来看,Mixin的根本目的有三:解耦、灵活、简单。
从Python的角度看,Mixin和多继承是密不可分的。在讲述Mixin的逻辑前,先来明确两个主体:A类 和 B类,我们假定A类为Mixin类,B类为需要使用A功能的类。
注意这里说的多继承与多重继承还有些许区别,强调广度而不是深度。
设计Mixin类的目的首先是为需要功能的类(如B类)提供功能,获得功能的方法就是继承。例如在程序设计中需要设计C类时,先考虑通过多继承来组合多个Mixin类来实现C类的功能,而不是依赖传统的多重继承。这样做有很多好处,例如使类的组织结构扁平化,抛弃了多重继承链的复杂性,以及减少子类功能冗余等等。
在Vue中同样使用了Mixin的思想,官方的说法是这样的:
Mixin(混入)提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
通俗来讲,Mixin有些像组件的组件,将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部。
不难看出,虽然在不同的语言中实现的方式略有不同,但Mixin的基本思想是一致的,Mixin类本质上是一种功能抽象。
Mixin规范
基于以上Mixin类的概念,可以发现Mixin类实际上还要遵守一些“隐性”规范,说“隐性”是因为Mixin并不是Python中的一种关键字,很多要求是语义上允许的,但当我们决定使用Mixin时,不得不去思考如何构建一个成功的Mixin模式,于是产生了这些规范。
引用一个很好的说法:
从某种程度上来说,继承强调 I am,Mixin 强调 I can。
这句话简单明了的说明了Mixin与传统继承的区别:Mixin类直接包含功能的默认实现,虽然使用继承的方式引入,但真正做到开箱即用。
笔者认为需要再次强调明确一下“继承”(指含义上的继承,并非语义上的继承)和“引入”的概念,假设有两种类:Mixin类和普通类,那么普通类继承普通类是“继承”,普通类继承Mixin类是“引入”,Mixin类继承Mixin类是“继承”,Mixin类继承普通类是不被允许的。
Mixin类需具备的特征(”隐性“要求):
1、不能单独产生实例,只能作为“功能模块”被其他可产生实例的类引入,因为Mixin类是抽象类。
2、不能继承普通类,只能继承其他Mixin类。
3、能独立实现功能,仅写出方法内容和方法所需的变量名。
4、普通类引入Mixin类时,不覆盖所继承Mixin的属性和方法,不需要调用super()去取Mixin中的方法。
5、可被同时继承的Mixin类之间没有重复的方法或属性,因此不用关心继承的顺序,强调隔离性。
之所以称上述特征为”隐性“要求,还是因为语义上对这些规范都没有明令禁止,但这些特征却实际上是Mixin奏效的灵魂所在。
举一个Mixin类的例子(取自Flask-Login):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
35
36
37
38
39
40
41
42
43
44class UserMixin:
"""
This provides default implementations for the methods
that Flask-Login expects user objects to have.
"""
# Python 3 implicitly set __hash__ to None if we override __eq__
# We set it back to its default implementation
__hash__ = object.__hash__
def is_active(self):
return True
def is_authenticated(self):
return self.is_active
def is_anonymous(self):
return False
def get_id(self):
try:
return str(self.id)
except AttributeError:
raise NotImplementedError("No `id` attribute - override `get_id`") from None
def __eq__(self, other):
"""
Checks the equality of two `UserMixin` objects using `get_id`.
"""
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
"""
Checks the inequality of two `UserMixin` objects using `get_id`.
"""
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
后记
关于Mixin,网络上也是众说纷纭,相关的内容有组合模式(Composite Pattern),Duck Typing等等,但笔者认为仅依靠上述解释已经可以初步理解 Mixin的含义 及 Mixin的使用目的。