使用装饰者模式做有趣的事情
什么是裝飾者模式
裝飾者模式是一種為函數(shù)或類增添特性的技術(shù),它可以讓我們在不修改原來對象的基礎(chǔ)上,為其增添新的能力和行為。它本質(zhì)上也是一個函數(shù)(在javascipt中,類也只是函數(shù)的語法糖)。
我們什么時候可以弄到它呢
我們來假設(shè)一個場景,一個自行車商店有幾種型號的自行車,現(xiàn)在商店允許用戶為每一種自行車提供一些額外的配件,比如前燈、尾燈、鈴鐺等。每選擇一種或幾種配件都會影響自行車的售價。
如果按照比較傳統(tǒng)的創(chuàng)建子類的方式,就等于我們目前有一個自行車基類,而我們要為每一種可能的選擇創(chuàng)建一個新的類??墒怯捎谟脩艨梢赃x擇一種或者幾種任意的配件,這就導(dǎo)致最終可能會生產(chǎn)幾十上百個子類,這明顯是不科學(xué)的。然而,對這種情況,我們可以使用裝飾者模式來解決這個問題。
自行車的基類如下:
class Bicycle {// 其它方法wash () {}ride () {}getPrice() {return 200;} } 復(fù)制代碼那么我們可以先創(chuàng)建一個裝飾者模式基類
class BicycleDecotator {constructor(bicycle) {this.bicycle = bicycle;}wash () {return this.bicycle.wash();}ride () {return this.bicycle.ride();}getPrice() {return this.bicycle.getPrice();} } 復(fù)制代碼這個基類其實沒有做什么事情,它只是接受一個Bicycle實例,實現(xiàn)其對應(yīng)的方法,并且將調(diào)用其方法返回而已。
有了這個基類之后,我們就可以根據(jù)我們的需求對原來的Bicycle類為所欲為了。比如我可以創(chuàng)建一個添加了前燈的裝飾器以及添加了尾燈的裝飾器:
class HeadLightDecorator extends BicycleDecorator {constructor(bicycle) {super(bicycle);}getPrice() {return this.bicycle.getPrice() + 20;} } class TailLightDecorator extends BicycleDecorator {constructor(bicycle) {super(bicycle);}getPrice() {return this.bicycle.getPrice() + 20;} } 復(fù)制代碼那么,接下來我們就可以來對其自由組合了:
let bicycle = new Bicycle(); console.log(bicycle.getPrice()); // 200 bicycle = new HeadLightDecorator(bicycle); // 添加了前燈的自行車 console.log(bicycle.getPrice()); // 220 bicycle = new TailLightDecorator(bicycle); // 添加了前燈和尾燈的自行車 console.log(bicycle.getPrice()); // 240 復(fù)制代碼這樣寫的好處是什么呢?假設(shè)說我們有10個配件,那么我們只需要寫10個配件裝飾器,然后就可以任意搭配成不同配件的自行車并計算價格。而如果是按照子類的實現(xiàn)方式的話,10個配件可能就需要有幾百個甚至上千個子類了。
從例子中我們可以看出裝飾者模式的適用場合:
裝飾者模式除了可以應(yīng)用在類上之外,還可以應(yīng)用在函數(shù)上(其實這就是高階函數(shù))。比如,我們想測量函數(shù)的執(zhí)行時間,那么我可以寫這么一個裝飾器:
function func() {console.log('func'); } function timeProfileDecorator(func) {return function (...args) {const startTime = new Date();func.call(this, ...args);const elapserdTime = (new Date()).getTime() - startTime.getTime();console.log(`該函數(shù)消耗了${elapserdTime}ms`);} } const newFunc = timeProfileDecorator(func); console.log(newFunc()); 復(fù)制代碼做一些有趣的事情
既然知道了裝飾者模式可以在不修改原來代碼的情況下為其增添一些新的功能,那么我們就可以來做一些有趣的事情。
我們可以為一個類的方法提供性能分析的功能。
class TimeProfileDecorator {constructor(component, keys) {this.component = component;this.timers = {};const self = this;for (let i in keys) {let key = keys[i];if (typeof component[key] === 'function') {this[key] = function(...args) {this.startTimer(key);// 解決this引用錯誤問題component[key].call(component, ...args);this.logTimer(key);}}}}startTimer(namespace) {this.timers[namespace] = new Date();}logTimer(namespace) {const elapserdTime = (new Date()).getTime() - this.timers[namespace].getTime();console.log(`該函數(shù)消耗了${elapserdTime}ms`);} } // example class Test {constructor() {this.name = 'cjg';this.age = 22;}sayName() {console.log(this.name);}sayAge() {console.log(this.age);} }let test1 = new Test(); test1 = new TimeProfileDecorator(test1, ['sayName', 'sayAge']); console.log(test1.sayName()); console.log(test1.sayAge()); 復(fù)制代碼對函數(shù)進(jìn)行增強(qiáng)
節(jié)流函數(shù)or防抖函數(shù)
function throttle(func, delay) {const self = this;let tid;return function(...args) {if (tid) return;tid = setTimeout(() => {func.call(self, ...args);tid = null;}, delay);} }function debounce(func, delay) {const self = this;let tid;return function(...args) {if (tid) clearTimeout(tid);tid = setTimeout(() => {func.call(self, ...args);tid = null;}, delay);} } 復(fù)制代碼緩存函數(shù)返回值
// 緩存函數(shù)結(jié)果,對于一些計算量比較大的函數(shù)效果比較明顯。 function memorize(func) {const cache = {};return function (...args) {const key = JSON.stringify(args);if (cache[key]) {console.log('緩存了');return cache[key];}const result = func.call(this, ...args);cache[key] = result;return result;}; }function fib(num) {return num < 2 ? num : fib(num - 1) + fib(num - 2); }const enhanceFib = memorize(fib); console.log(enhanceFib(40)); console.log(enhanceFib(40)); console.log(enhanceFib(40)); console.log(enhanceFib(40)); 復(fù)制代碼構(gòu)造React高階組件,為組件增加額外的功能,比如為組件提供shallowCompare功能:
import React from 'react'; const { Component } = react;const ShadowCompareDecorator = (Instance) => class extends Component {shouldComponentUpdate(nextProps, nextState) {return !shallowCompare(this.props, nextProps) ||!shallowCompare(this.state, nextState);}render() {return (<Instance {...this.props} />);} };export default ShadowCompareDecorator; 復(fù)制代碼當(dāng)然,你如果用過react-redux的話,你肯定也用過connect。其實connect也是一種高階組件的方式。它通過裝飾者模式,從Provider的context里拿到全局的state,并且將其通過props的方式傳給原來的組件。
總結(jié)
使用裝飾者模式可以讓我們?yōu)樵械念惡秃瘮?shù)增添新的功能,并且不會修改原有的代碼或者改變其調(diào)用方式,因此不會對原有的系統(tǒng)帶來副作用。我們也不用擔(dān)心原來系統(tǒng)會因為它而失靈或者不兼容。就我個人而言,我覺得這是一種特別好用的設(shè)計模式。
一個好消息就是,js的裝飾器已經(jīng)加入了es7的草案里啦。它讓我們可以更加優(yōu)雅的使用裝飾者模式,如果有興趣的可以添加下babel的plugins插件提前體驗下。阮一峰老師的這個教程也十分淺顯易懂。
覺得幫助的麻煩點下贊哦。謝謝。
本文地址在->本人博客地址, 歡迎給個 start 或 follow 參考文獻(xiàn):
Javascript設(shè)計模式
總結(jié)
以上是生活随笔為你收集整理的使用装饰者模式做有趣的事情的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery 之 [ 动画 ]
- 下一篇: 给jdk写注释系列之jdk1.6容器(1