Brief                              

从Mix-In模式到Mixin模式,中文常用翻译为“混入/织入模式”。单纯从名字上看不到多少端倪,而通过采用Mixin模式的jQuery.extend我们是否可以认为Mixin模式就是深拷贝的代名词呢?

本文试图从继承机制入手对Mixin模式进行剖析,若有纰漏请大家指正,谢谢。

The Fact of Inheritance                      

首先让我们一起探讨一下继承机制吧。作为OOP的三个重要特性(Inheritance,Polymorphism,and Encapsulation)之一,继承应该是和Encapsulation关系最紧密。

试想一下,现在我们得到需求分析后要对问题做概念模型设计,过程大概就是从具象化->抽象化->再具象化,而在抽象化时自然而然需要提取具象的共同点得到简单直接的识别模型(如:广东人啥都吃,外国教育就是好),而再具象化时则需要构建更为明确的含义更丰富的认知模型(广东人有的吃猫肉,有的不吃),但它始终是识别模型的具象,也就始终依赖识别模型。

认知模型 多对多 识别模型,如 广东人有的会做生意,有的喜欢打工(广东人有的吃猫肉,有的不吃) 对 广东人会做生意(广东人啥都吃)

PS:认知模型、识别模型均为本人老作出来,识别模型就如文章title,略看后大概知道文章方向;认知模型如文章的content,细看才了解文章的含义。

The Diamond Problem from Multiple Inheritance      

从上文了解到认知模型可对应多个识别模型,那么自然而然就需要多继承了,而C++和Python均支持这一语言特性。

示例:

D类为B、C的派生类,A类有方法M,若C重写方法M,若现在通过D类的实例调用方法M,那么到底是调用A类中的方法实现,还是C类中的方法实现呢?这个就是著名的Diamond Problem。

本质上是,对象在同一时间拥有多条继承链,并且相同的成员签名出现在>1条继承链上,在无优先级或其他约束条件的情况下自然是找不到北的哦。

  Cons:1. 随着项目规模发展,类间继承关系愈发复杂,继承链增多,容易发生Diamond Problem。

Single Inheritance Plus Multiple Interfaces         

鉴于多继承引起的问题,Java和C#、Ruby、Scala等后来者均 采用单继承+多接口 的继承机制。

单继承,导致对象无法在同一时间拥有多条继承链,从而防止Diamond Problem的发生。

多接口,定义行为契约和状态(严格的接口仅含行为契约),不含具体的行为实现,表达like-a语义。

但问题又随之产生,在撸ASP.NET MVC时项目组一般都会在ConcreteController和Controller间加>=1层的BaseController,然后各种Logger、Authentication、Authorization和Utils均塞到BaseController里面,然后美其名为基(鸡)类(肋)。这时你会发现BaseController中的成员(方法、字段)是无机集合,要靠#region...#endregion来划分功能区间,然后随着项目规模的扩大,各种狗死垃圾都往BaseController猛塞。那为什么不用Interface来做真抽象呢?那是因为Interface只能包含方法定义,具体实现则由派生类提供。BaseController的作用却是让派生类共享有血有肉的行为能力,难道还有每个ConcreteController去实现代码完全一样的Logger吗?

Cons:1. 在需要行为能力组合的情况下显得乏力。

由于上述问题,所以我们在开发时建议 组合优于继承,但如果组合是在BaseController上实现,那跟采用#region...#endregion划分代码片段是无异的。我们希望的是在ConcreteController直接组合Logger、Authentication等横切面功能。为什么呢?因为不是所有横切面功能都被ConcreteController所需要的,加入在BaseController中则增加冗余甚至留下隐患。

Make Mixin Pattern Clear                    

由于Multiple Inheritance容易诱发Diamond Problem,而Single Inheritance Plus Multiple Interfaces则表达乏力,那么可以引入其他方式完善上述问题呢?Mixin Pattern则是其中一种。

首先找个实现了Mixin Pattern的而我们又熟悉的实例,以便好好分析学习。很自然地我把目光投到了jQuery.extend函数,$.extend(target/*, ...args*/)会将args的成员(方法+字段)都拷贝到target中,然后target就拥有args的特性,后续处理过程中外界就可以将target当做args中的某个对象来用了。(Duck Type)

