摘要:自行車的基類如下其它方法那么我們可以先創建一個裝飾者模式基類這個基類其實沒有做什么事情,它只是接受一個實例,實現其對應的方法,并且將調用其方法返回而已。
什么是裝飾者模式
裝飾者模式是一種為函數或類增添特性的技術,它可以讓我們在不修改原來對象的基礎上,為其增添新的能力和行為。它本質上也是一個函數(在javascipt中,類也只是函數的語法糖)。
我們什么時候可以弄到它呢我們來假設一個場景,一個自行車商店有幾種型號的自行車,現在商店允許用戶為每一種自行車提供一些額外的配件,比如前燈、尾燈、鈴鐺等。每選擇一種或幾種配件都會影響自行車的售價。
如果按照比較傳統的創建子類的方式,就等于我們目前有一個自行車基類,而我們要為每一種可能的選擇創建一個新的類。可是由于用戶可以選擇一種或者幾種任意的配件,這就導致最終可能會生產幾十上百個子類,這明顯是不科學的。然而,對這種情況,我們可以使用裝飾者模式來解決這個問題。
自行車的基類如下:
class Bicycle { // 其它方法 wash () {} ride () {} getPrice() { return 200; } }
那么我們可以先創建一個裝飾者模式基類
class BicycleDecotator { constructor(bicycle) { this.bicycle = bicycle; } wash () { return this.bicycle.wash(); } ride () { return this.bicycle.ride(); } getPrice() { return this.bicycle.getPrice(); } }
這個基類其實沒有做什么事情,它只是接受一個Bicycle實例,實現其對應的方法,并且將調用其方法返回而已。
有了這個基類之后,我們就可以根據我們的需求對原來的Bicycle類為所欲為了。比如我可以創建一個添加了前燈的裝飾器以及添加了尾燈的裝飾器:
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; } }
那么,接下來我們就可以來對其自由組合了:
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
這樣寫的好處是什么呢?假設說我們有10個配件,那么我們只需要寫10個配件裝飾器,然后就可以任意搭配成不同配件的自行車并計算價格。而如果是按照子類的實現方式的話,10個配件可能就需要有幾百個甚至上千個子類了。
從例子中我們可以看出裝飾者模式的適用場合:
如果你需要為類增添特性或職責,可是從類派生子類的解決方法并不太現實的情況下,就應該使用裝飾者模式。
在例子中,我們并沒有對原來的Bicycle基類進行修改,因此也不會對原有的代碼產生副作用。我們只是在原有的基礎上增添了一些功能。因此,如果想為對象增添特性又不想改變使用該對象的代碼的話,則可以采用裝飾者模式。
裝飾者模式除了可以應用在類上之外,還可以應用在函數上(其實這就是高階函數)。比如,我們想測量函數的執行時間,那么我可以寫這么一個裝飾器:
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(`該函數消耗了${elapserdTime}ms`); } } const newFunc = timeProfileDecorator(func); console.log(newFunc());做一些有趣的事情
既然知道了裝飾者模式可以在不修改原來代碼的情況下為其增添一些新的功能,那么我們就可以來做一些有趣的事情。
我們可以為一個類的方法提供性能分析的功能。
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(`該函數消耗了${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());對函數進行增強
節流函數or防抖函數
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); } }
緩存函數返回值
// 緩存函數結果,對于一些計算量比較大的函數效果比較明顯。 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));
構造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 (); } }; export default ShadowCompareDecorator;
當然,你如果用過react-redux的話,你肯定也用過connect。其實connect也是一種高階組件的方式。它通過裝飾者模式,從Provider的context里拿到全局的state,并且將其通過props的方式傳給原來的組件。
總結使用裝飾者模式可以讓我們為原有的類和函數增添新的功能,并且不會修改原有的代碼或者改變其調用方式,因此不會對原有的系統帶來副作用。我們也不用擔心原來系統會因為它而失靈或者不兼容。就我個人而言,我覺得這是一種特別好用的設計模式。
一個好消息就是,js的裝飾器已經加入了es7的草案里啦。它讓我們可以更加優雅的使用裝飾者模式,如果有興趣的可以添加下babel的plugins插件提前體驗下。阮一峰老師的這個教程也十分淺顯易懂。
參考文獻:
Javascript設計模式
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109786.html
摘要:裝飾者模式組成結構抽象構件給出抽象接口或抽象類,以規范準備接收附加功能的對象。裝飾者模式圖解裝飾者模式應用場景需要擴展一個類的功能,或給一個類添加附加職責。裝飾者對象接受所有來自客戶端的請求。參考資料設計模式 一、了解裝飾者模式 1.1 什么是裝飾者模式 裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾者來包裹真實的對...
摘要:什么是裝飾者模式今天我們來講另外一個非常實用的設計模式裝飾者模式。就增加功能來說,裝飾者模式相比生成子類更為靈活。下面,裝飾者模式就要正式登場了。下一步,我們可以愉快的去使用裝飾者模式啦 什么是裝飾者模式 今天我們來講另外一個非常實用的設計模式:裝飾者模式。這個名字聽上去有些莫名其妙,不著急,我們先來記住它的一個別名:包裝器模式。 我們記著這兩個名字來開始今天的文章。 首先還是上《設計...
摘要:若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。裝飾者類反映出被裝飾組件類型。裝飾者會導致設計中出現許多小對象,如果過度使用,會讓程序變得很復雜。 嘿嘿嘿,你是不是很喜歡用繼承呢?感覺沒什么事情是一個爸爸類搞不定的,有的話就兩個,快來跟我看看這個模式吧,它能讓你斷奶,給愛用繼承的人一個全新的設計眼界。 直奔主題,你是否有聽說...
摘要:下裝飾者的實現了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來實現裝飾者模式原函數拍照片定義函數裝飾函數加濾鏡用裝飾函數裝飾原函數這樣我們就實現了抽離拍照與濾鏡邏輯,如果以后需要自動上傳功能,也可以通過函數來添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當我們拍了一張照片準備發朋友...
摘要:相關設計模式裝飾者模式和代理模式裝飾者模式關注再一個對象上動態添加方法代理模式關注再對代理對象的控制訪問,可以對客戶隱藏被代理類的信息裝飾著模式和適配器模式都叫包裝模式關于新職責適配器也可以在轉換時增加新的職責,但主要目的不在此。 0x01.定義與類型 定義:裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的...
閱讀 916·2021-09-29 09:35
閱讀 1261·2021-09-28 09:36
閱讀 1530·2021-09-24 10:38
閱讀 1079·2021-09-10 11:18
閱讀 639·2019-08-30 15:54
閱讀 2507·2019-08-30 13:22
閱讀 1972·2019-08-30 11:14
閱讀 707·2019-08-29 12:35