JAVA設計模式之 橋接模式【Bridge Pattern】

小呂-ICE發表於2015-03-06

一、概述
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種物件結構型模式,又稱為柄體(Handle and Body)模式。

二、適用場景
處理多維度變化。
業務場景:某功能為將資料庫中的資料轉換成多種檔案格式,例如txt、xml、pdf等格式,同時需要支援多種不同型別的資料庫的讀取。便可使用橋接模式對其進行設計。
這裡的維度有兩個 分別為:不同的檔案格式 和 不同的資料庫型別。如下圖:
這裡寫圖片描述

三、UML類圖
橋接模式通用結構圖:
這裡寫圖片描述
針對上面的業務場景-橋接模式-示例結構圖:
這裡寫圖片描述

四、參與者
①Abstraction(抽象類):用於定義抽象類的介面,它一般是抽象類而不是介面,其中定義了一個Implementor(實現類介面)型別的物件並可以維護該物件,它與Implementor之間具有關聯關係,它既可以包含抽象業務方法,也可以包含具體業務方法。
②RefinedAbstraction(擴充抽象類):擴充由Abstraction定義的介面,通常情況下它不再是抽象類而是具體類,它實現了在Abstraction中宣告的抽象業務方法,在RefinedAbstraction中可以呼叫在Implementor中定義的業務方法。
③Implementor(實現類介面):定義實現類的介面,這個介面不一定要與Abstraction的介面完全一致,事實上這兩個介面可以完全不同,一般而言,Implementor介面僅提供基本操作,而Abstraction定義的介面可能會做更多更復雜的操作。Implementor介面對這些基本操作進行了宣告,而具體實現交給其子類。通過關聯關係,在Abstraction中不僅擁有自己的方法,還可以呼叫到Implementor中定義的方法,使用關聯關係來替代繼承關係。
④ConcreteImplementor(具體實現類):具體實現Implementor介面,在不同的ConcreteImplementor中提供基本操作的不同實現,在程式執行時,ConcreteImplementor物件將替換其父類物件,提供給抽象類具體的業務操作方法。

五、用例學習<以適用場景裡的業務場景作為程式碼設計>
1、JDBC 驅動連線管理類:JdbcDriverManager.java

/**
 * JDBC 驅動連線管理類
 * @author lvzb.software@qq.com
 *
 */
public class JdbcDriverManager {

    public String connectAndReadOracle(){
        // 模擬連線Oracle資料庫的程式碼
        System.out.println("已成功連線到Oracle資料庫");
        // 模式 省略 從資料庫中獲取內容的程式碼
        String content = "已成功從Oracle資料庫中讀取到了內容";
        return content;
    }

    public String connectAndReadMySql(){
        // 模擬連線MySql資料庫的程式碼
        System.out.println("已成功連線到MySql資料庫");
        // 模式 省略 從資料庫中獲取內容的程式碼
        String content = "已成功從MySql資料庫中讀取到了內容";
        return content;
    }

    public String connectAndReadSqlServer(){
        // 模擬連線Sql Server資料庫的程式碼
        System.out.println("已成功連線到Sql Server資料庫");
        // 模式 省略 從資料庫中獲取內容的程式碼
        String content = "已成功從Sql Server資料庫中讀取到了內容";
        return content;
    }
}

2、<角色:實現類介面> FileExportImpl.java

/**
 * 獲取檔案內容、連線資料庫來源介面
 * @author lvzb.software@qq.com
 *
 */
public interface FileExportImpl {
    /**
     * 讀取資料庫中的內容
     * @param jdbcDriver
     * @return
     */
    public String readContent();
}

3、<角色:具體實現類> FileExportFromOracle.java

/**
 * 從Oracle資料庫獲取內容
 * @author lvzb.software@qq.com
 *
 */
public class FileExportFromOracle implements FileExportImpl {

    @Override
    public String readContent() {
        JdbcDriverManager jdbcDriver = new JdbcDriverManager();
        return jdbcDriver.connectAndReadOracle();
    }

}

4、<角色:具體實現類> FileExportFromMySql.java

/**
 * 從MySql資料庫獲取內容
 * @author lvzb.software@qq.com
 *
 */
public class FileExportFromMySql implements FileExportImpl {

    @Override
    public String readContent() {
        JdbcDriverManager jdbcDriver = new JdbcDriverManager();
        return jdbcDriver.connectAndReadMySql();
    }

}

5、<角色:具體實現類> FileExportFromSqlServer.java

/**
 * 從Sql Server資料庫獲取內容
 * @author lvzb.software@qq.com
 *
 */
public class FileExportFromSqlServer implements FileExportImpl {