好了现在我们可以提取一下Mixin Pattern的特点:

1. Roles:Mixin原料(args)、Mixin对象(target);

2. 将Mixin原料的成员(方法+字段)复制到Mixin对象中,然后Mixin对象就拥有Mixin原料的特性。

是不是这样就将Mixin Pattern描述完整了呢?当然不是啦,上面两条仅能作为初识时的印象而已。

Mixin Pattern的真实面目应该是这样的:

1. Roles:Mixin Source & Mixin Target;

2. Mixin Source将织入自身的所有成员(方法和字段)到Mixin Target;

3. Mixin Source织入的方法必须具备实现,而不仅仅是签名而已;

4. Mixin Source 和 Mixin Target相互独立。就是Mixin Target与Mixin Source互不依赖,Target即使移除Source的成员依然有效,而Source也不针对Target来设计和编码;

5. 若存在签名相同的成员,后来者覆盖前者还是保留,还是以其他规则处理都是正常的;(对象的继承链依然只有一条,因此若存在签名相同的成员,其实还是好办的^_^)

另外Mixin Pattern还细分为 对类进行Mixin(Mixin Classes)对对象进行Mixin(Mixin Objects) 两种实现形式

Mixin Class

// 引入defc.js库
/* 定义mixin source */
var mixins1 = {
name: 'fsjohnhuang',
getName: function(){return this.name}
}
var mixins2 = {
author: 'Branden Eich',
getAuthor: function(){return this.author}
} /*** 类定义时织入 ***/
var JS = defc('JS', [mixins1], {
ctor: function(){},
version: ,
getVersion: function(){return this.version}
})
// 实例化
var js = new JS()
js.getName() // 返回 fsjohnhuang
js.getVersion() // 返回1 /*** 类定义后织入 ***/
JS._mixin(mixins2 )
js.getAuthor() // 返回Branden Eich

Mixin Class对类织入字段和方法,因此会影响到所有类实例 和 继承链上的后续节点(既是其派生类)。

Mixin Object

// 引入defc.js库
/* 定义mixin source */
var mixins1 = {
name: 'fsjohnhuang',
getName: function(){return this.name}
}
var JS = defc('JS')
/*** 对Object进行Mixin ***/
var js = new JS()
defc.mixin(js, mixins1)
js.getName() //返回fsjohnhunag

Mixin Object对实例本身织入字段和方法,因此仅仅影响实例本身而已。

注意:Mixin Source实质为字段和方法的集合,而类、对象或模块等均仅仅是集合的形式而已。

上述代码片段使用的类继承实现库defc.js源码(处于实验阶段)如下:

