您好,登錄后才能下訂單哦!
這篇文章給大家介紹Decorator Pattern怎么用,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
裝飾器模式(Decorator Pattern) 的目的非常簡單,那就是:
在不修改原有代碼的情況下增加邏輯。 |
這句話聽起來可能有些矛盾,既然都要增加邏輯了,怎么可能不去修改原有的代碼?但 SOLID (向?qū)ο笤O(shè)計5大重要原則)的開放封閉原則就是在試圖解決這個問題,其內(nèi)容是不去改動已經(jīng)寫好的核心邏輯,但又能夠擴充新邏輯,也就是對擴展開放,對修改關(guān)閉。
舉個例子,假如產(chǎn)品的需求是實現(xiàn)一個專門在瀏覽器的控制臺中輸出文本的功能,你可能會這樣做:
class Printer { print(text) { console.log(text); } } const printer = new Printer(); printer.print('something'); // something
在你滿意的看著自己的成果時,產(chǎn)品過來說了一句:“我覺得顏色不夠突出,還是把它改成黃色的吧!”
小菜一碟!你自信的打開百度一通操作之后,把代碼改成了下面這樣子:
class Printer { print(text) { console.log(`%c${text}`,'color: yellow;'); } }
但產(chǎn)品看了看又說:“這個字體有點太小了,再大一點,最好是高端大氣上檔次那種。
”好吧。。。“你強行控制著自己拿刀的沖動,一邊琢磨多大的字體才是高端大氣上檔次,一邊修改 print 的代碼:
class Printer { print(text) { console.log(`%c${text}`,'color: yellow;font-size: 36px;'); } }
這次改完你之后你心中已經(jīng)滿是 mmp 了,而且偷偷給產(chǎn)品貼了個標簽:
你無法保證這次是最后的修改,而且也可能會不只一個產(chǎn)品來對你指手劃腳。你呆呆的看著顯示器,直到電腦進入休眠模式,屏幕中映出你那張苦大仇深的臉,想著不斷變得亂七八糟的 print 方法,不知道該怎么去應付那些永無休止的需求。。。
在上面的例子中,最開始的 Printer 按照需求寫出它應該要有的邏輯,那就是在控制臺中輸出一些文本。換句話說,當寫完“在控制臺中輸出一些文本”這段邏輯后,就能將 Printer 結(jié)束了,因為它就是 Printer 的全部邏輯了。那在這個情況下該如何改變字體或是顏色的邏輯呢?
這時你該需要裝飾器模式了。
首先修改原來的 Printer,使它可以支持擴充樣式:
class Printer { print(text = '', style = '') { console.log(`%c${text}`, style); } }
之后分別創(chuàng)建改變字體和顏色的裝飾器:
const yellowStyle = (printer) => ({ ...printer, print: (text = '', style = '') => { printer.print(text, `${style}color: yellow;`); } }); const boldStyle = (printer) => ({ ...printer, print: (text = '', style = '') => { printer.print(text, `${style}font-weight: bold;`); } }); const bigSizeStyle = (printer) => ({ ...printer, print: (text = '', style = '') => { printer.print(text, `${style}font-size: 36px;`); } });
代碼中的 yellowStyle、boldStyle 和 bigSizeStyle 分別是給 print 方法的裝飾器,它們都會接收 printer,并以 printer 為基礎(chǔ)復制出一個一樣的對象出來并返回,而返回的 printer 與原來的區(qū)別是,各自 Decorator 都會為 printer 的 print 方法加上各自裝飾的邏輯(例如改變字體、顏色或字號)后再調(diào)用 printer 的 print。
使用方式如下:
只要把所有裝飾的邏輯抽出來,就能夠自由的搭配什么時候要輸出什么樣式,加入要再增加一個斜體樣式,也只需要再新增一個裝飾器就行了,不需要改動原來的 print 邏輯。
不過要注意的是上面的代碼只是簡單的把 Object 用解構(gòu)復制,如果在 prototype 上存在方法就有可能會出錯,所以要深拷貝一個新對象的話,還需要另外編寫邏輯:
const copyObj = (originObj) => { const originPrototype = Object.getPrototypeOf(originObj); let newObj = Object.create(originPrototype); const originObjOwnProperties = Object.getOwnPropertyNames(originObj); originObjOwnProperties.forEach((property) => { const prototypeDesc = Object.getOwnPropertyDescriptor(originObj, property); Object.defineProperty(newObj, property, prototypeDesc); }); return newObj; }
然后裝飾器內(nèi)改使上面代碼中的 copyObj,就能正確復制相同的對象了:
const yellowStyle = (printer) => { const decorator = copyObj(printer); decorator.print = (text = '', style = '') => { printer.print(text, `${style}color: yellow;`); }; return decorator; };
因為我們用的語言是 JavaScript,所以沒有用到類,只是簡單的裝飾某個方個方法,比如下面這個用來發(fā)布文章的 publishArticle:
const publishArticle = () => { console.log('發(fā)布文章'); };
如果你想要再發(fā)布文章之后在 微博或QQ空間之類的平臺上發(fā)個動態(tài),那又該怎么處理呢?是像下面的代碼這樣嗎?
const publishArticle = () => { console.log('發(fā)布文章'); console.log('發(fā) 微博 動態(tài)'); console.log('發(fā) QQ空間 動態(tài)'); };
這樣顯然不好!publishArticle 應該只需要發(fā)布文章的邏輯就夠了!而且如果之后第三方服務(wù)平臺越來越多,那 publishArticle 就會陷入一直加邏輯一直爽的情況,在明白了裝飾器模式后就不能再這樣做了!
所以把這個需求套上裝飾器:
const publishArticle = () => { console.log('發(fā)布文章'); }; const publishWeibo = (publish) => (...args) => { publish(args); console.log('發(fā) 微博 動態(tài)'); }; const publishQzone = (publish) => (...args) => { publish(args); console.log('發(fā) QQ空間 動態(tài)'); }; const publishArticleAndWeiboAndQzone = publishWeibo(publishQzone(publishArticle));
前面 Printer 的例子是復制一個對象并返回,但如果是方法就不用復制了,只要確保每個裝飾器都會返回一個新方法,然后會去執(zhí)行被裝飾的方法就行了。
裝飾器模式是一種非常有用的設(shè)計模式,在項目中也會經(jīng)常用到,當需求變動時,覺得某個邏輯很多余,那么直接不裝飾它就行了,也不需要去修改實現(xiàn)邏輯的代碼。每一個裝飾器都做他自己的事情,與其他裝飾器互不影響。
關(guān)于Decorator Pattern怎么用就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。