1. 程式人生 > 實用技巧 >【java設計模式】java 橋接模式

【java設計模式】java 橋接模式

java橋接模式

生活中的一個例子:
拿汽車在路上行駛的來說。既有小汽車又有公共汽車,它們都不但能在市區中的公路上行駛,也能在高速公路上行駛。這你會發現,對於交通工具(汽車)有不同的型別,它們所行駛的環境(路)也有不同型別,在軟體系統中就要適應兩個方面(不同車型,不同道路)的變化,怎樣實現才能應對這種變化呢?

概述:
在軟體系統中,某些型別由於自身的邏輯,它具有兩個或多個維度的變化,那麼如何應對這種“多維度的變化”?如何利用面嚮物件的技術來使得該型別能夠輕鬆的沿著多個方向進行變化,而又不引入額外的複雜度?這就要使用Bridge模式。
意圖:
   將抽象部分與實現部分分離,使它們都可以獨立的變化。
——《設計模式》GOF

舉一個生活中的例子:傳送訊息

08143452_BN93.jpg

現在出現問題的根本原因,就在於訊息的抽象和實現是混雜在一起的,這就導致了,一個緯度的變化,會引起另一個緯度進行相應的變化,從而使得程式擴充套件起來非常困難。

要想解決這個問題,就必須把這兩個緯度分開,也就是將抽象部分和實現部分分開,讓它們相互獨立,這樣就可以實現獨立的變化,使擴充套件變得簡單。

橋接模式通過引入實現的介面,把實現部分從系統中分離出去;那麼,抽象這邊如何使用具體的實現呢?肯定是面向實現的介面來程式設計了,為了讓抽象這邊能夠很方便的與實現結合起來,把頂層的抽象介面改成抽象類,在裡面持有一個具體的實現部分的例項。

這樣一來,對於需要傳送訊息的客戶端而言,就只需要建立相應的訊息物件,然後呼叫這個訊息物件的方法就可以了,這個訊息物件會呼叫持有的真正的訊息傳送方式來把訊息傳送出去。也就是說客戶端只是想要傳送訊息而已,並不想關心具體如何傳送。

24.2.2 模式結構和說明

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

橋接模式的結構示意圖

Abstraction

抽象部分的介面。通常在這個物件裡面,要維護一個實現部分的物件引用,在抽象物件裡面的方法,需要呼叫實現部分的物件來完成。這個物件裡面的方法,通常都是跟具體的業務相關的方法。

RefinedAbstraction

擴充套件抽象部分的介面,通常在這些物件裡面,定義跟實際業務相關的方法,這些方法的實現通常會使用Abstraction中定義的方法,也可能需要呼叫實現部分的物件來完成。

Implementor

定義實現部分的介面,這個介面不用和Abstraction裡面的方法一致,通常是由Implementor介面提供基本的操作,而Abstraction裡面定義的是基於這些基本操作的業務方法,也就是說Abstraction定義了基於這些基本操作的較高層次的操作。

ConcreteImplementor

真正實現Implementor介面的物件。

24.2.3 橋接模式示例程式碼

(1)先看看Implementor介面的定義,示例程式碼如下:


/**

* 定義實現部分的介面,可以與抽象部分介面的方法不一樣

*/

public interface Implementor {

/**

* 示例方法,實現抽象部分需要的某些具體功能

*/

public void operationImpl();

}

(2)再看看Abstraction介面的定義,注意一點,雖然說是介面定義,但其實是實現成為抽象類。示例程式碼如下:

/**

* 定義抽象部分的介面

*/

publicabstract 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:從簡單功能開始

從相對簡單的功能開始,先實現普通訊息和加急訊息的功能,傳送方式先實現站內短訊息和Email這兩種。

使用橋接模式來實現這些功能的程式結構如圖24.7所示

圖24.7 使用橋接模式來實現簡單功能示例的程式結構示意圖

(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:新增功能

看了上面的實現,發現使用橋接模式來實現也不是很困難啊,關鍵得看是否能解決前面提出的問題,那就來新增還未實現的功能看看,新增對特急訊息的處理,同時新增一個使用手機發送訊息的方式。該怎麼實現呢?

很簡單,只需要在抽象部分再新增一個特急訊息的類,擴充套件抽象訊息就可以把特急訊息的處理功能加入到系統中了;對於新增手機發送訊息的方式也很簡單,在實現部分新增加一個實現類,實現用手機發送訊息的方式,也就可以了。

這麼簡單?好像看起來完全沒有了前面所提到的問題。的確如此,採用橋接模式來實現過後,抽象部分和實現部分分離開了,可以相互獨立的變化,而不會相互影響。因此在抽象部分新增新的訊息處理,對傳送訊息的實現部分是沒有影響的;反過來增加發送訊息的方式,對訊息處理部分也是沒有影響的。

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("請喝一杯茶", "小李");

}

}

執行結果如下:

使用站內短訊息的方式,傳送訊息'請喝一杯茶'給小李

使用站內短訊息的方式,傳送訊息'加急:請喝一杯茶'給小李

使用站內短訊息的方式,傳送訊息'特急:請喝一杯茶'給小李

使用手機短訊息的方式,傳送訊息'請喝一杯茶'給小李

使用手機短訊息的方式,傳送訊息'加急:請喝一杯茶'給小李

使用手機短訊息的方式,傳送訊息'特急:請喝一杯茶'給小李

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

轉載於:https://blog.51cto.com/craftsman001/1663966