歡迎關注個人公衆號睿Talk
,獲取我最新的文章:javascript
所謂裝飾者模式,就是動態的給類或對象增長職責的設計模式。它能在不改變類或對象自身的基礎上,在程序的運行期間動態的添加職責。這種設計模式很是符合敏捷開發的設計思想:先提煉出產品的MVP(Minimum Viable Product,最小可用產品)
,再經過快速迭代的方式添加功能。java
var Car = function() {} Car.prototype.drive = function() { console.log('乞丐版'); } var AutopilotDecorator = function(car) { this.car = car; } AutopilotDecorator.prototype.drive = function() { this.car.drive(); console.log('啓動自動駕駛模式'); } var car = new Car(); car = new AutopilotDecorator(car); car.drive(); //乞丐版;啓動自動駕駛模式;
這種方式的實現要點是裝飾器類要維護目標對象的一個引用,同時要實現目標類的全部接口(這個例子裏的drive
方法,若是還有其它方法,好比brake
,AutopilotDecorator也要實現)。調用方法時,先執行目標對象原有的方法,再執行自行添加的特性。編程
當接口比較多,裝飾器也比較多時,能夠獨立抽取一個裝飾器父類,實現目標類的全部接口,再建立真正的裝飾器來繼承這個父類。segmentfault
var Car = function() {} Car.prototype.drive = function() { console.log('乞丐版'); } /* 多了一個剎車方法 */ Car.prototype.brake = function() { console.log('剎車'); } /* 實現全部接口的裝飾器父類 */ var CarDecorator = function(car) { this.car = car; } CarDecorator.prototype = { drive: function() { this.car.drive(); }, brake: function() { this.car.brake(); } } /* 真正的裝飾器 */ var AutopilotDecorator = function(car) { CarDecorator.call(this, car); } AutopilotDecorator.prototype = new CarDecorator(); AutopilotDecorator.prototype.drive = function() { this.car.drive(); console.log('啓動自動駕駛模式'); } /* 真正的裝飾器 */ var HybridDecorator = function(car) { CarDecorator.call(this, car); } HybridDecorator.prototype = new CarDecorator(); HybridDecorator.prototype.brake = function() { this.car.brake(); console.log('啓動充電模式'); } var car = new Car(); car = new AutopilotDecorator(car); car = new HybridDecorator(car); car.drive(); //乞丐版;啓動自動駕駛模式; car.brake(); //剎車;啓動充電模式;
var car = { drive: function() { console.log('乞丐版'); } } var driveBasic = car.drive; var autopilotDecorator = function() { console.log('啓動自動駕駛模式'); } var carToDecorate = Object.create(car); carToDecorate.drive = function() { driveBasic(); autopilotDecorator(); } carToDecorate.drive(); //乞丐版;啓動自動駕駛模式;
這種實現方式徹底是基於JS自身的語言特色作考量。定義類的目的是實現代碼的封裝和複用,而JS這門語言是沒有類的概念的。它只有2種數據類型:基本類型和對象類型。實現邏輯的封裝和代碼的重用只須要經過對象來組織代碼,而後利用原生提供的克隆機制(Object.create
)來達到目的。設計模式
從代碼的角度看,若是想擴展drive
方法,只須要用一個變量來保存原函數的引用,而後再重寫drive
方法就能夠了。在重寫的方法裏面,只要記得調用方法原有的行爲就行。 app
另外,咱們能夠經過如下的工具函數,達到裝飾函數的目的:函數
Function.prototype.after = function(afterfn) { var _self = this; return function() { var ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; } } var car = { drive: function() { console.log('乞丐版'); } } var autopilotDecorator = function() { console.log('啓動自動駕駛模式'); } var carToDecorate = Object.create(car); carToDecorate.drive = car.drive.after(autopilotDecorator); carToDecorate.drive(); //乞丐版;啓動自動駕駛模式;
經過在Function
的原型鏈上定義after函數,給全部的函數都賦予了被擴展的功能,固然也能夠根據須要定義一個before
的函數,在函數執行前去作一些操做。這種實現方式借鑑了AOP(Aspect Oriented Programming,面向切面編程)
的思想。工具
ES7提供了一種相似的Java註解的語法糖decorator
,來實現裝飾者模式。使用起來很是簡潔:this
function autopilotDecorator(target, key, descriptor) { const method = descriptor.value; descriptor.value = () => { method.apply(target); console.log('啓動自動駕駛模式'); } return descriptor; } class Car { @autopilotDecorator drive() { console.log('乞丐版'); } } let car = new Car(); car.drive(); //乞丐版;啓動自動駕駛模式;
decorator
的實現依賴於ES5的Object.defineProperty
方法。defineProperty
所作的事情是爲一個對象增長新的屬性,或者更改某個已存在的屬性。調用方式是Object.defineProperty(obj, prop, descriptor)
。spa
var o = {}; // 建立一個新對象 // 在對象中添加一個屬性 Object.defineProperty(o, "name", { value : "Dickens", writable : true, enumerable : true, configurable : true }); // 在對象中添加一個方法 Object.defineProperty(o, "sayHello", { value : function() { console.log('Hello, my name is: ', this.name) }, writable : true, enumerable : true, configurable : true }); o.sayHello() //Hello, my name is: Dickens
decorator
的參數跟defineProperty
是徹底同樣的,含義也相似,經過修改descripter,就能達到擴展功能的目的。
本文介紹了裝飾者模式的基本概念,並經過不一樣的實現方式來介紹使用方法。對於不一樣的使用方法,也做了比較透徹的解釋,讓你們不但知其然,還知其因此然。
裝飾者模式是一種十分經常使用且功能強大的模式,利用ES7的語法糖,咱們能用很是簡潔的方式來表達裝飾的意圖,推薦你們在實際項目中用起來。