今天開始咱們專題的第八課了。本章節將介紹:三個設計模式,適配器模式、裝飾者模式和觀察者模式。經過學習適配器模式,能夠優雅的解決代碼功能的兼容問題。另外有重構需求的人羣必定須要掌握裝飾者模式。本章節參考資料書籍《Spring 5核心原理》中的第一篇 Spring 內功心法(Spring中經常使用的設計模式)(沒有電子檔,都是我取其精華並結合本身的理解,一個字一個字手敲出來的,若是以爲本文對你有用,請點個推薦)。java
適配器模式(Adapter Pattern)是指將一個類的接口轉換成客戶指望的另外一個接口,使本來的接口不兼容的類能夠一塊兒工做,屬於結構型設計模式。
適配器適用於如下幾種業務場景:
一、已經存在的類,它的方法和需求不匹配(方法結果相同或類似)的狀況。
二、適配器模式不是軟件設計階段考慮的設計模式,是隨着軟件維護,因爲不一樣產品、不一樣廠家形成功能相似而接口不相同狀況下的解決方案。有點亡羊補牢的感受。生活中也很是的應用場景,例如電源插轉換頭、手機充電轉換頭、顯示器轉接頭。json
在中國民用電都是 220V 交流電,但咱們手機使用的鋰電池使用的 5V 直流電。所以,我 們給手機充電時就須要使用電源適配器來進行轉換。下面咱們有代碼來還原這個生活場 景,建立 AC220 類,表示 220V 交流電:設計模式
package com.study; /** * @author wangzhongyuan */ public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("提供220V交流電"); return output; } }
建立5V電源的接口:app
package com.study; /** * @author wangzhongyuan */ public interface DC5 { int output5V(); }
建立提供5V電源的適配器:異步
package com.study; /** * @author wangzhongyuan */ public class PowerAdapter implements DC5{ AC220 ac220; public PowerAdapter(AC220 ac220) { this.ac220 = ac220; } @Override public int output5V() { int outputAC220V = ac220.outputAC220V(); System.out.println("將220v轉換成5v"); return outputAC220V/44; } }
測試代碼:ide
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/20 11:17 下午 */ public class DemoTest { public static void main(String[] args) { DC5 powerAdapter = new PowerAdapter(new AC220()); powerAdapter.output5V(); } }
輸出結果:post
從上面的代碼樣式能夠看出,適配器就是經過增長一個適配器類持有原有提供者的對象,實現了兩者的兼容。學習
一、能提升類的透明性和複用,現有的類複用但不須要改變。測試
二、目標類和適配器類解耦,提升程序的擴展性。this
三、在不少業務場景中符合開閉原則。
一、適配器編寫過程須要全面考慮,可能會增長系統的複雜性。
二、增長代碼閱讀難度,下降代碼可讀性,過多使用適配器會使系統代碼變得凌亂。
裝飾者模式(Decorator Pattern)是指在不改變原有對象的基礎之上,將功能附加到對 象上,提供了比繼承更有彈性的替代方案(擴展原有對象的功能),屬於結構型模式。 裝飾者模式在咱們生活中應用也比較多如給煎餅加雞蛋;給蛋糕加上一些水果;給房子 裝修等,爲對象擴展一些額外的職責。裝飾者在代碼程序中適用於如下場景:
一、用於擴展一個類的功能或給一個類添加附加職責。
二、動態的給一個對象添加功能,這些功能能夠再動態的撤銷。
來看一個這樣的場景,上班族白領其實大多有睡懶覺的習慣,天天早上上班都是踩點,因而不少小夥伴爲了多賴一下子牀都不吃早餐。那麼,也有些小夥伴可能在上班路上碰 到賣煎餅的路邊攤,都會順帶一個到公司茶水間吃早餐。賣煎餅的大姐能夠給你的煎餅 加雞蛋,也能夠加香腸。
建立煎餅類:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/20 11:57 下午 */ public class Battercake { public String getMsg(){ return "煎餅"; } public int getPrice(){ return 5; } }
給煎餅加個蛋:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/20 11:59 下午 */ public class BattercakeWithEgg extends Battercake{ @Override public String getMsg() { return super.getMsg() + "加雞蛋"; } @Override public int getPrice() { return super.getPrice() + 1; } }
再加個烤腸:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:00 上午 */ public class BatterCakeWithEggAndSausage extends BattercakeWithEgg{ @Override public String getMsg() { return super.getMsg() +"加烤腸"; } @Override public int getPrice() { return super.getPrice() + 3; } }
測試代碼:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:01 上午 */ public class DemoTest { public static void main(String[] args) { BatterCakeWithEggAndSausage batterCakeWithEggAndSausage = new BatterCakeWithEggAndSausage(); String msg = batterCakeWithEggAndSausage.getMsg(); int price = batterCakeWithEggAndSausage.getPrice(); System.out.println("買了:"+msg+",價格爲:"+price); } }
輸出結果:
運行結果沒有問題,可是若是用戶須要一個加 2 個雞蛋加 1 根香腸的煎餅,那麼用咱們如今的類結構是建立不出來的,也沒法自動計算出價格,除非再建立一個類作定製。 若是需求再變,一直加定製顯然是不科學的。那麼下面咱們就用裝飾者模式來解決上面 的問題。
建立煎餅抽象類:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:18 上午 */ public abstract class AbstractBatterCake { protected abstract String getMsg(); protected abstract int getPrice(); }
建立基礎套餐煎餅:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:19 上午 */ public class BatterCakeWithBase extends AbstractBatterCake{ @Override protected String getMsg() { return "煎餅"; } @Override protected int getPrice() { return 5; } }
建立額外套餐的抽象裝飾類:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:21 上午 */ public abstract class BatterCakeDecorator extends AbstractBatterCake{ AbstractBatterCake batterCake; public BatterCakeDecorator(AbstractBatterCake batterCake) { this.batterCake = batterCake; } protected abstract void doSomething(); @Override protected String getMsg() { return this.batterCake.getMsg(); } @Override protected int getPrice() { return this.batterCake.getPrice(); } }
建立加雞蛋套餐:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/20 11:59 下午 */ public class BattercakeWithEgg extends BatterCakeDecorator{ public BattercakeWithEgg(AbstractBatterCake batterCake) { super(batterCake); } @Override protected void doSomething() { } @Override public String getMsg() { return super.getMsg() + "加雞蛋"; } @Override public int getPrice() { return super.getPrice() + 1; } }
建立加烤腸套餐:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:00 上午 */ public class BatterCakeWithSausage extends BatterCakeDecorator{ public BatterCakeWithSausage(AbstractBatterCake batterCake) { super(batterCake); } @Override protected void doSomething() { } @Override public String getMsg() { return super.getMsg() +"加烤腸"; } @Override public int getPrice() { return super.getPrice() + 3; } }
測試代碼:
package com.study; /** * @author wang.zhongyuan * @version 1.0 * @date 2020/7/21 12:01 上午 */ public class DemoTest { public static void main(String[] args) { AbstractBatterCake batterCake; batterCake = new BatterCakeWithBase(); batterCake = new BattercakeWithEgg(batterCake); batterCake = new BatterCakeWithSausage(batterCake); //再加個雞蛋 batterCake = new BattercakeWithEgg(batterCake); System.out.println(batterCake.getMsg()+",價格:"+batterCake.getPrice()); } }
輸出結果:
裝飾者模式最本質的特徵是講原有類的附加功能抽離出來,簡化原有類的邏輯。經過這樣兩個案例,咱們能夠總結出來,其實抽象的裝飾者是無關緊要的,具體能夠根據業務模型來選擇。
裝飾者和適配器模式都是包裝模式(Wrapper Pattern),裝飾者也是一種特殊的代理模式。
裝飾者模式 適配器模式 形式 是一種很是特別的適配器模式 沒有層級關係, 裝飾器模式有層級關係 定義 裝飾者和被裝飾者都實現同一個接口, 適配器和被適配者沒有必然的聯繫,通 常是採用繼承或代理的形式進行包裝 主要目的是爲了擴展以後依舊保 留 OOP 關係 關係 知足 is-a 的關係 知足 has-a 的關係 功能 注重覆蓋、擴展 注重兼容、轉換 設計 前置考慮 後置考慮
一、裝飾者是繼承的有力補充,比繼承靈活,不改變原有對象的狀況下動態地給一個對象擴展功能,即插即用。
二、經過使用不一樣裝飾類以及這些裝飾類的排列組合,能夠實現不一樣效果。
三、裝飾者徹底遵照開閉原則。
一、會出現更多的代碼,更多的類,增長程序複雜性。
二、動態裝飾時,多層裝飾時會更復雜。
觀察者模式(Observer Pattern)定義了對象之間的一對多依賴,讓多個觀察者對象同 時監聽一個主體對象,當主體對象發生變化時,它的全部依賴者(觀察者)都會收到通 知並更新,屬於行爲型模式。觀察者模式有時也叫作發佈訂閱模式。觀察者模式主要用 於在關聯行爲之間創建一套觸發機制的場景。
小夥伴們確定都刷過抖音,遇到喜歡的做品都會點個❥喜歡,咱們經過模擬喜歡這個事件來實踐下觀察者模式。當你點擊喜歡時,會觸發兩個事件,一個喜歡數量會增長+1,二是做者會受到消息。你可能會想到MQ,異步隊列等,其實JDK自己就提供這樣的API。
建立事件模型類,用於區分什麼樣的事件,觀察者能夠根據不一樣事件類型作不一樣處理:
package com.study.demo4; /** * 事件模型 * @Author wangzhongyuan * @Date 2020/7/21 11:28 * @Version 1.0 **/ public enum EventModel { LIKE_EVENT("喜歡事件",1,null), MCOMENT_ENVET("評論事件",2,null) ; private String message; private int type; private Object date; EventModel(String message, int type, Object date) { this.message = message; this.type = type; this.date = date; }}
建立事件類,被觀察者對象,調用方能夠向該對象中傳送事件:
package com.study.demo4; import java.util.Observable; /** * 事件總線(被觀察者) * @Author wangzhongyuan * @Date 2020/7/21 11:24 * @Version 1.0 **/ public class EventBus extends Observable{ /** * 被觀察者方法 * @param eventModel */ public void postEvent(EventModel eventModel){ System.out.println("推送事件"); setChanged(); //通知觀察者 notifyObservers(eventModel); } }
建立不一樣業務的觀察者:
package com.study.demo4; import java.util.Observable; import java.util.Observer; /** * 觀察者1 * * @Author 19054253 * @Date 2020/7/21 11:32 * @Version 1.0 **/ public class OneObserver implements Observer { public void update(Observable o, Object arg) { System.out.println("觀察1,監聽到了事件,觸發:給做者推送消息!"); } }
package com.study.demo4; import java.util.Observable; import java.util.Observer; /** * 觀察者2 * * @Author 19054253 * @Date 2020/7/21 11:48 * @Version 1.0 **/ public class TwoObserver implements Observer { public void update(Observable o, Object arg) { System.out.println("觀察2,監聽到了事件,觸發:喜歡總數+1"); } }
調用測試:
package com.study.demo4; /** * @Author wangzhongyuan * @Date 2020/7/21 11:35 * @Version 1.0 **/ public class DemoTest { public static void main(String[] args) { //被觀察者 EventBus eventBus = new EventBus(); //觀察者 OneObserver oneObserver = new OneObserver(); TwoObserver twoObserver = new TwoObserver(); //監聽觀察者 eventBus.addObserver(oneObserver); eventBus.addObserver(twoObserver); //被觀察者觸發事件 eventBus.postEvent(EventModel.LIKE_EVENT); } }
輸出結果:
從上面代碼能夠看出來,觀察者模式的本質就是,被觀察者對象持有觀察者對象的引用,由被觀察者去通知了觀察者去作了某件事。JDK源碼中,觀察者模式也應用很是多。例如java.awt.Event就是觀察者模式的一種,只不過Java不多被用來寫桌面程序。以上就是我模擬的事件機制。
一、觀察者和被觀察者之間創建了一個抽象的耦合。
二、觀察者模式支持廣播通訊。
一、觀察者之間有過多的細節依賴、提升時間消耗及程序的複雜度。 二、使用要得當,要避免循環調用。