衆所周知裝飾器模式用于給對象在運作期間動态的增加某個功能,職責等。相較通過繼承的方式來擴充對象的功能,裝飾器顯得更加靈活,首先,我們可以動态給對象標明某個裝飾器,而不用hardcore繼承對象來實作某個功能點。其次:繼承的方式可能會導緻子類繁多,僅僅為了增加某一個單一的功能點,顯得有些多餘了。
裝飾器經典實作
首先來看看傳統的
decorator
的實作吧!假設現有一類叫
Person
的對象,該對象有個
speak
方法。開始的時候
Person
的對象隻會說中文,現在我要讓他說英文,那麼可以這麼做:
var Person = function(name) {
this.name = name;
};
Person.prototype.speak = function() {
console.log('你好!');
};
var SpeakEnglish = function(person) {
this.person = person;
};
SpeakEnglish.prototype.speak = function() {
this.speak();
console.log('hello!');
};
var SpeakJapanese = function(person) {
this.person = person;
};
SpeakJapanese.prototype.speak = function() {
this.speak();
console.log('こんにちは!');
};
var lufy = new Person('lufy');
var engLufy = new SpeakEnglish(lufy);
var japanLufy = new SpeakJapanese(engLufy);
japanLufy.speak();
通過包裝對象的方式給原對象增加新的功能,而不會影響原對象的同類對象。通過上面的方式路飛現在可以說3個國家的語言了。
javascript裝飾函數
對于
javascript
這類支援函數式程式設計的語言,我們要裝飾對象的某個方法其實也可以更加簡便,譬如說,通常我們寫前端頁面處理的時候需要監聽視窗的
onload
事件,但是同樣一個
onload
需要被多個地方監聽,可以這麼做:
var attachOnloadHandle = function(doit) {
if(typeof window.onload !== 'function') {
window.onload = doit;
}
else {
var func = window.onload;
window.onload = function() {
func();
doit();
};
}
};
函數實作比較簡單,但也算實用吧!
裝飾器模式與AOP
AOP是面向切面程式設計的簡寫。其語意為把一些與核心業務子產品無關的功能剝離出來,把整個系統劃分為更小的粒度。在完成這些子子產品的設計編碼後再逐漸包裝起來完成更大的功能。
javascript動态裝飾函數
通過前面的例子我們看到所謂裝飾無非是在執行某個功能之前執行一些前置或後置處理,提供兩個更加通用的接口:
Function.prototype.before = function(beforfunc) {
var self = this;
return function() {
beforfunc.apply(this, arguments);
self.apply(this, arguments);
};
};
Function.prototype.after = function(afterfunc) {
var self = this;
return function() {
self.apply(this, arguments);
afterfunc.apply(this, arguments);
};
};
試一試:
var func = function() {
console.log('hello');
};
func.before(function() {
console.log('你好');
}).after(function() {
console.log('こんにちは!');
});
考慮到安全方面的因素,我們在給微信伺服器發送一些請求的時候都需要帶上
accessToken
有些操作不需要帶這個參數,通過AOP函數包裝我們可以這樣來做:
var getToken = function(cb) {
//...
};
var preRequest = function(doit, params) {
var self = this;
getToken(function(err, token) {
if(err) {
//xxx
return;
}
self.token = token;
doit.apply(self, params);
});
};
var make = function(host, name, func) {
host[name] = function() {
this.preRequest(this['_' + name], arguments);
};
host['_' + name] = func;
};
//擷取卡券
make(exports, 'getCard', function(cardId, cb) {
var url = 'http://www.wechat.com/xxxx' + this.token;
//...
});