摘要:包裝模式是這樣干的首先我們弄一個裝飾器,它實現了接口,以組合的方式接收我們的默認實現類。其實裝飾器抽象類的作用就是代理核心的功能還是由最簡單的實現類來做,只不過在擴展的時候可以添加一些沒有的功能而已。
前言
只有光頭才能變強
回顧前面:
給女朋友講解什么是代理模式
前一篇已經講解了代理模式了,今天要講解的就是裝飾模式啦~
在看到FilterInputStream和FilterOutputStream時看到了之前常聽見的裝飾模式(對IO一定了解的同學可能都會知道那么一句話:在IO用得最多的就是裝飾模式了)!
其實無論是代理模式還是裝飾模式。本質上我認為就是對原有對象增強的方式~
那么接下來就開始吧,如果文章有錯誤的地方請大家多多包涵,不吝在評論區指正哦~
聲明:本文使用JDK1.8一、對象增強的常用方式
很多時候我們可能對Java提供給我們的對象不滿意,不能滿足我們的功能。此時我們就想對Java原對象進行增強,能夠實現我們想要的功能就好~
一般來說,實現對象增強有三種方式:
繼承
繼承父類,子類擴展
裝飾器模式
使用“包裝”的方式來增強對象
代理模式
給女朋友講解么是代理模式
1.1繼承最簡單的方式就是繼承父類,子類擴展來達到目的。雖然簡單,但是這種方式的缺陷非常大:
一、如果父類是帶有數據、信息、屬性的話,那么子類無法增強。
二、子類實現了之后需求無法變更,增強的內容是固定的。
1.1.1第一點第一點就拿以前在學JDBC的時候來說:
當時想要自己寫一個簡易的JDBC連接池,連接池由List
因為我們想要的功能是:調用close()是讓我們的Connection返回到“連接池”(集合)中,而不是關閉掉。
此時我們不能使用繼承父類的方式來實現增強。因為Connection對象是由數據庫廠商來實現的,在得到Connection對象的時候綁定了各種信息(數據庫的username、password、具體的數據庫是啥等等)。我們子類繼承Connection是無法得到對應的數據的!就更別說調用close()方法了。
1.1.2第二點第二點我也舉個例子:
現在我設計一個電話類:
public class Phone { // 可以打電話 public void call() { System.out.println("打電話給周圍的人關注公眾號Java3y"); } }
此時,我想打電話之前能聽彩鈴,于是我繼承Phone類,實現我想要的功能。
public class MusicPhone extends Phone { // 聽彩鈴 public void listenMusic() { System.out.println("我懷念的是無話不說,我懷念的是一起做夢~~~~~~"); } @Override public void call() { // 在打電話之前聽彩鈴 listenMusic(); super.call(); } }
我們的功能就做好了:
此時,我又突然想實現多一個需求了,我想要聽完電話之后告訴我一下當前的時間是多少。沒事,我們又繼承來增強一下:
// 這里繼承的是MusicPhone類 public class GiveCurrentTimePhone extends MusicPhone { // 給出當前的時間 public void currentTime() { System.out.println("當前的時間是:" + System.currentTimeMillis()); } @Override public void call() { super.call(); // 打完電話提示現在的時間是多少啦 currentTime(); } }
所以我們還是可以完成任務滴:
可是我需求現在又想變了:
我不想聽彩鈴了,只想聽完電話通知一下時間就好了........(可是我們的通知時間電話類是繼承在聽彩鈴的電話類基礎之上的),,,
我又有可能:我想在聽電話之前報告一下時間,聽完電話聽音樂!...
如果需求變動很大的情況下,而我們又用繼承的方式來實現這樣會導致一種現象:類爆炸(類數量激增)!并且繼承的層次可能會比較多~
所以,我們可以看到子類繼承父類這種方式來擴展是十分局限的,不靈活的~
因此我們就有了裝飾模式!
1.2裝飾模式首先我們來看看裝飾模式是怎么用的吧。
1.2.1前提代碼電話接口:
// 一個良好的設計是抽取成接口或者抽象類的 public interface Phone { // 可以打電話 void call(); }
具體的實現:
public class IphoneX implements Phone { @Override public void call() { System.out.println("打電話給周圍的人關注公眾號Java3y"); } }1.2.2包裝模式實現
上面我們已經擁有了一個接口還有一個默認實現。包裝模式是這樣干的:
首先我們弄一個裝飾器,它實現了接口,以組合的方式接收我們的默認實現類。
// 裝飾器,實現接口 public abstract class PhoneDecorate implements Phone { // 以組合的方式來獲取默認實現類 private Phone phone; public PhoneDecorate(Phone phone) { this.phone = phone; } @Override public void call() { phone.call(); } }
有了裝飾器以后,我們的擴展都可以以裝飾器為基礎進行擴展,繼承裝飾器來擴展就好了!
我們想要在打電話之前聽音樂:
// 繼承著裝飾器來擴展 public class MusicPhone extends PhoneDecorate { public MusicPhone(Phone phone) { super(phone); } // 定義想要擴展的功能 public void listenMusic() { System.out.println("繼續跑 帶著赤子的驕傲,生命的閃耀不堅持到底怎能看到,與其茍延殘喘不如縱情燃燒"); } // 重寫打電話的方法 @Override public void call() { // 在打電話之前聽音樂 listenMusic(); super.call(); } }
現在我也想在打完電話后通知當前的時間,于是我們也繼承裝飾類來擴展:
// 這里繼承的是MusicPhone裝飾器類 public class GiveCurrentTimePhone extends PhoneDecorate { public GiveCurrentTimePhone(Phone phone) { super(phone); } // 自定義想要實現的功能:給出當前的時間 public void currentTime() { System.out.println("當前的時間是:" + System.currentTimeMillis()); } // 重寫要增強的方法 @Override public void call() { super.call(); // 打完電話后通知一下當前時間 currentTime(); } }
可以完成任務:
就目前這樣看起來,比我直接繼承父類要麻煩,而功能效果是一樣的....我們繼續往下看~~
此時,我不想在打電話之前聽到彩鈴了,很簡單:我們不裝飾它就好了!
此時,我想在打電話前報告一下時間,在打完電話之后聽彩鈴。
注意:雖然說要改動類中的代碼,但是這種改動是合理的。因為我定義出的GiveCurrentTimePhone類和MusicPhone類本身從語義上就沒有規定擴展功能的執行順序
而繼承不一樣:先繼承Phone->實現MusicPhone->再繼承MusicPhone實現GiveCurrentTimePhone。這是固定的,從繼承的邏輯上已經寫死了具體的代碼,是難以改變的。
所以我們還是可以很簡單地完成功能:
二、裝飾模式講解可能有的同學在看完上面的代碼之后,還是迷迷糊糊地不知道裝飾模式是怎么實現“裝飾”的。下面我就再來解析一下:
第一步:我們有一個Phone接口,該接口定義了Phone的功能
第二步:我們有一個最簡單的實現類iPhoneX
第三步:寫一個裝飾器抽象類PhoneDecorate,以組合(構造函數傳遞)的方式接收我們最簡單的實現類iPhoneX。其實裝飾器抽象類的作用就是代理(核心的功能還是由最簡單的實現類iPhoneX來做,只不過在擴展的時候可以添加一些沒有的功能而已)。
第四步:想要擴展什么功能,就繼承PhoneDecorate裝飾器抽象類,將想要增強的對象(最簡單的實現類iPhoneX或者已經被增強過的對象)傳進去,完成我們的擴展!
再來看看下面的圖,就懂了!
往往我們的代碼可以省略起來,成了這個樣子(是不是和IO的非常像!)
// 先增強聽音樂的功能,再增強通知時間的功能 Phone phone = new GiveCurrentTimePhone(new MusicPhone(new IphoneX()));
結果是一樣的:
2.1裝飾模式的優缺點優點:
裝飾類和被裝飾類是可以獨立的,低耦合的?;ハ喽疾挥弥缹Ψ降拇嬖?/p>
裝飾模式是繼承的一種替代方案,無論包裝多少層,返回的對象都是is-a的關系(上面的例子:包裝完還是Phone類型)。
實現動態擴展,只要繼承了裝飾器就可以動態擴展想要的功能了。
缺點:
多層裝飾是比較復雜的,提高了系統的復雜度。不利于我們調試~
三、總結最后來補充一下包裝模式和代理模式的類圖:
對象增強的三種方式:
繼承
包裝模式
代理模式
那么只要遇到Java提供給我們的API不夠用,我們增強一下就行了。在寫代碼時,某個類被寫死了,功能不夠用,增強一下就可以了!
理解包裝模式,接下來就開始IO之旅咯~~~
參考資料:
《設計模式之禪》
https://wangjingxin.top/2016/10/21/decoration/
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導航:
https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69309.html
摘要:什么是裝飾者模式今天我們來講另外一個非常實用的設計模式裝飾者模式。就增加功能來說,裝飾者模式相比生成子類更為靈活。下面,裝飾者模式就要正式登場了。下一步,我們可以愉快的去使用裝飾者模式啦 什么是裝飾者模式 今天我們來講另外一個非常實用的設計模式:裝飾者模式。這個名字聽上去有些莫名其妙,不著急,我們先來記住它的一個別名:包裝器模式。 我們記著這兩個名字來開始今天的文章。 首先還是上《設計...
摘要:缺點不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。預防低水平人員帶來的風險。開閉原則,高拓展性。這里的訂閱者稱為觀察者,而被觀察者稱為發布者,當一個事件發生,發布者會發布通知所有訂閱者,并常常以事件對象形式傳遞消息。 介紹 最近開始給自己每周訂個學習任務,學習結果反饋為一篇文章的輸出,做好學習記錄。 這一周(02.25-03.03)我定的目標是《JavaScript 模式》...
摘要:在過去的幾個星期里,我開始看到基于的布局框架和柵格系統的出現。你可能傾向于明確給出所有元素的位置,或是盡可能依賴于自動布局。 showImg(https://segmentfault.com/img/remote/1460000010188997); 在過去的幾個星期里,我開始看到基于 CSS Grid 的布局框架和柵格系統的出現。我們驚訝它為什么出現的這么晚。但除了使用 CSS Gr...
摘要:在過去的幾個星期里,我開始看到基于的布局框架和柵格系統的出現。你可能傾向于明確給出所有元素的位置,或是盡可能依賴于自動布局。 showImg(https://segmentfault.com/img/remote/1460000010188997); 在過去的幾個星期里,我開始看到基于 CSS Grid 的布局框架和柵格系統的出現。我們驚訝它為什么出現的這么晚。但除了使用 CSS Gr...
閱讀 2123·2023-04-25 14:56
閱讀 2440·2021-11-16 11:44
閱讀 2696·2021-09-22 15:00
閱讀 1902·2019-08-29 16:55
閱讀 2177·2019-08-29 14:04
閱讀 2305·2019-08-29 11:23
閱讀 3678·2019-08-26 10:46
閱讀 1907·2019-08-22 18:43