摘要:裝飾者要實現這些相同的方法繼承自裝飾器對象創建具體的裝飾器,也是接收作對參數接下來我們要為每一個功能創建一個裝飾者對象,重寫父級方法,添加我們想要的功能。
裝飾模式
僅僅包裝現有的模塊,使之 “更加華麗” ,并不會影響原有接口的功能 —— 好比你給手機添加一個外殼罷了,并不影響手機原有的通話、充電等功能;使用 ES7 的 decorator
ES7 中增加了一個 decorator 屬性,它借鑒自 Python
下面我們以 鋼鐵俠 為例講解如何使用 ES7 的 decorator。
以鋼鐵俠為例,鋼鐵俠本質是一個人,只是“裝飾”了很多武器方才變得那么 NB,不過再怎么裝飾他還是一個人。
我們的示例場景是這樣的
首先創建一個普通的Man類,它的抵御值 2,攻擊力為 3,血量為 3;
然后我們讓其帶上鋼鐵俠的盔甲,這樣他的抵御力增加 100,變成 102;
讓其帶上光束手套,攻擊力增加 50,變成 53;
最后讓他增加“飛行”能力
【Demo 1】對方法的裝飾:裝備盔甲創建 Man 類:
class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`當前狀態 ===> ${tony}`); // 輸出:當前狀態 ===> 防御力:2,攻擊力:3,血量:3
代碼直接放在 http://babeljs.io/repl/ 中運行查看結果,
記得勾選Setting的Evaluate選項,和 options的選項為legacy
創建 decorateArmour 方法,為鋼鐵俠裝配盔甲——注意 decorateArmour 是裝飾在方法init上的。
function decorateArmour(target, key, descriptor) { const method = descriptor.value; let moreDef = 100; let ret; descriptor.value = (...args)=>{ args[0] += moreDef; ret = method.apply(target, args); return ret; } return descriptor; } class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`當前狀態 ===> ${tony}`); // 輸出:當前狀態 ===> 防御力:102,攻擊力:3,血量:3
我們先看輸出結果,防御力的確增加了 100,看來盔甲起作用了。
Decorators 的本質是利用了 ES5 的 Object.defineProperty 屬性,這三個參數其實是和 Object.defineProperty 參數一致的
【Demo 2】裝飾器疊加:增加光束手套在上面的示例中,我們成功為 普通人 增加 “盔甲” 這個裝飾;現在我想再給他增加 “光束手套”,希望額外增加 50 點防御值。
... function decorateLight(target, key, descriptor) { const method = descriptor.value; let moreAtk = 50; let ret; descriptor.value = (...args)=>{ args[1] += moreAtk; ret = method.apply(target, args); return ret; } return descriptor; } class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour @decorateLight init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } ... } var tony = new Man(); console.log(`當前狀態 ===> ${tony}`); //輸出:當前狀態 ===> 防御力:102,攻擊力:53,血量:3
在這里你就能看出裝飾模式的優勢了,它可以對某個方法進行疊加使用,對原類的侵入性非常小,只是增加一行@decorateLight而已,可以方便地增刪;(同時還可以復用)
【Demo 3】對類的裝飾:增加飛行能力裝飾模式有兩種:純粹的裝飾模式 和 半透明的裝飾模式。
上述的兩個 demo 中所使用的應該是 純粹的裝飾模式,它并不增加對原有類的接口;下面要講 demo 是給普通人增加“飛行”能力,相當于給類新增一個方法,屬于 半透明的裝飾模式,有點兒像適配器模式的樣子。
... // 3 function addFly(canFly){ return function(target){ target.canFly = canFly; let extra = canFly ? "(技能加成:飛行能力)" : ""; let method = target.prototype.toString; target.prototype.toString = (...args)=>{ return method.apply(target.prototype,args) + extra; } return target; } } @addFly(true) class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour @decorateLight init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } ... } ... console.log(`當前狀態 ===> ${tony}`); // 輸出:當前狀態 ===> 防御力:102,攻擊力:53,血量:3(技能加成:飛行能力)
作用在方法上的 decorator 接收的第一個參數(target )是類的 prototype;如果把一個 decorator 作用到類上,則它的第一個參數 target 是 類本身。
使用原生 JS 實現裝飾器模式Man 是具體的類,Decorator 是針對 Man 的裝飾器基類
具體的裝飾類 DecorateArmour 典型地使用 prototype 繼承方式 繼承自 Decorator 基類;
基于 IOC(控制反轉)思想 ,Decorator 是接受 Man 類,而不是自己創建 Man 類;
// 首先我們要創建一個基類 function Man(){ this.def = 2; this.atk = 3; this.hp = 3; } // 裝飾者也需要實現這些方法,遵守 Man 的接口 Man.prototype={ toString:function(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } // 創建裝飾器,接收 Man 對象作為參數。 var Decorator = function(man){ this.man = man; } // 裝飾者要實現這些相同的方法 Decorator.prototype.toString = function(){ return this.man.toString(); } // 繼承自裝飾器對象 // 創建具體的裝飾器,也是接收 Man 作對參數 var DecorateArmour = function(man){ var moreDef = 100; man.def += moreDef; Decorator.call(this,man); } DecorateArmour.prototype = new Decorator(); // 接下來我們要為每一個功能創建一個裝飾者對象,重寫父級方法,添加我們想要的功能。 DecorateArmour.prototype.toString = function(){ return this.man.toString(); } // 注意這里的調用方式 // 構造器相當于“過濾器”,面向切面的 var tony = new Man(); tony = new DecorateArmour(tony); console.log(`當前狀態 ===> ${tony}`); // 輸出:當前狀態 ===> 防御力:102,攻擊力:3,血量:3經典實現:Logger
經典應用就是 日志系統 了,那么我們也用 ES7 的語法給鋼鐵俠打造一個日志系統吧。
/** * Created by jscon on 15/10/16. */ let log = (type) => { return (target, name, descriptor) => { const method = descriptor.value; descriptor.value = (...args) => { console.info(`(${type}) 正在執行: ${name}(${args}) = ?`); let ret; try { ret = method.apply(target, args); console.info(`(${type}) 成功 : ${name}(${args}) => ${ret}`); } catch (error) { console.error(`(${type}) 失敗: ${name}(${args}) => ${error}`); } return ret; } } } class IronMan { @log("IronMan 自檢階段") check(){ return "檢查完畢"; } @log("IronMan 攻擊階段") attack(){ return "擊倒敵人"; } @log("IronMan 機體報錯") error(){ throw "Something is wrong!"; } } var tony = new IronMan(); tony.check(); tony.attack(); tony.error(); // 輸出: // (IronMan 自檢階段) 正在執行: check() = ? // (IronMan 自檢階段) 成功 : check() => 檢查完畢 // (IronMan 攻擊階段) 正在執行: attack() = ? // (IronMan 攻擊階段) 成功 : attack() => 擊倒敵人 // (IronMan 機體報錯) 正在執行: error() = ? // (IronMan 機體報錯) 失敗: error() => Something is wrong!
Logger 方法的關鍵在于:
首先使用 const method = descriptor.value; 將原有方法提取出來,保障原有方法的純凈;
在 try..catch 語句是 調用 ret = method.apply(target, args);在調用之前之后分別進行日志匯報;
最后返回 return ret; 原始的調用結果
相關庫https://github.com/jayphelps/...
vue中使用裝飾器實現AOP編程在JavaScript中實現AOP,是把一個函數“動態織入”到另一個函數之中。
首先要構造Function的prototype
//prototype.js Function.prototype.before = function (beforefn) { let _self = this; return function () { beforefn.apply(this, arguments); return _self.apply(this, arguments); }; }; Function.prototype.after = function (afterfn) { let _self = this; return function () { let ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; }; Function.prototype.around = function (beforefn, afterfn) { let _self = this; return function () { beforefn.apply(this, arguments); let ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; };
編輯我們的裝飾器函數
//decorator.js export const before = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.before(() => { console.log(`Action-${key} 觸發埋點!`); }); }; }; export const after = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.after(() => { console.log(`Action-${key} 觸發埋點!`); }); }; }; export const around = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.around(() => { console.log(`Action-${key} 觸發埋點before!`); }, () => { console.log(`Action-${key} 觸發埋點after!`); }); }; };
編輯我們的vue文件
//test.vue
.babelrc文件
{ "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }] ] }摘自
http://www.liuweibo.cn/p/254#...
淘寶前端團隊
https://www.jianshu.com/p/208...
今日圖 羞羞噠文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109784.html
摘要:裝飾者模式組成結構抽象構件給出抽象接口或抽象類,以規范準備接收附加功能的對象。裝飾者模式圖解裝飾者模式應用場景需要擴展一個類的功能,或給一個類添加附加職責。裝飾者對象接受所有來自客戶端的請求。參考資料設計模式 一、了解裝飾者模式 1.1 什么是裝飾者模式 裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾者來包裹真實的對...
摘要:相關設計模式裝飾者模式和代理模式裝飾者模式關注再一個對象上動態添加方法代理模式關注再對代理對象的控制訪問,可以對客戶隱藏被代理類的信息裝飾著模式和適配器模式都叫包裝模式關于新職責適配器也可以在轉換時增加新的職責,但主要目的不在此。 0x01.定義與類型 定義:裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的...
摘要:裝飾者模式遵循了開閉原則,對擴展開放,對修改關閉。但是在使用裝飾者模式的同時可能會引入大量小類,而且使用裝飾者模式除了實例化組件外,還要把組件包裝進裝飾者,會使代碼顯得不易理解。 1. 簡介 ??裝飾者模式是一種結構型模式,它可以動態的將責任附加到對象上,在擴展功能方面,它比繼承更有彈性。裝飾者模式遵循了開閉原則,對擴展開放,對修改關閉。??雖然在裝飾者模式中也使用了繼承,但是繼承只是...
摘要:簡介代理模式和裝飾者模式是兩種常見的設計模式。這里通過構造函數的參數將被代理對象傳入到代理中,也可以通過其它方式,如提供一個方法。下面是的代碼輸出首先依然是先創建一個需要被代理的對象,然后把它傳入到的構造函數中。 簡介 代理模式和裝飾者模式是兩種常見的設計模式。代理模式是為其它對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以...
閱讀 645·2021-10-13 09:39
閱讀 1454·2021-09-09 11:53
閱讀 2646·2019-08-29 13:55
閱讀 724·2019-08-28 18:08
閱讀 2594·2019-08-26 13:54
閱讀 2408·2019-08-26 11:44
閱讀 1837·2019-08-26 11:41
閱讀 3777·2019-08-26 10:15