原文連接:https://www.cnblogs.com/qiuyong/p/6357839.html
橋接模式:探索JDBC的接口
目錄概要
場景問題
假設要設計一個電腦商場管理系統的某個模塊設計,電腦分爲品牌和類型兩個緯度,我們應該怎麼解決?我們初學者最容易想到的辦法就是利用繼承的方式,那利用繼承實現的類圖又是什麼樣子呢?我們看圖。
代碼展示
package com.aaron.bridge; public interface Computer { public void sale(); } class Desktop implements Computer{ @Override public void sale() { System.out.println("臺式電腦"); } } class Laptop implements Computer{ @Override public void sale() { System.out.println("筆記本電腦"); } } class Pad implements Computer{ @Override public void sale() { System.out.println("平板電腦"); } }
宏碁品牌的三種類型
package com.aaron.bridge; public class AcerDesktop extends Desktop{ @Override public void sale() { System.out.println("宏碁臺式機"); } } class AcerLaptop extends Laptop{ @Override public void sale() { System.out.println("宏碁筆記本電腦"); } } class AcerPad extends Pad{ @Override public void sale() { System.out.println("宏碁平板電腦"); } }
蘋果品牌的三種類型
package com.aaron.bridge; public class AppleDesktop extends Desktop{ @Override public void sale() { System.out.println("蘋果臺式機"); } } class AppleLaptop extends Laptop{ @Override public void sale() { System.out.println("蘋果筆記本電腦"); } } class ApplePad extends Pad{ @Override public void sale() { System.out.println("蘋果平板電腦"); } }
戴爾品牌的三種類型
package com.aaron.bridge; public class DellDesktop extends Desktop{ @Override public void sale() { System.out.println("戴爾臺式機"); } } class DellLaptop extends Laptop{ @Override public void sale() { System.out.println("戴爾筆記本電腦"); } } class DellPad extends Pad{ @Override public void sale() { System.out.println("戴爾平板電腦"); } }
思考:假設我們的系統按照上述思路設計。當我們新增一個品牌的時候,怎麼辦?當然分別要在臺式機、筆記本電腦、平板電腦下又添加三個類就搞定。當我們新增一個機器類型的時候又怎麼辦?當然分別在機器類型又把所有的品牌再添加一次。這樣解決起來感覺也不過如此也不難嘛?但是,假設我們電腦商城,添加10個、100個品牌,又添加10種機型,與此同時這個系統時已經開發完畢,只是在維護的系統,那麼我們怎麼辦?這樣人工代碼會超級痛苦,假設又有很多這樣的問題?那麼我們豈不是變成無腦碼農了?
問題:違背單一職責原則(一個類只由一個維度影響)、開閉原則(對拓展開放,對修改關閉)。
解決方案
1、什麼是橋接模式?
將兩個維度(抽象、實現)分離,使它們都可以獨立地變化。
2、橋接模式的分析
類型:臺式機、筆記本電腦、平板電腦。
品牌:宏碁、蘋果、戴爾。
將上述問題分爲兩個緯度類型和品牌。我們剛剛也討論了,就是因爲拓展性問題,當添加一個新的類型或者品牌的時候,不會對其他類型或者品牌產生影響。只要解決這個問題,那麼就大功告成。那我們就從橋接模式的類圖開始理解。
Abstraction:
抽象部分的接口。通常在這個對象裏面,要維護一個實現部分的對象引用,在抽象對象裏面的方法,需要調用實現部分的對象來完成。這個對象裏面的方法,通常都是跟具體的業務相關的方法。
RefinedAbstraction:
擴展抽象部分的接口,通常在這些對象裏面,定義跟實際業務相關的方法,這些方法的實現通常會使用Abstraction中定義的方法,也可能需要調用實現部分的對象來完成。
Implementor:
定義實現部分的接口,這個接口不用和Abstraction裏面的方法一致(根據約定優於配置原則,建議跟Abstraction一致。),通常是由Implementor接口提供基本的操作,而Abstraction裏面定義的是基於這些基本操作的業務方法,也就是說Abstraction定義了基於這些基本操作的較高層次的操作。
ConcreteImplementor:
真正實現Implementor接口的對象。
3、橋接模式代碼實現
(1)實現部分接口
package com.aaron.bridge; /** * * @author xiaoyongAaron * 品牌緯度-實現部分接口 */ public interface ImplementorBrand { public void sale(); } class ConcreteImplementorAcer implements ImplementorBrand{ @Override public void sale() { System.out.println("宏碁品牌"); } } class ConcreteImplementorApple implements ImplementorBrand{ @Override public void sale() { System.out.println("蘋果品牌"); } } class ConcreteImplementorDell implements ImplementorBrand{ @Override public void sale() { System.out.println("戴爾品牌"); } }
(2)抽象部分
package com.aaron.bridge; /** * 機器類型緯度-抽象部分 * @author xiaoyongAaron */ public class AbstractionComputer { protected ImplementorBrand brand; public AbstractionComputer(ImplementorBrand brand){ this.brand=brand; } public void sale(){ brand.sale(); }; } /** * 擴展部分 * @author xiaoyongAaron */ class RefinedAbstractionDesktop extends AbstractionComputer{ public RefinedAbstractionDesktop(ImplementorBrand brand) { super(brand); } @Override public void sale() { //添加自己的特性,實際業務。 paly(); super.sale(); } public void paly(){ System.out.println("我臺式機抗摔打"); } } class RefinedAbstractionLaptop extends AbstractionComputer{ public RefinedAbstractionLaptop(ImplementorBrand brand) { super(brand); } @Override public void sale() { //添加自己的特性,實際業務。 lighting(); super.sale(); } public void lighting(){ System.out.println("我筆記本電腦充電5分鐘,續航5小時"); } } class RefinedAbstractionPad extends AbstractionComputer{ public RefinedAbstractionPad(ImplementorBrand brand) { super(brand); } @Override public void sale() { //添加自己的特性,實際業務。 weighting(); super.sale(); } public void weighting(){ System.out.println("我平板電腦便於攜帶"); } }
(3)客戶端調用
package com.aaron.bridge; public class Clent { public static void main(String[] args) { //品牌緯度-實現部分接口 ImplementorBrand brand=new ConcreteImplementorApple(); //機器類型緯度 AbstractionComputer computer=new RefinedAbstractionLaptop(brand); computer.sale(); } }
(4)運行結果
問題回顧
1、什麼是橋接模式、爲什麼要橋接?
簡單說橋接模式就是把兩個緯度分離,所以說當我們在實際開發的時候,遇到兩個維度問題的時候,直接條件反射橋接模式。就像上述問題,當有兩個維度(品牌+機器類型)賦予給一個類的時候,基於單一職責原則,需要把它們解耦。那通過上述範例可知,那麼我們就需要一座橋一樣,把兩個緯度用一箇中間物(類或者接口)把它們關聯起來,從而達到我們的目的。
2、橋接模式怎麼接?
核心:如何把Implementor對象傳遞到抽象接口。
(1)如上述描述,利用構造函數傳參。
(2)創造無參構造函數,添加get、set方法。
(3)工廠模式:參考設計模式的工廠模式。
(4)IOC控制反轉,最經典的就是Spring容器。內部的實現原理原本創建對象都是由我們自己管理,但是把這一步驟交給容器管理,就不用我們擔心了。例如在JDBC的設計當中,充當這個角色的就是DriverManage去把對象注入在抽象接口當中。
3、橋接模式本質和經驗
(1)本質:抽象與實現(兩個緯度)分離。
(2)多用對象組合(has-A),少用繼承。
(3)開閉原則:我們應該對代碼拓展開放,拒絕代碼修改。
實際應用場景
參考:https://www.oschina.net/question/1435262_140274