文章目錄
設計模式-橋接模式
1. 案例引出橋接模式
現在對不同手機類型的不同品牌實現操作編程(比如:開機、關機、上網,打電話等);
傳統方案解決手機操作問題分析
擴展性問題( 類爆炸)
,如果我們再增加手機的樣式(全面屏),就需要增加各個品牌手機的類,同樣如果我們增加一個手機品牌,也要在各個手機樣式類下增加。- 違反了單一職責原則,當我們增加手機樣式時,要同時增加所有品牌的手機,這樣增加了代碼維護成本。
- 解決方案–就是設計模式中的
橋接模式
。
2. 橋接模式
2.1 橋接模式基本介紹
橋接模式(Bridge 模式)是指:將實現與抽象放在兩個不同的類層次中,使兩個層次可以獨立改變。
- 是一種
結構型設計模式。
- Bridge 模式基於
類的最小設計原則
,通過使用封裝、聚合及繼承等行爲讓不同的類承擔不同的職責。它的主要特點是把抽象(Abstraction)與行爲實現(Implementation)分離開來,從而可以保持各部分的獨立性以及應對他們的功能擴展。
2.2 橋接模式原理類圖
Client 類:
橋接模式的調用者抽象類(Abstraction) :
充當橋接類RefinedAbstraction類 : 是
Abstraction 抽象類的子類Implementor接口
: 行爲實現類的接口ConcreteImplementorA/B類
:行爲的具體實現類從 UML 圖:這裏的抽象類和接口是聚合的關係,其實調用和被調用關係
,重點是抽象類Abstraction,其維護了 Implementor接口(即它的實現類 ConcreteImplementorA/B)
, 抽象類Abstraction與接口Implementor是聚合關係。
2.3 橋接模式解決手機操作問題
使用橋接模式改進傳統方式,讓程序具有搞好的擴展性,利用程序維護。
2.4 代碼實現之
抽象類Phone及其子類
注意Phone抽象類是怎麼與Brand接口進行關聯的。
// 抽象類Phone
public abstract class Phone {
//組合品牌
private Brand brand;
//構造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
// 直立樣式手機
public class UpRightPhone extends Phone {
//構造器
public UpRightPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 直立樣式手機 ");
}
public void close() {
super.close();
System.out.println(" 直立樣式手機 ");
}
public void call() {
super.call();
System.out.println(" 直立樣式手機 ");
}
}
//摺疊式手機類,繼承 抽象類 Phone
public class FoldedPhone extends Phone {
//構造器
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 摺疊樣式手機 ");
}
public void close() {
super.close();
System.out.println(" 摺疊樣式手機 ");
}
public void call() {
super.call();
System.out.println(" 摺疊樣式手機 ");
}
}
接口Brand及其子類
//接口
public interface Brand {
void open();
void close();
void call();
}
// Vivo類
public class Vivo implements Brand {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println(" Vivo手機開機 ");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println(" Vivo手機關機 ");
}
@Override
public void call() {
// TODO Auto-generated method stub
System.out.println(" Vivo手機打電話 ");
}
}
// XiaoMi類
public class XiaoMi implements Brand {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println(" 小米手機開機 ");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println(" 小米手機關機 ");
}
@Override
public void call() {
// TODO Auto-generated method stub
System.out.println(" 小米手機打電話 ");
}
}
調用端Client測試
public class Client {
public static void main(String[] args) {
//獲取摺疊式手機 (樣式 + 品牌 )
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("=======================");
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
// 所有的直立式手機
Phone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("=======================");
Phone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
- 通過代碼是不是可以領會到橋接模式的力量?
- 我們如果增加手機的品牌
HUAWEI
,我們只需要讓其實現Brand類重寫方法就可以了;- 如果我們想增加全面屏手機
FullScreenPhone
類型,我們只需要讓其繼承Phone類然後重寫其方法就行了;
2.5 橋接模式在 JDBC 的源碼應用
使用JDBC的時候,你是否一直很困惑獲取connection的過程和原理,爲什麼把不同的數據庫驅動名稱放到
Class.forName("com.mysql.jdbc.Driver")
中就能獲取到對應的數據庫連接呢?
Jdbc 的 Driver 接口,如果從橋接模式來看,Driver 就是一個接口,下面可以有 MySQL 的 Driver,Oracle 的Driver,這些就可以當做實現接口類。
通過Class.forName("com.mysql.jdbc.Driver")類加載的時候執行靜態代碼塊將Driver註冊到DriverManager中的registeredDrivers中,DriverManager中的registeredDrivers是個Driver容器,管理不同的Driver,這樣具體的數據Driver實現就統一交給容器管理,客戶端通過DriverManager執行驗證連接,獲取連接的操作。
下面是部分源碼:
public class DriverManager {
// 已註冊的JDBC驅動程序列表
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* 如果驅動程序還沒有添加到我們的列表中,請註冊它 */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// 這是爲了與原始驅動管理器兼容
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
// ...
}
class DriverInfo {
final Driver driver;
DriverAction da;
DriverInfo(Driver driver, DriverAction action) {
this.driver = driver;
da = action;
}
@Override
public boolean equals(Object other) {
return (other instanceof DriverInfo)
&& this.driver == ((DriverInfo) other).driver;
}
@Override
public int hashCode() {
return driver.hashCode();
}
@Override
public String toString() {
return ("driver[className=" + driver + "]");
}
DriverAction action() {
return da;
}
}
3. 橋接模式的細節☆
實現了抽象和實現部分的分離,從而極大的提供了系統的靈活性,讓抽象部分和實現部分獨立開來,這有助於系統進行分層設計,從而產生更好的結構化系統。
對於系統的高層部分,只需要知道抽象部分和實現部分的接口就可以了,其它的部分由具體業務來完成。
橋接模式替代多層繼承方案,可以減少子類的個數,降低系統的管理和維護成本。
- 橋接模式的引入增加了系統的理解和設計難度,由於聚合關聯關係建立在抽象層,要求開發者針對抽象進行設計和編程。
橋接模式要求正確識別出系統中兩個獨立變化的維度( 抽象、和實現),因此其使用範圍有一定的侷限性,即需要有這樣的應用場景。
對於那些不希望使用繼承或因爲多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤爲適用。
- 常見的應用場景
- JDBC 驅動程序
- 銀行轉賬系統
- 轉賬分類: 網上轉賬,櫃檯轉賬,AMT 轉賬
- 轉賬用戶類型:普通用戶,銀卡用戶,金卡用戶…
- 消息管理
- 消息類型:即時消息,延時消息
- 消息分類:手機短信,郵件消息,