裝飾者模式(Decorator Pattern):在不改變原類別和繼承的情況下動態擴展物件功能,透過包裝一個物件來實作一個新的具有原始物件相同介面的新的物件。
裝飾者模式的特性:
1. 在不改變原始物件的原本結構的情況下進行功能新增。
2. 裝飾物件和原始物件具有相同的接口,可以使客戶以與原始物件相同的方式使用裝飾物件。
3. 裝飾物件中包含原始物件的引用,即裝飾物件是真正的原始物件經過包裝後的物件。
Javascript裝飾者模式詳解:
裝飾模式中,可以在執行時間動態新增附加功能到物件中。當處理靜態類別時,這可能是一個挑戰。在Javascript中,由於物件是可變的,因此,新增功能到物件中的過程本身並不是問題。
裝飾者模式的一個比較方便的特徵在於其預期行為的可自訂和可配置特性。可以從僅具有一些基本功能的普通物件開始,然後從可用裝飾資源池中選擇需要用於增強普通物件的哪些功能,並且按照順序進行裝飾,尤其是當裝飾順序很重要的時候。
實現裝飾者模式的其中一個方法是使得每個裝飾者成為一個對象,並且該對象包含了應該被重載的方法。每個裝飾者實際上繼承了目前已經被前一個裝飾者進行增強後的物件。每個裝飾方法在「繼承的物件」上呼叫了相同的方法並取得其值,此外它還繼續執行了一些操作。
先上實例1:
//需要装饰的类(函数) function Macbook() { this.cost = function () { return 1000; }; } //计算商品的包装费 function PackagingFee(macbook) { this.cost = function () { return macbook.cost() + 75; }; } //计算商品的运费 function Freight(macbook) { this.cost = function () { return macbook.cost() + 300; }; } //计算商品的保险费用 function Insurance(macbook) { this.cost = function () { return macbook.cost() + 250; }; } // 用法 var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook()))); console.log(myMacbook.cost());//1625
我們簡單的分析下上面的程式碼,上面的程式碼中,一共定義了四個函數(其中一個需要修飾的函數,三個用於修飾的函數)。
然後,宣告一個變數myMacbook指向new出來的Insurance對象,Insurance物件的形參指向new出來的Freight對象,Freight物件的形參指向new出來的PackagingFee對象,PackagingFee物件的形參指向new出來的Macbook對象。
接下來,呼叫myMacbook的cost方法。從上面的分析,我們可以得到myMacbook.cost()的值等於(Freight物件的cost方法+250),Freight物件的cost方法等於(PackagingFee物件的cost方法+300),PackagingFee物件的cost方法等於( Macbook物件的cost方法+75)。
所以最終的結果是:myMacbook.cost()的值 = 250 + (300 + (75 + 1000)) = 1625。
// 用法 var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook()))); console.log(myMacbook.cost());//1625 //上面的代码等价于下面拆分后的代码,或许拆分后代码你更能看出前后的逻辑性 var macbook = new Macbook(); var package = new PackagingFee(macbook); var freight = new Freight(package); var myMacbook = new Insurance(freight); //当然,如果你不想声明这么多变量(macbook、package、freight),只用一个变量也是可以的 var macbook = new Macbook(); macbook = new PackagingFee(macbook); macbook = new Freight(macbook); var myMacbook = new Insurance(macbook);
再看看實例2:
function ConcreteClass() { this.performTask = function () { this.preTask(); console.log('doing something'); this.postTask(); }; } function AbstractDecorator(decorated) { this.performTask = function () { decorated.performTask(); }; } function ConcreteDecoratorClass(decorated) { this.base = AbstractDecorator; this.base(decorated);// add performTask method decorated.preTask = function () { console.log('pre-calling..'); }; decorated.postTask = function () { console.log('post-calling..'); }; } var concrete = new ConcreteClass(); var decorator1 = new ConcreteDecoratorClass(concrete); decorator1.performTask(); //pre-calling.. //doing something //post-calling..
實例2實際上和實例1是非常類似的,我們來簡單分析下吧。首先,實例2中定義了三個函數,然後宣告了兩個變數concrete和decorator1,最後呼叫了decorator1的performTask方法。
粗看一眼,ConcreteDecoratorClass裡面好像並沒有performTask方法。我們先來分析下面的兩行程式碼:
var concrete = new ConcreteClass(); //声明一个变量concrete指向new出来的ConcreteClass对象 var decorator1 = new ConcreteDecoratorClass(concrete); //声明一个变量decorator1指向new出来的ConcreteDecoratorClass对象,并传入变量concrete作为形参
然後,我們再來逐行分析下ConcreteDecoratorClass函數裡面的程式碼:
this.base = AbstractDecorator; //定义一个当前对象(decorator1)的base属性,并指向函数AbstractDecorator this.base(decorated); //调用base属性指向的函数,也就是调用AbstractDecorator函数,同时传入形参decorated,形参decorated指向new出来的ConcreteClass对象
說到這裡,好像還是沒有分析出ConcreteDecoratorClass函數裡面有performTask方法,重點是看"this"!
ConcreteDecoratorClass函數中的this指向new出來的ConcreteDecoratorClass物件(也就是和decorator1指向同一個物件);
AbstractDecorator函數裡面的this關鍵是看哪個物件來呼叫這個函數,this就指向哪個物件(從程式碼「this.base = AbstractDecorator; this.base(decorated);」我們可以看出是new出來的ConcreteDecoratorClass物件在呼叫AbstractDecorator函數),所以AbstractDecorator函數裡面的this指向new出來的ConcreteDecoratorClass物件(也和decorator1指向同一個物件)。
總結下來,我們會發現,在上面的程式碼中,不管是ConcreteDecoratorClass函數裡面的this,還是AbstractDecorator函數裡面的this,都指向new出來的ConcreteDecoratorClass物件。
所以,當我們執行decorator1.performTask()時,它會繼續執行匿名函數中的程式碼(decorated.performTask();),匿名函數中的decorated形參指向new出來的ConcreteClass對象,並執行該物件的performTask方法。
最後看看實例3:
var tree = {}; tree.decorate = function () { console.log('Make sure the tree won\'t fall'); }; tree.getDecorator = function (deco) { tree[deco].prototype = this; return new tree[deco]; }; tree.RedApples = function () { this.decorate = function () { this.RedApples.prototype.decorate(); // 第7步:先执行原型(这时候是Angel了)的decorate方法 console.log('Add some red apples'); // 第8步 再输出 red // 将这2步作为RedApples的decorate方法 } }; tree.BlueApples = function () { this.decorate = function () { this.BlueApples.prototype.decorate(); // 第1步:先执行原型的decorate方法,也就是tree.decorate() console.log('Put on some blue apples'); // 第2步 再输出blue // 将这2步作为BlueApples的decorate方法 } }; tree.Angel = function () { this.decorate = function () { this.Angel.prototype.decorate(); // 第4步:先执行原型(这时候是BlueApples了)的decorate方法 console.log('An angel on the top'); // 第5步 再输出angel // 将这2步作为Angel的decorate方法 } }; tree = tree.getDecorator('BlueApples'); // 第3步:将BlueApples对象赋给tree,这时候父原型里的getDecorator依然可用 tree = tree.getDecorator('Angel'); // 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用 tree = tree.getDecorator('RedApples'); // 第9步:将RedApples对象赋给tree tree.decorate(); // 第10步:执行RedApples对象的decorate方法 //Make sure the tree won't fall //Add blue apples //An angel on the top //Put on some red apples
實例3看起來很複雜,實際上分析邏輯還是和前面兩個實例一樣,我們可以看出實例3中一共宣告了5個函數表達式。我們重點分析下下面的程式碼:
//tree.getDecorator('BlueApples')返回new出来的tree.BlueApples的实例对象,并将该对象赋值给空的tree对象 tree = tree.getDecorator('BlueApples'); //new出来的tree.BlueApples的实例对象的原型指向 --> 空对象tree //tree.getDecorator('Angel')返回new出来的tree.Angel的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.BlueApples的实例对象) tree = tree.getDecorator('Angel'); //new出来的tree.Angel的实例对象的原型指向 --> tree.BlueApples的实例对象 //tree.getDecorator('RedApples')返回new出来的tree.RedApples的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.Angel的实例对象) tree = tree.getDecorator('RedApples'); //new出来的tree.RedApples的实例对象的原型指向 --> tree.Angel的实例对象 //调用tree.decorate(),这里的tree已经是new出来的tree.RedApples的实例对象了。 //tree.RedApples的实例对象的decorate属性方法里面的第一行代码是 “this.RedApples.prototype.decorate()” //结合上面的分析可以得出以下的原型链结构: //this.RedApples.prototype --> tree.Angel; //tree.Angel.prototype --> tree.BlueApples; //tree.BlueApples.prototype --> 空对象tree tree.decorate();
分析到這裡,就不難知道最後的輸出結果了。
以上是Javascript裝飾者模式的特性與用法實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!