function extend(subClass, superClass) { var F = function() {} F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass subClass.superclass = superClass.prototype if(superClass.prototype.constructor !== superClass) { superClass.prototype.constructor = superClass } } // var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']); /** * [AcmeComfortCruiser 自行車類] */ var AcmeComfortCruiser = function(){}; AcmeComfortCruiser.prototype = { assemble: function() {}, wash: function() {}, ride: function() {}, repair: function() {}, getPrice: function() { return 399.00 } } /** * [BicycleDecorator 裝飾類的抽象類] */ var BicycleDecorator = function(bicycle) { // Interface.ensureImplements(bicycle, Bicycle); this.bicycle = bicycle; } BicycleDecorator.prototype = { assemble: function() { return this.bicycle.assemble(); }, wash: function() { return this.bicycle.wash(); }, ride: function() { return this.bicycle.ride(); }, repair: function() { return this.bicycle.repair(); }, getPrice: function() { return this.bicycle.getPrice(); } } /** * [HeadlightDecorator 裝飾者類] */ var HeadlightDecorator = function(bicycle) { HeadlightDecorator.superclass.constructor.call(this, bicycle) } extend(HeadlightDecorator, BicycleDecorator) HeadlightDecorator.prototype.assemble = function() { return this.bicycle.assemble() + ' Attach headlight to handlebars.'; } HeadlightDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 15.00; } /** * [TaillightDecorator 裝飾者類] */ var TaillightDecorator = function(bicycle) { TaillightDecorator.superclass.constructor.call(this, bicycle) } extend(TaillightDecorator, BicycleDecorator); TaillightDecorator.prototype.assemble = function() { return this.bicycle.assemble() + ' Attach taillight to the seat post.'; } TaillightDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 9.00; } // usage var myBicycle = new AcmeComfortCruiser(); console.log(myBicycle.getPrice()) myBicycle = new HeadlightDecorator(myBicycle); console.log(myBicycle.getPrice()) myBicycle = new TaillightDecorator(myBicycle); console.log(myBicycle.getPrice())
先調用組件方法,並在其放回後實施一些附加行爲javascript
HeadlightDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 15.00; }
var FrameColorDecorator = function(bicycle, frameColor) { FrameColorDecorator.superclass.constructor.call(this, bicycle); // 添加了用以實現其提供的附加特性的屬性 this.frameColor = frameColor; } extend(FrameColorDecorator, BicycleDecorator); FrameColorDecorator.prototype.assemble = function() { // 方法添加的步驟出如今其方法以前 return 'Print the frame ' + this.frameColor + ' and allow it to dry. ' + this.bicycle.assemble() } FrameColorDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 30.00; } // usage var myBicycle = new AcmeComfortCruiser(); console.log(myBicycle.assemble()) myBicycle = new FrameColorDecorator(myBicycle, 'red') console.log(myBicycle.assemble())
引入替換組件方法的裝飾者後,必須設法確保按正確的順序應用裝飾者(如:使用工廠方法,ps:參考第 5 大點)java
/** * [LifetimeWarrantyDecorator 裝飾者類] */ var LifetimeWarrantyDecorator = function(bicycle) { LifetimeWarrantyDecorator.superclass.constructor.call(this, bicycle); } extend(LifetimeWarrantyDecorator, BicycleDecorator); // 把原來的 repair 方法替換爲一個新方法,而組件的方法則不會再被調用 LifetimeWarrantyDecorator.prototype.repair = function() { return 'This bicycle is covered by a lifetime warranty.' } LifetimeWarrantyDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 199.00 } /** * [TimedWarrantyDecorator 裝飾者類] */ var TimedWarrantyDecorator = function(bicycle, coverageLengthInYears) { TimedWarrantyDecorator.superclass.constructor.call(this, bicycle); this.coverageLength = coverageLengthInYears; this.expDate = new Date(); var coverageLengthInMs = this.coverageLength * 365 * 24 * 60 * 60 * 1000; this.expDate.setTime(this.expDate.getTime() + coverageLengthInMs) } extend(TimedWarrantyDecorator, BicycleDecorator); // 根據某種條件決定是否替代組件方法,在條件知足是替代,不然使用組件的方法 TimedWarrantyDecorator.prototype.repair = function() { var repairInstructions; var currentDate = new Date(); if(currentDate < this.expDate) { repairInstructions = 'This bicycle is currently covered by a warrenty.' }else { repairInstructions = this.bicycle.repair(); } return repairInstructions; } TimedWarrantyDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + (40.00 * this.coverageLength) } // usage var myBicycle = new AcmeComfortCruiser(); console.log(myBicycle.getPrice()) // 替代 myBicycle = new LifetimeWarrantyDecorator(myBicycle) console.log(myBicycle.repair()) // 判斷是否替代 myBicycle = new TimedWarrantyDecorator(myBicycle, 1) console.log(myBicycle.getPrice())
想穩妥地實現這一點並不容易,想要使用這些新方法,外圍代碼必須知道有這些新方法。因爲這些新方法並非在接口中定義的,而是動態添加的,所以有必要進行類型檢查,以驗明用於包裝組件對象的最外層裝飾者與用新方法裝飾的組件對象相同架構
var BellDecorator = function(bicycle) { BellDecorator.superclass.constructor.call(this, bicycle); } extend(BellDecorator, BicycleDecorator); BellDecorator.prototype.assemble = function() { return this.bicycle.assemble() + 'Attach bell to handlebars.' } BellDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 6.00; } // 這裏添加了一個新方法 BellDecorator.prototype.ringBell = function() { return 'Bell rung' } // usage var myBicycle = new AcmeComfortCruiser(); myBicycle = new BellDecorator(myBicycle) myBicycle = new HeadlightDecorator(myBicycle); console.log(myBicycle.ringBell()) // 這樣子會報錯,由於 BellDecorator 添加的 ringBell 方法(及其餘方法)會在 HeadlightDecorator 類經過 extend() 繼承 new F() 時被抹除(也不是被抹除,只是不能在經過當前對象的原型鏈找到,其實這個方法在新對象的 bicycle 屬性裏面仍是能經過其原型鏈找到)。 // BellDecorator 必須放在最後應用,不然這個新方法將沒法訪問。這是由於其餘裝飾者只能傳遞他們知道的方法,也即那些定義在接口中的方法。P170
關於上面說到的方法被抹除問題的解決方案app
function extend(subClass, superClass) { var F = function() {} F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass subClass.superclass = superClass.prototype if(superClass.prototype.constructor !== superClass) { superClass.prototype.constructor = superClass } } var Interface = function(name, methods) { if(arguments.length !== 2) { throw new Error("Interface constructor called with "+ arguments.length + "arguments, but expected exactly 2") } this.name = name; this.methods = []; for(var i=0, len=methods.length; i<len; i++) { if(typeof methods[i] !== 'string') { throw new Error("Interface constructor expects method names to be passed in as a string") } this.methods.push(methods[i]); } } var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']); var BicycleDecorator = function(bicycle) { this.bicycle = bicycle; this.interface = Bicycle; outerloop: // 使用標記,能夠在程序的任何地方使用這個名字來引用他 for(var key in this.bicycle) { if(typeof this.bicycle[key] !== 'function') { continue outerloop; } for(var i=0, len=this.interface.methods.length; i<len; i++) { if(key === this.interface.methods[i]) { continue outerloop } } var that = this; (function(methodName) { that[methodName] = function() { return that.bicycle[methodName](); } })(key); } } BicycleDecorator.prototype = { assemble: function() { return this.bicycle.assemble(); }, wash: function() { return this.bicycle.wash(); }, ride: function() { return this.bicycle.ride(); }, repair: function() { return this.bicycle.repair(); }, getPrice: function() { return this.bicycle.getPrice(); } } /** * [AcmeComfortCruiser 自行車類] */ var AcmeComfortCruiser = function(){}; AcmeComfortCruiser.prototype = { assemble: function() { return 'assemble:' }, wash: function() {}, ride: function() {}, repair: function() {}, getPrice: function() { return 399.00 } } /** * [HeadlightDecorator 裝飾者類] */ var HeadlightDecorator = function(bicycle) { HeadlightDecorator.superclass.constructor.call(this, bicycle) } extend(HeadlightDecorator, BicycleDecorator) HeadlightDecorator.prototype.assemble = function() { return this.bicycle.assemble() + ' Attach headlight to handlebars.'; } HeadlightDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 15.00; } /** * [BellDecorator 裝飾者類] */ var BellDecorator = function(bicycle) { BellDecorator.superclass.constructor.call(this, bicycle); } extend(BellDecorator, BicycleDecorator); BellDecorator.prototype.assemble = function() { return this.bicycle.assemble() + 'Attach bell to handlebars.' } BellDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 6.00; } BellDecorator.prototype.ringBell = function() { return 'Bell rung' } var myBicycle = new AcmeComfortCruiser(); console.log(myBicycle.getPrice()) myBicycle = new BellDecorator(myBicycle) myBicycle = new HeadlightDecorator(myBicycle); console.log(myBicycle.getPrice()) console.log(myBicycle.ringBell())
能夠使用工廠模式配合裝飾者模式,這樣就能夠事先規定好實例化時的裝飾者應用順序,從而避免上面說到的新添加的方法在通過別的裝飾類包裝後訪問不到添加的方法的問題ide
function extend(subClass, superClass) { var F = function() {} F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass subClass.superclass = superClass.prototype if(superClass.prototype.constructor !== superClass) { superClass.prototype.constructor = superClass } } var Interface = function(name, methods) { if(arguments.length !== 2) { throw new Error("Interface constructor called with "+ arguments.length + "arguments, but expected exactly 2") } this.name = name; this.methods = []; for(var i=0, len=methods.length; i<len; i++) { if(typeof methods[i] !== 'string') { throw new Error("Interface constructor expects method names to be passed in as a string") } this.methods.push(methods[i]); } } Interface.ensureImplements = function(object) { if(arguments.length < 2) { throw new Error("Function Interface.ensureImplements call with " + arguments.length + "arguments, but expected at least 2") } for(var i=1,len=arguments.length; i<len; i++) { var interface = arguments[i]; if(interface.constructor !== Interface) { throw new Error("Function Interface.ensureImplements expects arguments two and above to be instances of Interface"); } for(var j=0, methodsLen = interface.methods.length; j<methodsLen; j++) { var method = interface.methods[j]; if(!object[method] || typeof object[method] !== 'function') { throw new Error('Function Interface.ensureImplements: Object does not implement the '+ interface.name + " interface. Method " + method + " was not found") } } } } var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']); // model 1 var AcmeComfortCruiser = function(){}; AcmeComfortCruiser.prototype = { assemble: function() {}, wash: function() {}, ride: function() {}, repair: function() {}, getPrice: function() { return 399.00 } } // model 2 var AcmeSpeedster = function() {} extend(AcmeSpeedster, AcmeComfortCruiser) /** * [BicycleDecorator 裝飾類的抽象類] */ var BicycleDecorator = function(bicycle) { // Interface.ensureImplements(bicycle, Bicycle); this.bicycle = bicycle; } BicycleDecorator.prototype = { assemble: function() { return this.bicycle.assemble(); }, wash: function() { return this.bicycle.wash(); }, ride: function() { return this.bicycle.ride(); }, repair: function() { return this.bicycle.repair(); }, getPrice: function() { return this.bicycle.getPrice(); } } /** * [HeadlightDecorator 裝飾者類] */ var HeadlightDecorator = function(bicycle) { HeadlightDecorator.superclass.constructor.call(this, bicycle) } extend(HeadlightDecorator, BicycleDecorator) HeadlightDecorator.prototype.assemble = function() { return this.bicycle.assemble() + ' Attach headlight to handlebars.'; } HeadlightDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 15.00; } /** * [BellDecorator 裝飾者類] */ var BellDecorator = function(bicycle) { BellDecorator.superclass.constructor.call(this, bicycle); } extend(BellDecorator, BicycleDecorator); BellDecorator.prototype.assemble = function() { return this.bicycle.assemble() + 'Attach bell to handlebars.' } BellDecorator.prototype.getPrice = function() { return this.bicycle.getPrice() + 6.00; } BellDecorator.prototype.ringBell = function() { return 'Bell rung' } // BicycleShop class 是一個抽象類,須要在繼承後實現裏面的方法 var BicycleShop = function() {}; BicycleShop.prototype = { sellBicycle: function(model) { var bicycle = this.createBicycle(model) bicycle.assemble() return bicycle; }, // 工廠方法 createBicycle: function(model) { throw new Error('Unsupported operation on an abstract class.') } } var AcmeBicycleShop = function() {}; extend(AcmeBicycleShop, BicycleShop); AcmeBicycleShop.prototype.createBicycle = function(model, options) { var bicycle = new AcmeBicycleShop.models[model](); // 有必要時能夠在這裏對裝飾者組件前後應用進行排序,下面使用的是直接遍歷按順序應用 for(var i=0, len= options.length; i<len; i++) { var decorator = AcmeBicycleShop.options[options[i].name] if(typeof decorator !== 'function') { throw new Error('Decorator ' + options[i].name + 'not found'); } var argument = options[i].arg; bicycle = new decorator(bicycle, argument) } Interface.ensureImplements(bicycle, Bicycle); return bicycle } AcmeBicycleShop.models = { 'The Speedster' : AcmeSpeedster, 'The Comfort Cruiser' : AcmeComfortCruiser } AcmeBicycleShop.options = { 'headlight' : HeadlightDecorator, 'bell': BellDecorator } var alecsCruisers = new AcmeBicycleShop(); var myBicycle = alecsCruisers.createBicycle('The Speedster', [ {name: 'headlight'}, {name: 'bell'} ]) myBicycle.ringBell()
用於包裝獨立的函數和方法的裝飾者函數
// 將傳入函數的執行結果轉化爲大寫形式 function upperCaseDecorator(func) { return function() { return func.apply(this, arguments).toUpperCase() } } function getDate() { return (new Date()).toString() } getDateCaps = upperCaseDecorator(getDate) // usage getDateCaps()
function upperCaseDecorator(func) { return function() { return func.apply(this, arguments).toUpperCase() } } function extend(subClass, superClass) { var F = function() {} F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass subClass.superclass = superClass.prototype if(superClass.prototype.constructor !== superClass) { superClass.prototype.constructor = superClass } } /** * [AcmeComfortCruiser 自行車類] */ var AcmeComfortCruiser = function(){}; AcmeComfortCruiser.prototype = { assemble: function() {}, wash: function() {}, ride: function() {}, repair: function() {}, getPrice: function() { return 399.00 } } /** * [BicycleDecorator 裝飾類的抽象類] */ var BicycleDecorator = function(bicycle) { this.bicycle = bicycle; } BicycleDecorator.prototype = { assemble: function() { return this.bicycle.assemble(); }, wash: function() { return this.bicycle.wash(); }, ride: function() { return this.bicycle.ride(); }, repair: function() { return this.bicycle.repair(); }, getPrice: function() { return this.bicycle.getPrice(); } } /** * [BellDecorator 裝飾者類] */ var BellDecorator = function(bicycle) { BellDecorator.superclass.constructor.call(this, bicycle); } extend(BellDecorator, BicycleDecorator); BellDecorator.prototype.ringBell = function() { return 'Bell rung' } // 使用函數裝飾者裝飾方法 BellDecorator.prototype.ringBellLoudly = upperCaseDecorator(BellDecorator.prototype.ringBell) var myBicycle = new AcmeComfortCruiser(); myBicycle = new BellDecorator(myBicycle) myBicycle.ringBell() myBicycle.ringBellLoudly()
/** * [ListBuilder] * @param {[type]} parent [description] */ var ListBuilder = function(parent) { this.parentEl = document.getElementById(parent); } ListBuilder.prototype = { buildList: function(listLength) { var list = document.createElement('ol'); this.parentEl.appendChild(list); for(var i=0; i< listLength; i++) { var item = document.createElement('li'); list.appendChild(item) } } } /** * [MethodProfiler class] * @param {[type]} component [description] */ var MethodProfiler = function(component) { this.component = component; this.timers = {}; for(var key in this.component) { if(typeof this.component[key] !== 'function') { continue; } var that = this; // 使用匿名函數的做用是保留正確的 methodName 變量值 (function(methodName) { that[methodName] = function() { that.startTimer(methodName); var returnValue = that.component[methodName].apply(that.component, arguments); that.displayTime(methodName, that.getElapsedTime(methodName)); return returnValue; } })(key) } } MethodProfiler.prototype = { startTimer: function(methodName) { this.timers[methodName] = (new Date()).getTime(); }, getElapsedTime: function(methodName) { return (new Date()).getTime() - this.timers[methodName]; }, displayTime: function(methodName, time) { console.log(methodName + ': ' + time + ' ms') } } // usage var list = new ListBuilder('feed-readers') var listp = new MethodProfiler(list) listp.buildList(500)
注意oop
轉載、引用,但請標明做者和原文地址post