橋接模式

1.1橋接模式

 (1)橋接模式定義
    將抽象部分與它的實現部分分離,使它們均可以獨立地變化。
html

 (2)應用橋接模式來解決的思路
    仔細分析上面的示例,根據示例的功能要求,示例的變化具備兩個緯度,一個緯度是抽象的消息這邊,包括普通消息、加急消息和特急消息,這幾個抽象的消息自己就具備必定的關係,加急消息和特急消息會擴展普通消息;另外一個緯度在具體的消息發送方式上,包括站內短消息、Email和手機短信息,這幾個方式是平等的,可被切換的方式。這兩個緯度一共能夠組合出9種不一樣的可能性來,它們的關係以下圖所示:
java

wKioL1liJfTjMkRwAABsbSZ4eys752.png

 如今出現問題的根本緣由,就在於消息的抽象和實現是混雜在一塊兒的,這就致使了,一個緯度的變化,會引發另外一個緯度進行相應的變化,從而使得程序擴展起來很是困難。
   要想解決這個問題,就必須把這兩個緯度分開,也就是將抽象部分和實現部分分開,讓它們相互獨立,這樣就能夠實現獨立的變化,使擴展變得簡單。
   橋接模式經過引入實現的接口,把實現部分從系統中分離出去;那麼,抽象這邊如何使用具體的實現呢?確定是面向實現的接口來編程了,爲了讓抽象這邊可以很方便的與實現結合起來,把頂層的抽象接口改爲抽象類,在裏面持有一個具體的實現部分的實例。
   這樣一來,對於須要發送消息的客戶端而言,就只須要建立相應的消息對象,而後調用這個消息對象的方法就能夠了,這個消息對象會調用持有的真正的消息發送方式來把消息發送出去。也就是說客戶端只是想要發送消息而已,並不想關心具體如何發送。算法

1.2  模式結構和說明

    橋接模式的結構如圖所示:編程

wKioL1liJx3BugEQAAEM0U4KVpk807.png

Abstraction:
  抽象部分的接口。一般在這個對象裏面,要維護一個實現部分的對象引用,在抽象對象裏面的方法,須要調用實現部分的對象來完成。這個對象裏面的方法,一般都是跟具體的業務相關的方法。
RefinedAbstraction:
設計模式

  擴展抽象部分的接口,一般在這些對象裏面,定義跟實際業務相關的方法,這些方法的實現一般會使用Abstraction中定義的方法,也可能須要調用實現部分的對象來完成。
Implementor:
ide

  定義實現部分的接口,這個接口不用和Abstraction裏面的方法一致,一般是由Implementor接口提供基本的操做,而Abstraction裏面定義的是基於這些基本操做的業務方法,也就是說Abstraction定義了基於這些基本操做的較高層次的操做。
ConcreteImplementor:
學習

  真正實現Implementor接口的對象。測試

1.3  橋接模式示例代碼

(1)先看看Implementor接口的定義,示例代碼以下:this

/** 
 * 定義實現部分的接口,能夠與抽象部分接口的方法不同 
 */  
public interface Implementor {  
    /** 
     * 示例方法,實現抽象部分須要的某些具體功能 
     */  
    public void operationImpl();  
}

(2)再看看Abstraction接口的定義,注意一點,雖說是接口定義,但實際上是實現成爲抽象類。示例代碼以下:spa

/** 
 * 定義抽象部分的接口 
 */  
public abstract class Abstraction {  
    /** 
     * 持有一個實現部分的對象 
     */  
    protected Implementor impl;  
    /** 
     * 構造方法,傳入實現部分的對象  
     * @param impl 實現部分的對象 
     */  
    public Abstraction(Implementor impl){  
        this.impl = impl;  
    }  
    /** 
     * 示例操做,實現必定的功能,可能須要轉調實現部分的具體實現方法 
     */  
    public void operation() {  
        impl.operationImpl();  
    }  
}

(3)該來看看具體的實現了,示例代碼以下:

/** 
 * 真正的具體實現對象 
 */  
public class ConcreteImplementorA implements Implementor {  
    public void operationImpl() {   
        //真正的實現  
    }  
}

另一個實現,示例代碼以下:

/** 
 * 真正的具體實現對象 
 */  
public class ConcreteImplementorB implements Implementor {  
    public void operationImpl() {   
        //真正的實現  
    }  
}

(4)最後來看看擴展Abstraction接口的對象實現,示例代碼以下:

/** 
 * 擴充由Abstraction定義的接口功能 
 */  
public class RefinedAbstraction extends Abstraction {  
    public RefinedAbstraction(Implementor impl) {  
        super(impl);  
    }  
    /** 
     * 示例操做,實現必定的功能 
     */  
    public void otherOperation(){  
        //實現必定的功能,可能會使用具體實現部分的實現方法,  
        //可是本方法更大的多是使用Abstraction中定義的方法,  
        //經過組合使用Abstraction中定義的方法來完成更多的功能  
    }  
}

1.4  使用橋接模式重寫示例

   學習了橋接模式的基礎知識事後,該來使用橋接模式重寫前面的示例了。經過示例,來看看使用橋接模式來實現一樣的功能,是否能解決「既能方便的實現功能,又能有很好的擴展性」的問題。
       要使用橋接模式來從新實現前面的示例,首要任務就是要把抽象部分和實現部分分離出來,分析要實現的功能,抽象部分就是各個消息的類型所對應的功能,而實現部分就是各類發送消息的方式。
        其次要按照橋接模式的結構,給抽象部分和實現部分分別定義接口,而後分別實現它們就能夠了。
1:從簡單功能開始
        從相對簡單的功能開始,先實現普通消息和加急消息的功能,發送方式先實現站內短消息和Email這兩種。 
        使用橋接模式來實現這些功能的程序結構如圖所示:

wKioL1liKfLRk4mXAADPqSdUKmg396.png

(1)先看看實現部分定義的接口,示例代碼以下:

/** 
 * 實現發送消息的統一接口 
 */  
public interface MessageImplementor {  
    /** 
     * 發送消息 
     * @param message 要發送的消息內容 
     * @param toUser 消息發送的目的人員 
     */  
    public void send(String message,String toUser);  
}

(2)再看看抽象部分定義的接口,示例代碼以下:

/** 
 * 抽象的消息對象 
 */  
public abstract class AbstractMessage {  
    /** 
     * 持有一個實現部分的對象 
     */  
    protected MessageImplementor impl;  
    /** 
     * 構造方法,傳入實現部分的對象  
     * @param impl 實現部分的對象 
     */  
    public AbstractMessage(MessageImplementor impl){  
        this.impl = impl;  
    }  
    /** 
     * 發送消息,轉調實現部分的方法 
     * @param message 要發送的消息內容 
     * @param toUser 消息發送的目的人員 
     */  
    public void sendMessage(String message,String toUser){  
        this.impl.send(message, toUser);  
    }     
}

(3)看看如何具體的實現發送消息,先看站內短消息的實現吧,示例代碼以下:

/** 
 * 以站內短消息的方式發送消息 
 */  
public  class MessageSMS implements MessageImplementor{  
    public void send(String message, String toUser) {  
        System.out.println("使用站內短消息的方式,發送消息'"  
+message+"'給"+toUser);  
    }  
}

再看看Email方式的實現,示例代碼以下:

/** 
 * 以Email的方式發送消息 
 */  
public class MessageEmail implements MessageImplementor{  
    public void send(String message, String toUser) {  
        System.out.println("使用Email的方式,發送消息'"  
                               +message+"'給"+toUser);  
    }  
}

(4)接下來該看看如何擴展抽象的消息接口了,先看普通消息的實現,示例代碼以下:

public class CommonMessage extends AbstractMessage{  
    public CommonMessage(MessageImplementor impl) {  
        super(impl);  
    }  
    public void sendMessage(String message, String toUser) {  
        //對於普通消息,什麼都不幹,直接調父類的方法,把消息發送出去就能夠了  
        super.sendMessage(message, toUser);  
    }     
}

再看看加急消息的實現,示例代碼以下:

public class UrgencyMessage extends AbstractMessage{  
    public UrgencyMessage(MessageImplementor impl) {  
        super(impl);  
    }  
    public void sendMessage(String message, String toUser) {  
        message = "加急:"+message;  
        super.sendMessage(message, toUser);  
    }  
    /** 
     * 擴展本身的新功能:監控某消息的處理過程 
     * @param messageId 被監控的消息的編號 
     * @return 包含監控到的數據對象,這裏示意一下,因此用了Object 
     */  
    public Object watch(String messageId) {  
        //獲取相應的數據,組織成監控的數據對象,而後返回         
        return null;  
    }     
}

2:添加功能
  看了上面的實現,發現使用橋接模式來實現也不是很困難啊,關鍵得看是否能解決前面提出的問題,那就來添加還未實現的功能看看,添加對特急消息的處理,同時添加一個使用手機發送消息的方式。該怎麼實現呢?
  很簡單,只須要在抽象部分再添加一個特急消息的類,擴展抽象消息就能夠把特急消息的處理功能加入到系統中了;對於添加手機發送消息的方式也很簡單,在實現部分新增長一個實現類,實現用手機發送消息的方式,也就能夠了。
  這麼簡單?好像看起來徹底沒有了前面所提到的問題。的確如此,採用橋接模式來實現事後,抽象部分和實現部分分離開了,能夠相互獨立的變化,而不會相互影響。所以在抽象部分添加新的消息處理,對發送消息的實現部分是沒有影響的;反過來增長髮送消息的方式,對消息處理部分也是沒有影響的。
(1)接着看看代碼實現,先看看新的特急消息的處理類,示例代碼以下:

public class SpecialUrgencyMessage extends AbstractMessage{  
    public SpecialUrgencyMessage(MessageImplementor impl) {  
        super(impl);  
    }  
    public void hurry(String messageId) {  
        //執行催促的業務,發出催促的信息  
    }  
    public void sendMessage(String message, String toUser) {  
        message = "特急:"+message;  
        super.sendMessage(message, toUser);  
        //還須要增長一條待催促的信息  
    }  
}

(2)再看看使用手機短消息的方式發送消息的實現,示例代碼以下:

/** 
 * 以手機短消息的方式發送消息 
 */  
public  class MessageMobile implements MessageImplementor{  
    public void send(String message, String toUser) {  
        System.out.println("使用手機短消息的方式,發送消息'"  
+message+"'給"+toUser);  
    }  
}

3:測試一下功能

  看了上面的實現,可能會感受獲得,使用橋接模式來實現前面的示例事後,添加新的消息處理,或者是新的消息發送方式是如此簡單,但是這樣實現,好用嗎?寫個客戶端來測試和體會一下,示例代碼以下:

public class Client {  
    public static void main(String[] args) {  
        //建立具體的實現對象  
        MessageImplementor impl = new MessageSMS();  
        //建立一個普通消息對象  
        AbstractMessage m = new CommonMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");         
        //建立一個緊急消息對象  
        m = new UrgencyMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");         
        //建立一個特急消息對象  
        m = new SpecialUrgencyMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");  
          
        //把實現方式切換成手機短消息,而後再實現一遍  
        impl = new MessageMobile();  
        m = new CommonMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");  
        m = new UrgencyMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");  
        m = new SpecialUrgencyMessage(impl);  
        m.sendMessage("請喝一杯茶", "小李");  
    }  
}

運行結果以下:

使用站內短消息的方式,發送消息'請喝一杯茶'給小李  
使用站內短消息的方式,發送消息'加急:請喝一杯茶'給小李  
使用站內短消息的方式,發送消息'特急:請喝一杯茶'給小李  
使用手機短消息的方式,發送消息'請喝一杯茶'給小李  
使用手機短消息的方式,發送消息'加急:請喝一杯茶'給小李  
使用手機短消息的方式,發送消息'特急:請喝一杯茶'給小李

前面三條是使用的站內短消息,後面三條是使用的手機短消息,正確的實現了預期的功能。看來前面的實現應該是正確的,可以完成功能,且能靈活擴展。

1.5  橋接模式的優缺點

  • 分離抽象和實現部分
        橋接模式分離了抽象和實現部分,從而極大地提升了系統的靈活性。讓抽象部分和實現部分獨立開來,分別定義接口,這有助於對系統進行分層,從而產生更好的結構化的系統。對於系統的高層部分,只須要知道抽象部分和實現部分的接口就能夠了。

  • 更好的擴展性
        因爲橋接模式把抽象和實現部分分離開了,並且分別定義接口,這就使得抽象部分和實現部分能夠分別獨立的擴展,而不會相互影響,從而大大的提升了系統的可擴展性。

  • 可動態切換實現
        因爲橋接模式把抽象和實現部分分離開了,那麼在實現橋接的時候,就能夠實現動態的選擇和使用具體的實現,也就是說一個實現再也不是固定的綁定在一個抽象接口上了,能夠實現運行期間動態的切換實現。

    可減小子類的個數
        根據前面的講述,對於有兩個變化緯度的狀況,若是採用繼承的實現方式,大約須要兩個緯度上的可變化數量的乘積個子類;而採用橋接模式來實現的話,大約須要兩個緯度上的可變化數量的和個子類。能夠明顯地減小子類的個數。

     

    1.6  相關模式

  •  橋接模式和策略模式
        這兩個模式有很大的類似之處。
        若是把橋接模式的抽象部分簡化來看,若是暫時不去擴展Abstraction,也就是去掉RefinedAbstraction。能夠體會到橋接模式和策略模式是如此類似。能夠把策略模式的Context視作是使用接口的對象,而Strategy就是某個接口了,具體的策略實現那就至關於接口的具體實現。這樣看來的話,某些狀況下,可使用橋接模式來模擬實現策略模式的功能。
        這兩個模式雖然類似,也仍是有區別的。最主要的是模式的目的不同,策略模式的目的是封裝一系列的算法,使得這些算法能夠相互替換;而橋接模式的目的是分離抽象和實現部分,使得它們能夠獨立的變化。

  • 橋接模式和狀態模式
        因爲從模式結構上看,狀態模式和策略模式是同樣的,這兩個模式的關係也基本上相似於橋接模式和策略模式的關係。
        只不過狀態模式的目的是封裝狀態對應的行爲,並在內部狀態改變的時候改變對象的行爲。

  • 橋接模式和模板方法模式
        這兩個模式有類似之處。
        雖然標準的模板方法模式是採用繼承來實現的,可是模板方法也能夠經過回調接口的方式來實現,若是把接口的實現獨立出去,那就相似於模板方法經過接口去調用具體的實現方法了。這樣的結構就和簡化的橋接模式相似了。
        可使用橋接模式來模擬實現模板方法模式的功能。若是在實現Abstraction對象的時候,在裏面定義方法,方法裏面就是某個固定的算法骨架,也就是說這個方法就至關於模板方法。在模板方法模式裏,是把不能肯定實現的步驟延遲到子類去實現;如今在橋接模式裏面,把不能肯定實現的步驟委託給具體實現部分去完成,經過回調實現部分的接口,來完成算法骨架中的某些步驟。這樣一來,就能夠實現使用橋接模式來模擬實現模板方法模式的功能了。
        使用橋接模式來模擬實現模板方法模式的功能,還有個潛在的好處,就是模板方法也能夠很方便的擴展和變化了。在標準的模板方法裏面,一個問題就是當模板發生變化的時候,全部的子類都要變化,很是不方便。而使用橋接模式來實現相似的功能,就沒有這個問題了。
        另外,這裏只是說從實現具體的業務功能上,橋接模式能夠模擬實現出模板方法模式能實現的功能,並非說橋接模式和模板方法模式就變成同樣的,或者是橋接模式就能夠替換掉模板方法模式了。要注意它們自己的功能、目的、本質思想都是不同的。

  • 橋接模式和抽象工廠模式
        這兩個模式能夠組合使用。
        橋接模式中,抽象部分須要獲取相應的實現部分的接口對象,那麼誰來建立實現部分的具體實現對象呢?這就是抽象工廠模式派上用場的地方。也就是使用抽象工廠模式來建立和配置一個特定的具體實現的對象。
        事實上,抽象工廠主要是用來建立一系列對象的,若是建立的對象不多,或者是很簡單,還能夠採用簡單工廠,能夠達到同樣的效果,可是會比抽象工廠來得簡單。

  • 橋接模式和適配器模式
        這兩個模式能夠組合使用。
        這兩個模式功能是徹底不同的,適配器模式的功能主要是用來幫助無關的類協同工做,重點在解決本來因爲接口不兼容而不能一塊兒工做的那些類,使得它們能夠一塊兒工做。而橋接模式則重點在分離抽象和實現部分。
        因此在使用上,一般在系統設計完成事後,纔會考慮使用適配器模式;而橋接模式,是在系統開始的時候就要考慮使用。
        雖然功能上不同,這兩個模式仍是能夠組合使用的,好比:已有實現部分的接口,可是有些不太適應如今新的功能對接口的須要,徹底拋棄吧,有些功能還用得上,該怎麼辦呢?那就使用適配器來進行適配,使得舊的接口可以適應新的功能的須要。


  • 轉載至:http://sishuok.com/forum/blogPost/list/109.html cc老師的設計模式是我目前看過最詳細最有實踐的教程。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息