Brief javascript
从Mix-In模式到Mixin模式,中文经常使用翻译为“混入/织入模式”。单纯从名字上看不到多少端倪,而经过采用Mixin模式的jQuery.extend咱们是否能够认为Mixin模式就是深拷贝的代名词呢? html
本文试图从继承机制入手对Mixin模式进行剖析,如有纰漏请你们指正,谢谢。java
The Fact of Inheritance node
首先让咱们一块儿探讨一下继承机制吧。做为OOP的三个重要特性(Inheritance,Polymorphism,and Encapsulation)之一,继承应该是和Encapsulation关系最紧密。linux
试想一下,如今咱们获得需求分析后要对问题作概念模型设计,过程大概就是从具象化->抽象化->再具象化,而在抽象化时天然而然须要提取具象的共同点获得简单直接的识别模型(如:广东人啥都吃,外国教育就是好),而再具象化时则须要构建更为明确的含义更丰富的认知模型(广东人有的吃猫肉,有的不吃),但它始终是识别模型的具象,也就始终依赖识别模型。es6
认知模型 多对多 识别模型,如 广东人有的会作生意,有的喜欢打工(广东人有的吃猫肉,有的不吃) 对 广东人会作生意(广东人啥都吃)ruby
PS:认知模型、识别模型均为本人老做出来,识别模型就如文章title,略看后大概知道文章方向;认知模型如文章的content,细看才了解文章的含义。app
The Diamond Problem from Multiple Inheritance ide
从上文了解到认知模型可对应多个识别模型,那么天然而然就须要多继承了,而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: 1, 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-1]) && args[argCount-1] || {} mixins = utils.isArray(mixins) && mixins || utils.isArray(zuper) && zuper || [] zuper = utils.isFn(zuper) && zuper || utils.isStr(zuper) && clazzes[zuper] && clazzes[zuper].ctor || 0 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('{') + 1 , nFnStr = fnStr.substring(0, 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, 1) 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