一.Mixin模式与代码复用
最常用的代码复用方式是继承,子类会获得父类的所有属性,所以可能会继承到多余的属性。一种解决方案是把父类的属性拆分到多个接口类中,子类从这些接口类中“自取”需要继承的属性即可,但Java中的接口是完全抽象的(不包含实现),无法实现代码复用。采用组合能缓解这个问题,但不能从根本上解决它,组合需要持有各个依赖对象的引用,在此基础上再次封装并提供接口,增加了复杂性
如果只想复用现有对象的一些属性,Mixin模式无疑是最好的选择,因为完全不需要牵扯复杂的类层次关系。在现有继承机制上,我们希望能够实现部分继承(只从父类获取需要的一部分属性)与多重继承(可以从多个父类获得属性),Mixin模式就做了这个事情
二.Mixin模式的实现
Mixin模式的实现其实就是一种属性复制,示例代码如下:
// 零件仓库1
function Moveable() {}
Moveable.prototype.walk = function() {
console.log('walked slowly');
}
Moveable.prototype.run = function() {
console.log('ran quickly');
}
Moveable.prototype.jump = function() {
// ...
}
// 零件仓库2
function Souled() {}
Souled.prototype.smile = function(age) {
console.log('smiled as ' + age + ' year\'s old kid');
}
// 零件仓库3
// mixin
function augment(sub, sup) {
// 继承所有属性
if (arguments.length === 2) {
for(var attr in sup.prototype) {
sub.prototype[attr] = sup.prototype[attr];
}
}
// 继承部分属性
else if(arguments.length > 2) {
for (var i = 2; i < arguments.length; i++) {
sub.prototype[arguments[i]] = sup.prototype[arguments[i]];
}
}
else {
// do nothing
}
}
// 需要增强的类
function Robot(name) {
this.name = name;
}
// 增强
augment(Robot, Moveable, 'walk', 'run'); // 从Moveable继承walk和run
augment(Robot, Souled); // 继承Souled的全部属性
// test
var robot = new Robot('little boy');
robot.walk(); // walked slowly
robot.run(); // ran quickly
robot.smile(12); // smiled as 12 year's old kid
很容易就实现了部分继承和多重继承,解除了经典继承机制的束缚,使得代码复用更加灵活
一些JS库提供了Mixin的实现,例如Underscore
的_.extend
方法、JQuery
的extend
方法、YUI的mix/augment/extend/merge
方法等等
三.Mixin模式的缺点
Mixin是一种很灵活的代码复用方式,但把功能注入原型对象会导致原型污染和函数来源方面的不确定性,在大型系统中可能是一个严重的问题。可以通过详细的文档来解决函数来源不确定的问题,但原型污染是不可避免的
参考资料
《JavaScript设计模式》
哈哈