    @Override
    public String readContent() {
        JdbcDriverManager jdbcDriver = new JdbcDriverManager();
        return jdbcDriver.connectAndReadSqlServer();
    }

}

6、<角色:抽象類> FileExportAbstraction.java

/**
 * 檔案格式匯出 抽象類
 * @author lvzb.software@qq.com
 *
 */
public abstract class FileExportAbstraction {

    protected FileExportImpl fileSouce;

    public void setFileSource(FileExportImpl fileSouce){
        this.fileSouce = fileSouce;
    }

    public abstract void exportFile();

}

7、<角色:擴充抽象類> TxtFileExport.java

/**
 * Txt檔案格式匯出具體類
 * @author lvzb.software@qq.com
 *
 */
public class TxtFileExport extends FileExportAbstraction {

    @Override
    public void exportFile() {
        String readContent = fileSouce.readContent();
        System.out.println(readContent + ",將內容匯出為.txt格式");
    }

}

8、<角色:擴充抽象類> XmlFileExport.java

/**
 * xml檔案格式匯出具體類
 * @author  lvzb.software@qq.com
 *
 */
public class XmlFileExport extends FileExportAbstraction {

    @Override
    public void exportFile() {
        String readContent = fileSouce.readContent();
        System.out.println(readContent + ",將內容匯出為.xml格式");
    }
}

9、<角色:擴充抽象類> PdfFileExport.java

/**
 * pdf檔案格式匯出具體類
 * @author  lvzb.software@qq.com
 *
 */
public class PdfFileExport extends FileExportAbstraction {

    @Override
    public void exportFile() {
        String readContent = fileSouce.readContent();
        System.out.println(readContent + ",將內容匯出為.pdf格式");
    }
}

10、客戶端測試類: Client.java

public class Client {

    public static void main(String[] args) {
        FileExportImpl fileOracle = new FileExportFromOracle();
        FileExportImpl fileMySql = new FileExportFromMySql();
        FileExportImpl fileSqlServer = new FileExportFromSqlServer();

        FileExportAbstraction fileTxtExport = new TxtFileExport();
        FileExportAbstraction fileXmlExport = new XmlFileExport();
        FileExportAbstraction filePdfExport = new PdfFileExport();

        // 如果我們要從Oracle中匯出xml格式的資料
        fileXmlExport.setFileSource(fileOracle);
        fileXmlExport.exportFile();

        System.out.println("--------------------\n");
        // 如果我們要從Oracle中匯出txt格式的資料
        fileTxtExport.setFileSource(fileOracle);
        fileTxtExport.exportFile();

        System.out.println("--------------------\n");
        // 如果我們要從MySql中匯出pdf格式的資料
        filePdfExport.setFileSource(fileMySql);
        filePdfExport.exportFile();
    }

}

11、執行結果 如下:

已成功連線到Oracle資料庫
已成功從Oracle資料庫中讀取到了內容,將內容匯出為.xml格式
--------------------

已成功連線到Oracle資料庫
已成功從Oracle資料庫中讀取到了內容,將內容匯出為.txt格式
--------------------

已成功連線到MySql資料庫
已成功從MySql資料庫中讀取到了內容,將內容匯出為.pdf格式

12、後續系統擴充套件
①、現如果需求要求新增加一種檔案輸入格式 如:html格式
則只需新寫一個FileExportAbstraction抽象類的子類 用以輸出html格式即可,而不需要修改其他任何類、不需要修改另一維度的程式碼。
②、現如果需求要求新增加一型別資料庫型別 如:Sybase
則只需新寫一個FileExportImpl介面的實現類、用以從Sybase資料庫中獲取內容、並修改JdbcDriverManager.java類、新增對Sybase資料庫的連線和訪問。

由此可見 在兩個變化維度中任意擴充套件一個維度,都不需要修改原有系統、提高了系統的擴充套件性和可維護性。

六、其他
主要優點:
(1)分離抽象介面及其實現部分。橋接模式使用“物件間的關聯關係”解耦了抽象和實現之間固有的繫結關係,使得抽象和實現可以沿著各自的維度來變化。所謂抽象和實現沿著各自維度的變化,也就是說抽象和實現不再在同一個繼承層次結構中,而是“子類化”它們,使它們各自都具有自己的子類,以便任何組合子類,從而獲得多維度組合物件。
(2)在很多情況下,橋接模式可以取代多層繼承方案,多層繼承方案違背了“單一職責原則”,複用性較差,且類的個數非常多,橋接模式是比多層繼承方案更好的解決方法,它極大減少了子類的個數。
(3)橋接模式提高了系統的可擴充套件性,在兩個變化維度中任意擴充套件一個維度,都不需要修改原有系統,符合“開閉原則”。

相關文章