一。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 設計模式》
暫無評論,快來發表你的看法吧