Mixin(织入)模式并非GOF的《设计模式》概括中的一种,可是在各类语言以及框架都会发现该模式(或者思想)的一些应用。简单来讲,Mixin是带有所有实现或者部分实现的接口,其主要做用是更好的代码复用。本文将介绍Mixin的应用场景,以及关于继承、组合、多继承、接口的一些思考。
相关概念:
前面提到,Mixin是有部分或者所有实现的接口,其主要做用是代码复用,须要理解这个简单的描述,须要先理清一些概念。
继承与组合:
继承是面向对象的三大特征(封装、继承、多态),若是类A继承自类B,那么咱们称A为子类(派生类),称B为父类(基类)。何时类A才能继承类B呢,能够说A是B的一种特殊化,英语来讲就是
A is a B,或者A is a kind of B。好比狗(dog)和动物(Animal)这两个抽象,dog is animal,这个是成立的,因此dog能够继承自animal。
而组合表明的是其中一个类的对象是另外一个类的对象的组成组合,英语来讲,“
has a”,好比人(People)这个类有一个属性addr是一个地址类(Address)的实例,就是说每一个人都有一个地址。
不论是继承仍是组合,都起到了代码复用的做用,但又各有优缺点。上面继承和组合的例子都很明显,但有些状况就不那么容易区分两类事物是继承仍是组合的关系了,或者说,两个类之间既能够用继承,又能够用组合,好比设计模式中的adapter模式,既能够类适配,又能够组合适配(对象适配)。
多继承与接口:
在使用编程语言抽象事物之间的继承关系的时候,须要考虑对多继承的实现。所谓多继承就是说一个类有多个基类,举个简单的例子,dog是animal,同时dog又是runnable(能够跑动的对象)。多继承对于人类的思惟来讲是比较正常直观的,可是对于计算机编程语言,都会遇到一个绕不过去的问题,那就是菱形继承(diamond problem),下面这个图形象展现了什么时菱形继承:
从上图能够看到,类B、类C都继承了类A,类D同时继承了类B和类C,在继承关系上就造成了
“菱形”--类D有两条路径到达类A。菱形问题带来什么问题呢,若是类A定义了某个方法foo,并且B和C都没有重写该方法,那么B和C都会有某种机制找到foo,那么对于D的实例在调用foo方法的时候,是调用到B中指向的foo方法仍是C中指向的foo方法呢?
菱形继承的问题会影响到语言的设计,一些编程语言支持多继承,如C++、python等等;另一些则不支持多继承,如Java,ruby等。对于支持多继承的语言,为了解决菱形继承的问题,通常都会使用特定的方法,好比C++中的虚继承,python中的新式类的MRO。而对于不支持多继承的语言,通常使用某种接口(或者约定、协议)来实现多继承的功能,如Java中的interface,ruby中的include module。
duck typing:
一个事物是否是鸭子(duck),若是它走起来像一只鸭子、叫起来也像一只鸭子,即从表现来看像一只鸭子,那么咱们就认为它是一只鸭子。把这种思想应用到编程中,就是duck typing,简而言之,一个约定要求必须实现某些功能,而某个类实现了这个功能,就能够把这个类当作约定的具体实现来使用。duck typing的思想在编程语言中的使用很是普遍,如Java中的Interface,Python中的各类protocol,ruby中的include module。
上面提到协议(泛指接口、预约、duck typing,下同)能够用来实现多继承的功能,在如下状况特别合适:“基类”表明的实际上是一种能力或者承诺。好比前面dog同时继承animal和runnale,但跑(run)就是一种能力的体现,这个时候就能够把runable做为一个协议,dog是能够跑的,因此应该实现run方法,也就遵循了这个协议,那么dog就是一个runable了。从例子能够看出,
若是一个所谓的“基类”事实上表明了某种能力(it can,xxxable)的时候,使用协议比多继承是更佳合理的选择。
Mixin:
在前面说清楚了各个概念以后,咱们来看看Mixin到表表明了什么,不过再次回顾上面一段提到的java interface和python protocol,这两者自己是没有任何实现的,都是须要使用者来实现相应的方法。Mixin自己也是一种能力的承诺,但
Mixin不一样的不一样之处在于Mixin是有部分或者所有实现的,在Mixin中的实现有利于代码复用。若是是部分实现,那么就是在Mixin中实现整个流程,而实现Mixin约定的类提供关键的、该类特有的方法,这有点相似模板模式,也是依赖倒置原则的体现。
不一样的语言或者框架中,对Mixin模式有不一样的实现形式,python中,除了protocol,也能够用多继承的形式来实现Mixin,为了区分普通的多继承,Mixin类的类名通常都会带上后缀:“Mixin”,好比python lib里面的两个Mixin类:UserDict.DictMixin和SocketServer.ForkingMixIn。DictMixin类包括部分实现,使用者只要实现几个核心的函数接口就好了。
Mixin defining all dictionary methods for classes that already have a minimum dictionary interface including getitem, setitem, delitem,and keys.
而python中的SocketServer.ForkingMixIn有所有的实现,因此使用者无需特殊处理,就拥有了fork带来的好处,例如
1 class ForkingUDPServer(ForkingMixIn, UDPServer): pass
2 class ForkingTCPServer(ForkingMixIn, TCPServer): pass
在python的一些框架中,也有Mixin的身影,如tornado。
在ruby中,并不直接使用Mixin这个单词,而是使用在类的声明中include 一个module的办法,以下面的代码(来自wiki):
1 class Student
2 include Comparable # The class Student inherits Comparable module using include keyword
3 attr_accessor :name, :score
4
5 def initialize(name, score)
6 @name = name
7 @score = score
8 end
9
10 # Including the Comparison module, requires the implementing class to define the <=> comparison operator
11 # Here's the comparison operator. We compare 2 student instances based on their scores.
12
13 def <=>(other)
14 @score <=> other.score
15 end
16
17 end
首先,include的module叫Comparable (Java中也有一个同名的接口),便可比较的对象,按照以前对协议、约定的讲解,是很是适合使用Mixin模式的。其次,ruby中Comparable这个module也是部分实现,须要具体的类实现<=>方法。java
总结:
Mixin是一种思想,用部分实现的接口来实现代码复用。能够用来解决多继承的问题,又能够用来扩展功能。Mixin在不一样的编程语言中又不一样的使用形式或者命名,但其本质都是同样的。
references: