Mixin 模式
在諸如C++或者List著這樣的傳統(tǒng)語言中,織入模式就是一些提供能夠被一個或者一組子類簡單繼承功能的類,意在重用其功能。
子類劃分
對于不熟悉子類劃分的開發(fā)者,在深入織入模式和裝飾器模式之前,我們將對他們進行一個簡短的初學者入門指引。
子類劃分是一個參考了為一個新對象繼承來自一個基類或者超類對象的屬性的術(shù)語.在傳統(tǒng)的面向?qū)ο缶幊讨?類B能夠從另外一個類A處擴展.這里我們將A看做是超類,而將B看做是A的子類.如此,所有B的實體都從A處繼承了其A的方法.然而B仍然能夠定義它自己的方法,包括那些重載的原本在A中的定義的方法毫蚓。
B是否應(yīng)該調(diào)用已經(jīng)被重載的A中的方法,我們將這個引述為方法鏈.B是否應(yīng)該調(diào)用A(超類)的構(gòu)造器,我們將這稱為構(gòu)造器鏈。
為了演示子類劃分,首先我們需要一個能夠創(chuàng)建自身新實體的基對象昔善。
var Person = function( firstName , lastName ){
this.firstName = firstName;
this.lastName = lastName;
this.gender = "male";
};
接下來,我們將制定一個新的類(對象),它是一個現(xiàn)有的Person對象的子類.讓我們想象我們想要加入一個不同屬性用來分辨一個Person和一個繼承了Person"超類"屬性的Superhero.由于超級英雄分享了一般人類許多共有的特征(例如:name,gender),因此這應(yīng)該很有希望充分展示出子類劃分是如何工作的元潘。
// a new instance of Person can then easily be created as follows:
var clark = new Person( "Clark" , "Kent" );
// Define a subclass constructor for for "Superhero":
var Superhero = function( firstName, lastName , powers ){
// Invoke the superclass constructor on the new object
// then use .call() to invoke the constructor as a method of
// the object to be initialized.
Person.call( this, firstName, lastName );
// Finally, store their powers, a new array of traits not found in a normal "Person"
this.powers = powers;
};
SuperHero.prototype = Object.create( Person.prototype );
var superman = new Superhero( "Clark" ,"Kent" , ["flight","heat-vision"] );
console.log( superman );
// Outputs Person attributes as well as powers
Superhero構(gòu)造器創(chuàng)建了一個自Peroson下降的對象。這種類型的對象擁有鏈中位于它之上的對象的屬性,而且如果我們在Person對象中設(shè)置了默認的值,Superhero能夠使用特定于它的對象的值覆蓋任何繼承的值君仆。
Mixin(織入目標類)
在Javascript中,我們會將從Mixin繼承看作是通過擴展收集功能的一種途徑.我們定義的每一個新的對象都有一個原型,從其中它可以繼承更多的屬性.原型可以從其他對象繼承而來,但是更重要的是,能夠為任意數(shù)量的對象定義屬性.我們可以利用這一事實來促進功能重用翩概。
Mix允許對象以最小量的復(fù)雜性從它們那里借用(或者說繼承)功能.作為一種利用Javascript對象原型工作得很好的模式,它為我們提供了從不止一個Mix處分享功能的相當靈活,但比多繼承有效得多得多的方式牲距。
它們可以被看做是其屬性和方法可以很容易的在其它大量對象原型共享的對象.想象一下我們定義了一個在一個標準對象字面量中含有實用功能的Mixin,如下所示:
var myMixins = {
moveUp: function(){
console.log( "move up" );
},
moveDown: function(){
console.log( "move down" );
},
stop: function(){
console.log( "stop! in the name of love!" );
}
};
然后我們可以方便的擴展現(xiàn)有構(gòu)造器功能的原型,使其包含這種使用一個 如下面的score.js_.extends()方法輔助器的行為:
// A skeleton carAnimator constructor
function carAnimator(){
this.moveLeft = function(){
console.log( "move left" );
};
}
// A skeleton personAnimator constructor
function personAnimator(){
this.moveRandomly = function(){ /*..*/ };
}
// Extend both constructors with our Mixin
_.extend( carAnimator.prototype, myMixins );
_.extend( personAnimator.prototype, myMixins );
// Create a new instance of carAnimator
var myAnimator = new carAnimator();
myAnimator.moveLeft();
myAnimator.moveDown();
myAnimator.stop();
// Outputs:
// move left
// move down
// stop! in the name of love!
如我們所見,這允許我們將通用的行為輕易的"混"入相當普通對象構(gòu)造器中。
在接下來的示例中,我們有兩個構(gòu)造器:一個Car和一個Mixin.我們將要做的是靜Car參數(shù)化(另外一種說法是擴展),以便它能夠繼承Mixin中的特定方法,名叫driveForwar()和driveBackward().這一次我們不會使用Underscore.js钥庇。
取而代之牍鞠,這個示例將演示如何將一個構(gòu)造器參數(shù)化,以便在無需重復(fù)每一個構(gòu)造器函數(shù)過程的前提下包含其功能上沐。
// Define a simple Car constructor
var Car = function ( settings ) {
this.model = settings.model || "no model provided";
this.color = settings.color || "no colour provided";
};
// Mixin
var Mixin = function () {};
Mixin.prototype = {
driveForward: function () {
console.log( "drive forward" );
},
driveBackward: function () {
console.log( "drive backward" );
},
driveSideways: function () {
console.log( "drive sideways" );
}
};
// Extend an existing object with a method from another
function augment( receivingClass, givingClass ) {
// only provide certain methods
if ( arguments[2] ) {
for ( var i = 2, len = arguments.length; i < len; i++ ) {
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
}
}
// provide all methods
else {
for ( var methodName in givingClass.prototype ) {
// check to make sure the receiving class doesn't
// have a method of the same name as the one currently
// being processed
if ( !Object.hasOwnProperty(receivingClass.prototype, methodName) ) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
// Alternatively:
// if ( !receivingClass.prototype[methodName] ) {
// receivingClass.prototype[methodName] = givingClass.prototype[methodName];
// }
}
}
}
// Augment the Car constructor to include "driveForward" and "driveBackward"
augment( Car, Mixin, "driveForward", "driveBackward" );
// Create a new Car
var myCar = new Car({
model: "Ford Escort",
color: "blue"
});
// Test to make sure we now have access to the methods
myCar.driveForward();
myCar.driveBackward();
// Outputs:
// drive forward
// drive backward
// We can also augment Car to include all functions from our mixin
// by not explicitly listing a selection of them
augment( Car, Mixin );
var mySportsCar = new Car({
model: "Porsche",
color: "red"
});
mySportsCar.driveSideways();
// Outputs:
// drive sideways
優(yōu)點&缺點
Mixin支持在一個系統(tǒng)中降解功能的重復(fù)性,增加功能的重用性.在一些應(yīng)用程序也許需要在所有的對象實體共享行為的地方,我們能夠通過在一個Mixin中維護這個共享的功能,來很容易的避免任何重復(fù),而因此專注于只實現(xiàn)我們系統(tǒng)中真正彼此不同的功能。
也就是說,對Mixin的副作用是值得商榷的.一些開發(fā)者感覺將功能注入到對象的原型中是一個壞點子,因為它會同時導(dǎo)致原型污染和一定程度上的對我們原有功能的不確定性.在大型的系統(tǒng)中,很可能是有這種情況的楞艾。
我認為,強大的文檔對最大限度的減少對待功能中的混入源的迷惑是有幫助的,而且對于每一種模式而言,如果在實現(xiàn)過程中小心行事,我們應(yīng)該是沒多大問題的参咙。