/*!
* defc
* author: fsjohnhuang
* version: 0.1.0
* blog: fsjohnhuang.cnblogs.com
* description: define class with single inheritance, multiple mixins
* sample:
* defc('omg.JS', {
* ctor: function(version){
* this.ver = verison
* },
* author: 'Brendan Eich',
* getAuthor: function(){ return this.author }
* })
* var ES5 = defc('omg.ES5', 'omg.JS', {
* ctor: function(version){}
* })
* var mixins = [{isGreat: true, hasModule: function(){return true}}]
* var ES6 = defc('omg.ES6', ES5, mixins, {
* ctor: function(version){},
* getAuthor: function(){
* var author = zuper() // invoke method of super class which is the same signature
* return [author, 'JSers']
* }
* })
* var es6 = new ES6('2015')
* var es6_copy = new ES6('2015')
* assert.deepEquals(['Branden Eich', 'JSers'], es6.getAuthor())
* assert.equals(true, es6.isGreat)
* ES6._mixin({isGreat: false})
* assert.equals(false, es6_copy.isGreat)
*
* defc.mixin(es6, {isGreat: true})
* assert.equals(true, es6.isGreat)
* assert.equals(false, es6_copy.isGreat)
*/
;(function(factory){
var require = function(module){ return require[module] }
require.utils = {
isArray: function(obj){
return /Array/.test(Object.prototype.toString.call(obj))
},
isFn: function(obj){
return typeof obj === 'function'
},
isObj: function(obj){
return /Object/.test(Object.prototype.toString.call(obj))
},
isStr: function(obj){
return '' + obj === obj
},
noop: function(){}
} factory(require, this)
}(function(require, exports){
var VERSION = '0.1.0'
var utils = require('utils') var clazzes = {} /**
* @method defc
* @public
* @param {DOMString} clzName - the full qualified name of class, i.e. com.fsjohnhuang.Post
* @param {Function|DOMString|Array.<Object>|Object} [zuper|mixins|members] - super class, mixin classes array or members of class
* @param {Array.<Object>|Object} [mixins|members] - mixin classes array or members of class
* @param {Object} [members] - members of class. ps: property "ctor" is the contructor of class
* @returns {Object}
*/
var defc = exports.defc = function(clzName, zuper, mixins, members){
if (clazzes[clzName]) return clazzes[clzName].ctor
var args = arguments, argCount = args.length members = utils.isObj(args[argCount-]) && args[argCount-] || {}
mixins = utils.isArray(mixins) && mixins || utils.isArray(zuper) && zuper || []
zuper = utils.isFn(zuper) && zuper || utils.isStr(zuper) && clazzes[zuper] && clazzes[zuper].ctor || var clz = clazzes[clzName] = {}
var ctor = clz.ctor = function(){
// execute constructor of super class
if (zuper) zuper.apply(this, arguments)
// execute constructor
members.ctor && members.ctor.apply(this, arguments)
// contruct public fields
for(var m in members)
if(utils.isFn(this[m] = members[m])) delete this[m]
}
ctor.toString = function(){ return (members.ctor || utils.noop).toString() } // extends super class
if (zuper){
var M = function(){}
M.prototype = zuper.prototype
ctor.prototype = new M()
ctor.prototype.contructor = members.ctor || utils.noop
} // construct public methods
for(var m in members)
if(m === 'ctor' || !utils.isFn(members[m])) continue
else if(!(zuper.prototype || zuper.constructor.prototype)[m]) ctor.prototype[m] = members[m]
else (function(m){
// operate the memebers of child within the methods of super class
var _super = function(self){ return function(){ return (zuper.prototype || zuper.constructor.prototype)[m].apply(self, arguments)} }
var fnStr = members[m].toString()
, idx = fnStr.indexOf('{') +
, nFnStr = fnStr.substring(, idx) + ';var zuper = _super(this);' + fnStr.substring(idx) eval('ctor.prototype[m] = ' + nFnStr)
}(m)) // do shallow mixins
for(var mixin in mixins)
for(var m in mixins[mixin]) ctor.prototype[m] = mixins[mixin][m] // additional methods
ctor._mixin = function(/*...mixins*/){
var mixins = arguments
for(var mixin in mixins)
for(var m in mixins[mixin]) this.prototype[m] = mixins[mixin][m]
} return ctor
} /**
* @method defc.mixin
* @public
* @param {Any} obj - mixin target
* @param {...Object} mixins - mixin source
*/
defc.mixin = function(obj/*, ...mixins*/){
var mixins = Array.prototype.slice.call(arguments, )
for(var mixin in mixins)
for(var m in mixins[mixin]) obj[m] = mixins[mixin][m]
}
}))

Conclusion                          

后续我们将继续探讨C#和Java实现Mixin Pattern的方式,敬请期待,哈哈!

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4634039.html ^_^肥子John

Thanks                            

http://hax.iteye.com/blog/182339

http://cxyclub.cn/n/34324/

http://wiki.jikexueyuan.com/project/javascript-design-patterns/mixin.html

http://www.zhihu.com/question/20778853

https://en.wikipedia.org/wiki/Mixin

https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

http://codecrafter.blogspot.com/2011/03/c-mixins-with-state.html

http://codecrafter.blogspot.com/2010/02/c-quasi-mixins-example.html

http://stackoverflow.com/questions/6644668/mixins-with-c-sharp-4-0

http://www.sitepoint.com/ruby-mixins-2/

http://www.tutorialspoint.com/ruby/ruby_object_oriented.htm

http://www.ibm.com/developerworks/cn/java/j-diag1203/

http://www.linuxjournal.com/node/4540/print

