在諸如C++或者List著這樣的傳統(tǒng)語(yǔ)言中,織入模式就是一些提供能夠被一個(gè)或者一組子類簡(jiǎn)單繼承功能的類,意在重用其功能。
對(duì)于不熟悉子類劃分的開發(fā)者,在深入織入模式和裝飾器模式之前,我們將對(duì)他們進(jìn)行一個(gè)簡(jiǎn)短的初學(xué)者入門指引。
子類劃分是一個(gè)參考了為一個(gè)新對(duì)象繼承來(lái)自一個(gè)基類或者超類對(duì)象的屬性的術(shù)語(yǔ).在傳統(tǒng)的面向?qū)ο缶幊讨?類B能夠從另外一個(gè)類A處擴(kuò)展.這里我們將A看做是超類,而將B看做是A的子類.如此,所有B的實(shí)體都從A處繼承了其A的方法.然而B仍然能夠定義它自己的方法,包括那些重載的原本在A中的定義的方法。
B是否應(yīng)該調(diào)用已經(jīng)被重載的A中的方法,我們將這個(gè)引述為方法鏈.B是否應(yīng)該調(diào)用A(超類)的構(gòu)造器,我們將這稱為構(gòu)造器鏈。
為了演示子類劃分,首先我們需要一個(gè)能夠創(chuàng)建自身新實(shí)體的基對(duì)象。
var Person = function( firstName , lastName ){
this.firstName = firstName;
this.lastName = lastName;
this.gender = "male";
};
接下來(lái),我們將制定一個(gè)新的類(對(duì)象),它是一個(gè)現(xiàn)有的Person對(duì)象的子類.讓我們想象我們想要加入一個(gè)不同屬性用來(lái)分辨一個(gè)Person和一個(gè)繼承了Person"超類"屬性的Superhero.由于超級(jí)英雄分享了一般人類許多共有的特征(例如: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)建了一個(gè)自Peroson下降的對(duì)象。這種類型的對(duì)象擁有鏈中位于它之上的對(duì)象的屬性,而且如果我們?cè)赑erson對(duì)象中設(shè)置了默認(rèn)的值,Superhero能夠使用特定于它的對(duì)象的值覆蓋任何繼承的值。
在Javascript中,我們會(huì)將從Mixin繼承看作是通過(guò)擴(kuò)展收集功能的一種途徑.我們定義的每一個(gè)新的對(duì)象都有一個(gè)原型,從其中它可以繼承更多的屬性.原型可以從其他對(duì)象繼承而來(lái),但是更重要的是,能夠?yàn)槿我鈹?shù)量的對(duì)象定義屬性.我們可以利用這一事實(shí)來(lái)促進(jìn)功能重用。
Mix允許對(duì)象以最小量的復(fù)雜性從它們那里借用(或者說(shuō)繼承)功能.作為一種利用Javascript對(duì)象原型工作得很好的模式,它為我們提供了從不止一個(gè)Mix處分享功能的相當(dāng)靈活,但比多繼承有效得多得多的方式。
它們可以被看做是其屬性和方法可以很容易的在其它大量對(duì)象原型共享的對(duì)象.想象一下我們定義了一個(gè)在一個(gè)標(biāo)準(zhǔn)對(duì)象字面量中含有實(shí)用功能的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!" );
}
};
然后我們可以方便的擴(kuò)展現(xiàn)有構(gòu)造器功能的原型,使其包含這種使用一個(gè) 如下面的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!
如我們所見,這允許我們將通用的行為輕易的"混"入相當(dāng)普通對(duì)象構(gòu)造器中。
在接下來(lái)的示例中,我們有兩個(gè)構(gòu)造器:一個(gè)Car和一個(gè)Mixin.我們將要做的是靜Car參數(shù)化(另外一種說(shuō)法是擴(kuò)展),以便它能夠繼承Mixin中的特定方法,名叫driveForwar()和driveBackward().這一次我們不會(huì)使用Underscore.js。
取而代之,這個(gè)示例將演示如何將一個(gè)構(gòu)造器參數(shù)化,以便在無(wú)需重復(fù)每一個(gè)構(gòu)造器函數(shù)過(guò)程的前提下包含其功能。
// 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
Mixin支持在一個(gè)系統(tǒng)中降解功能的重復(fù)性,增加功能的重用性.在一些應(yīng)用程序也許需要在所有的對(duì)象實(shí)體共享行為的地方,我們能夠通過(guò)在一個(gè)Mixin中維護(hù)這個(gè)共享的功能,來(lái)很容易的避免任何重復(fù),而因此專注于只實(shí)現(xiàn)我們系統(tǒng)中真正彼此不同的功能。
也就是說(shuō),對(duì)Mixin的副作用是值得商榷的.一些開發(fā)者感覺(jué)將功能注入到對(duì)象的原型中是一個(gè)壞點(diǎn)子,因?yàn)樗鼤?huì)同時(shí)導(dǎo)致原型污染和一定程度上的對(duì)我們?cè)泄δ艿牟淮_定性.在大型的系統(tǒng)中,很可能是有這種情況的。
我認(rèn)為,強(qiáng)大的文檔對(duì)最大限度的減少對(duì)待功能中的混入源的迷惑是有幫助的,而且對(duì)于每一種模式而言,如果在實(shí)現(xiàn)過(guò)程中小心行事,我們應(yīng)該是沒(méi)多大問(wèn)題的。
更多建議: