在傳統的面嚮對象語言中,給對象添加功能經常使用繼承的方式,可是繼承的方式並不靈活, 還會帶來許多問題:一方面會致使超類和子類之間存在強耦合性,當超類改變時,子類也會隨之 改變;另外一方面,繼承這種功能複用方式一般被稱爲「白箱複用」,「白箱」是相對可見性而言的, 在繼承方式中,超類的內部細節是對子類可見的,繼承經常被認爲破壞了封裝性。裝飾者模式可以在不改變對象自身的基礎上,在程序運行期間給對象動態地添加職責。跟繼承相比,裝飾者是一種更輕便靈活的作法,這是一種「即用即付」的方式ajax
假設咱們在編寫一個飛機大戰的遊戲,隨着經驗值的增長,咱們操做的飛機對象能夠升級成更厲害的飛機,一開始這些飛機只能發射普通的子彈,升到第二級時能夠發射導彈,升到第三級時能夠發射原子彈。設計模式
var plane = {
fire: function(){
console.log( '發射普通子彈' );
}
}
var missileDecorator = function(){
console.log( '發射導彈' );
}
var atomDecorator = function(){
console.log( '發射原子彈' );
}
var fire1 = plane.fire;
plane.fire = function(){
fire1();
missileDecorator();
}
var fire2 = plane.fire;
plane.fire = function(){
fire2();
atomDecorator();
}
plane.fire();
// 分別輸出: 發射普通子彈、發射導彈、發射原子彈
複製代碼
首先給出 Function.prototype.before 方法和 Function.prototype.after 方法bash
Function.prototype.before = function( beforefn ){
var __self = this; // 保存原函數的引用
return function(){ // 返回包含了原函數和新函數的"代理"函數
beforefn.apply( this, arguments ); // 執行新函數,且保證 this 不被劫持,新函數接受的參數 // 也會被原封不動地傳入原函數,新函數在原函數以前執行
return __self.apply( this, arguments ); // 執行原函數並返回原函數的執行結果, // 而且保證 this 不被劫持
} }
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.apply( this, arguments );
afterfn.apply( this, arguments );
return ret;
}
};
複製代碼
var showLogin = function(){
console.log( '打開登陸浮層' );
log( this.getAttribute( 'tag' ) );
}
var log = function( tag ){
console.log( '上報標籤爲: ' + tag );
(new Image).src = 'http:// xxx.com/report?tag=' + tag;
}
document.getElementById( 'button' ).onclick = showLogin;
複製代碼
var showLogin = function(){
console.log( '打開登陸浮層' );
}
var log = function(){
console.log( '上報標籤爲: ' + this.getAttribute( 'tag' ) );
}
showLogin = showLogin.after( log ); // 打開登陸浮層以後上報數據
document.getElementById( 'button' ).onclick = showLogin;
複製代碼
var formSubmit = function(){
if ( username.value === '' ){
return alert ( '用戶名不能爲空' );
}
if ( password.value === '' ){
return alert ( '密碼不能爲空' );
}
var param = {
username: username.value, password: password.value
}
ajax( 'http:// xxx.com/login', param );
}
submitBtn.onclick = function(){
formSubmit();
}
複製代碼
var validata = function(){
if ( username.value === '' ){
alert ( '用戶名不能爲空' );
return false;
}
if ( password.value === '' ){
alert ( '密碼不能爲空' );
return false;
}
}
var formSubmit = function(){
var param = {
username: username.value,
password:password.value
}
ajax( 'http:// xxx.com/login', param );
}
formSubmit = formSubmit.before( validata );
submitBtn.onclick = function(){
formSubmit();
}
複製代碼
裝飾者模式和代理模式的結構看起來很是相像,這兩種模式都描述了怎樣爲對象提供 必定程度上的間接引用,它們的實現部分都保留了對另一個對象的引用,而且向那個對象發送 請求。 代理模式和裝飾者模式最重要的區別在於它們的意圖和設計目的。代理模式的目的是,當直接訪問本體不方便或者不符合須要時,爲這個本體提供一個替代者。本體定義了關鍵功能,而代理提供或拒絕對它的訪問,或者在訪問本體以前作一些額外的事情。裝飾者模式的做用就是爲對 象動態加入行爲。app