Design Pattern: Not Just Mixin Pattern的更多相关文章

  1. python正则表达式基础,以及pattern.match(),re.match(),pattern.search(),re.search()方法的使用和区别

    正则表达式(regular expression)是一个特殊的字符序列,描述了一种字符串匹配的模式,可以用来检查一个字符串是否含有某种子字符串. 将匹配的子字符串替换或者从某个字符串中取出符合某个条件 ...

  2. 【java设计模式】【创建模式Creational Pattern】单例模式Singleton Pattern

    //饿汉式:资源利用率较低(无论是否需要都会创建),性能较高(使用前无需判断实例是否存在,可直接使用) public class EagerSingleton{ private static fina ...

  3. 设计模式复习小结一(Strategy Pattern/Observer Pattern/Decorator Patter/Factory Pattern)

    目录: 前言 1. Stratrgy Pattern 2. Observer Pattern 3. Decorator Pattern 4. Factory Pattern 4.1 FactoryPa ...

  4. 【java设计模式】【结构模式Structural Pattern】装饰模式Decorator Pattern

    public class Client { public static void main(String[] args) { Component component=new ConcreteCompo ...

  5. 【Pattern】-NO.150.Pattern.1 -【Pattern UML】

    Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...

  6. [转]Design Pattern Interview Questions - Part 4

    Bridge Pattern, Composite Pattern, Decorator Pattern, Facade Pattern, COR Pattern, Proxy Pattern, te ...

  7. [转]Design Pattern Interview Questions - Part 3

    State, Stratergy, Visitor Adapter and fly weight design pattern from interview perspective. (I) Can ...

  8. [转]Design Pattern Interview Questions - Part 1

    Factory, Abstract factory, prototype pattern (B) What are design patterns? (A) Can you explain facto ...

  9. Design Pattern: Observer Pattern

    1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...

随机推荐

  1. 微软Connect教程系列—VS2015集成新潮工具(四)

    本课程来源与微软connect视频教程,Modern Web Tooling in Visual Studio 2015 本课程主要讲下当下流行的前端工具 bower和grunt 首先简单介绍下这俩货 ...

  2. file /usr/share/mysql/... conflicts with file from package mysql-libs-5.1.73-3.el6_5.x86_ 64 MySQL安装

    在CentOS 6.5安装MySQL 5.6.17,安装到最后一个rpm文件MySQL-server时 安装命令是:rpm -ivh MySQL-server-5.6.17-1.el6.x86_64. ...

  3. 生产环境下实践DDD中的规约模式

    最近的开发工作涉及到两个模块“任务”和“日周报”.关系是日周报消费任务,因为用户在写日周报的时候,需要按一定的规则筛选当前用户的任务,作为日周报的一部分提交.整个项目采用类似于Orchard那种平台加 ...

  4. hadoop 笔记(hbase)

    hbase 基础: hbase是基于列的数据,其数据模式如下: 1.安装 1.1)hbase安装分为单机.伪分布式.分布式,单机下安装不依赖于hadoop:因为不需要分布式文件系统支持: 1.2)安装 ...

  5. mongodb(Index)

    备忘mongoDb 索引的一些知识点. 1.索引是用以加速数据库查询的手段,在mongo中主要是采用B树的方式: 2.每个集合最多可增加64个索引,不建议增加过多索引,原因在于一是索引本身占用空间,二 ...

  6. 系统架构:Web应用架构的新趋势---前端和后端分离的一点想法

    最近研究servlet,看书时候书里讲到了c/s架构到b/s架构的演变,讲servlet的书都很老了,现在的b/s架构已经不是几年前的b/s架构,其实b/s架构就是web应用开发,对于这样的架构我们现 ...

  7. 【面试必备】javascript操作DOM元素

    前言 时间过的真快,不知不觉就到年底了.问问自己,这一年你对自己的工作满意吗? 评价标准是什么呢?当然是马云的那两条准则了:钱给到了吗?干的爽吗?如果答案都是no,那么,你准备好跳槽了吗? 为了应对年 ...

  8. 《30天自制操作系统》笔记(03)——使用Vmware

    <30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...

  9. Move to Github

    Hi,各位,好久不见.我已经将博客移动到了 Github 上.原因是我喜欢用 Markdown 而不是 WYSIWYG 的编辑器写博客.请访问新地址: http://lxconan.github.io

  10. Field 'id' doesn't have a default value

    首先原因在于没有设置主键自增长. mysql的自增长模式是IDENTITY. jpa标签: @Id @GeneratedValue(strategy=GenerationType.IDENTITY) ...