設計模式——橋接模式

橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種對象結構型模式,又稱爲柄體(Handle and Body)模式或接口(Interface)模式。

設想如果要繪製矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪製的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:

  1. 爲每一種形狀都提供一套各種顏色的版本。
  2. 根據實際需要對形狀和顏色進行組合

對於有兩個變化維度(即兩個變化的原因)的系統,採用第二種方案來進行設計系統中類的個數更少,且系統擴展更爲方便。第二種方案即是橋接模式的應用。橋接模式將繼承關係轉換爲關聯關係,從而降低了類與類之間的耦合,減少了代碼編寫量。對於有兩個變化維度(即兩個變化的原因)的系統,採用橋接模式開發更爲方便簡潔。橋接模式將繼承關係轉換爲關聯關係,從而降低了類與類之間的耦合,減少了代碼編寫量。

<!--more-->

模式結構

橋接模式包含如下角色:

  • Abstraction:抽象類,橋接類
  • RefinedAbstraction:擴充抽象類
  • Implementor:實現類,被橋接的接口
  • ConcreteImplementor:具體實現類

源碼導讀

JDBC是基於Java支持多種數據庫的操作,但是不同數據庫的自我實現和傳輸協議都不盡相同,難道Java爲每一種數據庫寫一種接口去支持數據庫廠商的實現,顯然違背了精簡設計的原則,這裏Java做的是提供一套接口讓廠商自己實現,一套接口給程序開發者調用,兩者的結合就是經典的橋接模式。作爲程序員操作jdbc是這樣的:

    Class.forName("com.mysql.jdbc.Driver");
    String url = "";
    String user = "";
    String password = "";
    Connection con = DriverManager.getConnection(url, user, password);
    Statement statement = connection.createStatement();
    String sql = "insert into student (name,age) VALUE ('" + name + "'," + age + ")";
    statement.execute(sql);

我們來看看``部分源碼

 private static Connection getConnection(String var0, Properties var1, Class<?> var2) throws SQLException {
        ClassLoader var3 = var2 != null ? var2.getClassLoader() : null;
        Class var4 = DriverManager.class;
        synchronized(DriverManager.class) {
            if (var3 == null) {
                var3 = Thread.currentThread().getContextClassLoader();
            }
        }

        if (var0 == null) {
            throw new SQLException("The url cannot be null", "08001");
        } else {
            println("DriverManager.getConnection(\"" + var0 + "\")");
            SQLException var10 = null;
            Iterator var5 = registeredDrivers.iterator();

            while(true) {
                while(var5.hasNext()) {
                    DriverInfo var6 = (DriverInfo)var5.next();
                    if (isDriverAllowed(var6.driver, var3)) {
                        try {
                            println("    trying " + var6.driver.getClass().getName());
                            Connection var7 = var6.driver.connect(var0, var1);
                            if (var7 != null) {
                                println("getConnection returning " + var6.driver.getClass().getName());
                                return var7;
                            }
                        } catch (SQLException var8) {
                            if (var10 == null) {
                                var10 = var8;
                            }
                        }
                    } else {
                        println("    skipping: " + var6.getClass().getName());
                    }
                }

                if (var10 != null) {
                    println("getConnection failed: " + var10);
                    throw var10;
                }

                println("getConnection: no suitable driver found for " + var0);
                throw new SQLException("No suitable driver found for " + var0, "08001");
            }
        }
    }

看這幾行代碼

 ClassLoader var3 = var2 != null ? var2.getClassLoader() : null;
        Class var4 = DriverManager.class;
        synchronized(DriverManager.class) {
            if (var3 == null) {
                var3 = Thread.currentThread().getContextClassLoader();
            }
        }
     ......
     ......
Connection var7 = var6.driver.connect(var0, var1);

其實這裏DriverManager獲得Connection是通過反射和類加載機制從數據庫驅動包的driver中拿到連接,所以這裏真正參與橋接模式的是driver,而DriverManager和橋接模式沒有關係,DriverManager只是對driver的一個管理器。而我們作爲使用者只去關心Connection,不會去關心driver,因爲我們的操作都是通過操作Connection來實現的。這樣分析下來這個橋接就清晰了邏輯——java.sql.Driver作爲抽象橋類,而驅動包如com.mysql.jdbc.Driver具體的實現橋接類,而Connection是被橋接的對象。這裏的兩個維度是:

  • 數據庫類型的不同(驅動不同)
  • 數據庫的連接信息不同(URL,username,password)

現在假設一個這樣的場景-我們設計了一個框架,需要對外提供api,但是這個框架內部某個類需要頻繁變更,很不穩定,但是我們提供的api不能一直變吧。如何將api的方法和頻繁變更的代碼隔離開呢,其實就可以考慮適配器模式或者橋接模式。

六個核彈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章