什么是Mixin模式:带实现的协议
2017-03-05 11:11
274 查看
Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。本文将介绍Mixin的应用场景,以及关于继承、组合、多继承、接口的一些思考。
4000
前面提到,Mixin是有部分或者全部实现的接口,其主要作用是代码复用,需要理解这个简单的描述,需要先理清一些概念。
而组合代表的是其中一个类的对象是另一个类的对象的组成组合,英语来说,“has a”,比如人(People)这个类有一个属性addr是一个地址类(Address)的实例,就是说每个人都有一个地址。
不管是继承还是组合,都起到了代码复用的作用,但又各有优缺点。上面继承和组合的例子都很明显,但有些情况就不那么容易区分两类事物是继承还是组合的关系了,或者说,两个类之间既可以用继承,又可以用组合,比如设计模式中的adapter模式,既可以类适配,又可以组合适配(对象适配)。
从上图可以看到,类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,下同)可以用来实现多继承的功能,在以下情况特别合适:“基类”代表的其实是一种能力或者承诺。比如前面dog同时继承animal和runnale,但跑(run)就是一种能力的体现,这个时候就可以把runable作为一个协议,dog是可以跑的,所以应该实现run方法,也就遵循了这个协议,那么dog就是一个runable了。从例子可以看出,如果一个所谓的“基类”事实上代表了某种能力(it
can,xxxable)的时候,使用协议比多继承是更佳合理的选择。
不同的语言或者框架中,对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带来的好处,例如
在python的一些框架中,也有Mixin的身影,如tornado。
在ruby中,并不直接使用Mixin这个单词,而是使用在类的声明中include 一个module的办法,如下面的代码(来自wiki):
首先,include的module叫Comparable (Java中也有一个同名的接口),即可比较的对象,按照之前对协议、约定的讲解,是非常适合使用Mixin模式的。其次,ruby中Comparable这个module也是部分实现,需要具体的类实现<=>方法。
references:
wiki: Mixin
wiki: Duck Typing
相关概念:
4000
前面提到,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也是部分实现,需要具体的类实现<=>方法。
总结:
Mixin是一种思想,用部分实现的接口来实现代码复用。可以用来解决多继承的问题,又可以用来扩展功能。Mixin在不同的编程语言中又不同的使用形式或者命名,但其本质都是一样的。references:
wiki: Mixin
wiki: Duck Typing
相关文章推荐
- 什么是Mixin模式:带实现的协议
- 什么是Mixin模式:带实现的协议
- 什么是Mixin模式:带实现的协议
- 什么是Mixin模式:带实现的协议
- 实现采用客户/服务器通信模式,基于TCP网络通信协议的多客户端简单应用
- PHP单例模式是什么 php实现单例模式的方法
- QQ使用了什么通讯协议?为什么要这样做?为什么采用 UDP 协议,而不采用 TCP 协议实现?
- 黑马程序员_ 利用oc的协议实现代理模式
- Swift学习第十一枪-基于协议的MVVM模式的实现
- MQTT协议之使用Future模式订阅及发布(使用fusesource mqtt-client实现)
- 组播路由技术的实现过程中,主要的协议有哪些,各自的作用是什么
- PHP单例模式是什么 php实现单例模式的方法
- es6 Mixin 模式的实现
- QQ使用了什么通讯协议?为什么要这样做?为什么采用 UDP 协议,而不采用 TCP 协议实现?
- es6 使用修饰器实现Mixin模式
- 用协议实现代理模式
- NTLM挑战模式散列认证加密协议过程,算法实现与一些想法
- 2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职
- 基本上,把switch,用设计模式代替,肯定是bug和过度设计。想想,本来修改一个文件几行代码可以解决的问题,变成修改3-6个类才能实现一样的功能。不是傻是什么?
- GPU上基于SIMD的实现模式与多核CPU上基于MIMD的实现模式各有什么优缺点