設計模式解密(12)- 橋接模式
1、簡介
定義:將抽象部分與實現(xiàn)部分分離,使它們都可以獨立的變化。
主要解決:在多維可能會變化的情況下,用繼承會造成類爆炸問題,擴展起來不靈活。
何時使用:實現(xiàn)系統(tǒng)可能有多個角度分類,每一種角度都可能變化。
如何解決:把這種多角度分類分離出來,讓它們獨立變化,減少它們之間耦合。
注意事項:對于兩個獨立變化的維度,使用橋接模式再適合不過了。
英文:bridge
類型:結構型
2、問題引入
在軟件系統(tǒng)中,某些類型由于自身的邏輯,它具有兩個或多個維度的變化,那么如何應對這種“多維度的變化”?如何利用面向對象的技術來使得該類型能夠輕松的沿著多個方向進行變化,而又不引入額外的復雜度?
先來試想一個情景(OA系統(tǒng)中的消息處理):
消息類型:普通消息,加急消息,特急消息
消息方式:系統(tǒng)內消息,手機短信,郵件
在不使用橋接模式的情況下(即:使用繼承方式),首先我們想到的方案:為每一種消息方式都提供一套消息類型,或者為每一種消息類型都提供一套消息方式;
示意圖如下:
或者:
實現(xiàn)代碼(按第二個圖實現(xiàn)代碼邏輯)(這里區(qū)分普通,加急,特急消息,采用的是在消息頭加上標記,例:*加急*:.....):
package com.designpattern.bridge.solution_1;/** * 消息統(tǒng)一接口 * @author Json*/public interface Message { /** * 發(fā)送消息 * @param message 消息內容 * @param users 接收人 */ public void send(String message,String users); }
消息類型(3種):
package com.designpattern.bridge.solution_1;/** * 普通消息接口 * @author Json*/public interface CommonMessage extends Message { }
package com.designpattern.bridge.solution_1;/** * 加急消息接口 * @author Json*/public interface UrgencyMessage extends Message { }
package com.designpattern.bridge.solution_1;/** * 特急消息接口 * @author Json*/public interface VeryUrgencyMessage extends Message { /** * 擴展自己的新功能:特急任務,需要催促接收人盡快完成 * @param messageId 消息的編號 * @return */ public void urge(String messageId); }
普通消息 對應 3種消息發(fā)送方式:
package com.designpattern.bridge.solution_1;/** * 使用站內消息方式發(fā)送【普通】信息 * @author Json*/public class CommonSystemMessage implements UrgencyMessage { @Override public void send(String message, String users) { message = "*普通*:" + message; System.out.println("使用站內消息方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
package com.designpattern.bridge.solution_1;/** * 使用短信方式發(fā)送【普通】信息 * @author Json*/public class CommonMobileMessage implements UrgencyMessage { @Override public void send(String message, String users) { message = "*普通*:" + message; System.out.println("使用短信方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
package com.designpattern.bridge.solution_1;/** * 使用郵件方式發(fā)送【普通】信息 * @author Json*/public class CommonEmailMessage implements CommonMessage { @Override public void send(String message, String users) { message = "*普通*:" + message; System.out.println("使用郵件方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
緊急消息 對應 3種消息發(fā)送方式(代碼類似,這里折疊了):
View Code
View Code
View Code
緊急消息 對應 3種消息發(fā)送方式(代碼類似,這里折疊了):
View Code
View Code
View Code
測試代碼:
package com.designpattern.bridge.solution_1;/** * 測試 * @author Json*/public class Client { public static void main(String[] args) { Message s; //創(chuàng)建普通消息 s = new CommonEmailMessage(); s.send("本月需完成任務如下:...", "Json_Wang"); //創(chuàng)建一個加急消息對象 s = new UrgencyEmailMessage(); s.send("本周需完成任務如下:...", "Json_Wang"); //創(chuàng)建一個特急消息對象 s = new VeryUrgencyEmailMessage(); s.send("盡快修復致命bug,今天客戶端無法登陸的問題!", "Json_Wang"); System.out.println("---------------------------------"); //創(chuàng)建普通消息 s = new CommonMobileMessage(); s.send("本月需完成任務如下:...", "Json_Wang"); //創(chuàng)建一個加急消息對象 s = new UrgencyMobileMessage(); s.send("本周需完成任務如下:...", "Json_Wang"); //創(chuàng)建一個特急消息對象 s = new VeryUrgencyMobileMessage(); s.send("盡快修復致命bug,今天客戶端無法登陸的問題!", "Json_Wang"); //站內消息實現(xiàn)省略... } }
結果:
使用郵件方式,發(fā)送信息【*普通*:本月需完成任務如下:...】To【Json_Wang】 使用郵件方式,發(fā)送信息【*加急*:本周需完成任務如下:...】To【Json_Wang】 使用郵件方式,發(fā)送信息【*特急*:盡快修復致命bug,今天客戶端無法登陸的問題!】To【Json_Wang】 --------------------------------- 使用短信方式,發(fā)送信息【*普通*:本月需完成任務如下:...】To【Json_Wang】 使用短信方式,發(fā)送信息【*加急*:本周需完成任務如下:...】To【Json_Wang】 使用短信方式,發(fā)送信息【*特急*:盡快修復致命bug,今天客戶端無法登陸的問題!】To【Json_Wang】
上面這樣實現(xiàn),好像也能滿足基本的功能要求,可是這么實現(xiàn)好不好呢?有沒有什么問題呢?
試想一下:新增一種消息類型或者消息方式,我們就要實現(xiàn)其對應的全套實現(xiàn);例如,增加一種消息類型,需要實現(xiàn)所有不同的消息發(fā)送方式;
這意味著,以后每次擴展一下消息類型或消息方式,都必須要實現(xiàn)這其對應的全套處理方式,是不是很痛苦?
采用通過繼承來擴展的實現(xiàn)方式,有個明顯的缺點:擴展消息的類型或方式不太容易,很容易會造成類爆炸問題,擴展起來不靈活。
就沒有更好的解決方案嗎?
3、解決問題 && 橋接模式類圖及組成
先來看看橋接模式的類圖及組成,解決方案及代碼稍后講解;
(引)類圖:
組成:
抽象類(Abstraction):定義抽象類的接口,維護一個指向Implementor類型對象的引用。
擴充抽象類(RefinedAbstraction):擴充由Abstraction定義的接口。
實現(xiàn)類接口(Implementor):定義實現(xiàn)類的接口,該接口不一定要與Abstraction的接口完全一致;事實上這兩個接口可以完全不同。一般來講, Implementor接口僅提供基本操作,而 Abstraction則定義了基于這些基本操作的較高層次的操作。
具體實現(xiàn)類(ConcreteImplementor):實現(xiàn)Implementor接口并定義它的具體實現(xiàn)。
代碼結構:
/** * 定義實現(xiàn)部分的接口,可以與抽象部分接口的方法不一樣 */ public interface Implementor { /** * 示例方法,實現(xiàn)抽象部分需要的某些具體功能 */ public void operationImpl(); }/** * 定義抽象部分的接口 */public abstract class Abstraction { /** * 持有一個實現(xiàn)部分的對象 */ protected Implementor impl; /** * 構造方法,傳入實現(xiàn)部分的對象 * @param impl 實現(xiàn)部分的對象 */ public Abstraction(Implementor impl){ this.impl = impl; } /** * 示例操作,實現(xiàn)一定的功能,可能需要轉調實現(xiàn)部分的具體實現(xiàn)方法 */ public void operation() { impl.operationImpl(); } }/** * 真正的具體實現(xiàn)對象 */ public class ConcreteImplementorA implements Implementor { public void operationImpl() { //真正的實現(xiàn) } } /** * 真正的具體實現(xiàn)對象 */ public class ConcreteImplementorB implements Implementor { public void operationImpl() { //真正的實現(xiàn) } }/** * 擴充由Abstraction定義的接口功能 */ public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor impl) { super(impl); } /** * 示例操作,實現(xiàn)一定的功能 */ public void otherOperation(){ //實現(xiàn)一定的功能,可能會使用具體實現(xiàn)部分的實現(xiàn)方法, //但是本方法更大的可能是使用Abstraction中定義的方法, //通過組合使用Abstraction中定義的方法來完成更多的功能 } }
這里大家對橋接模式有點印象沒?
接下來,咱們利用橋接模式來解決上述的問題:解決方案:是根據(jù)實際需要對消息類型和消息方式進行組合。
示意圖如下:
下面在維度上看一下組合情況:
實現(xiàn)代碼:
package com.designpattern.bridge.solution_2;/** * 實現(xiàn)發(fā)送消息的統(tǒng)一接口 * @author Json*/ public interface MessageImplementor { /** * 發(fā)送消息 * @param message 消息內容 * @param toUser 接收人 */ public void send(String message,String users); }
package com.designpattern.bridge.solution_2;/** * 抽象的消息對象 * @author Json*/public abstract class AbstractMessage { /** * 持有一個實現(xiàn)部分的對象 */ protected MessageImplementor impl; /** * 構造方法,傳入實現(xiàn)部分的對象 * @param impl 實現(xiàn)部分的對象 */ public AbstractMessage(MessageImplementor impl){ this.impl = impl; } /** * 發(fā)送消息,轉調實現(xiàn)部分的方法 * @param message 消息內容 * @param toUser 接收人 */ public void sendMessage(String message,String toUser){ this.impl.send(message, toUser); } }
3種消息方式:
package com.designpattern.bridge.solution_2;/** * 使用站內消息方式發(fā)送信息 * @author Json*/public class SystemMessage implements MessageImplementor { @Override public void send(String message, String users) { System.out.println("使用站內消息方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
package com.designpattern.bridge.solution_2;/** * 使用短信方式發(fā)送信息 * @author Json*/public class MobileMessage implements MessageImplementor { @Override public void send(String message, String users) { System.out.println("使用短信方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
package com.designpattern.bridge.solution_2;/** * 使用郵件方式發(fā)送信息 * @author Json*/public class EmailMessage implements MessageImplementor { @Override public void send(String message, String users) { System.out.println("使用郵件方式,發(fā)送信息【"+message+"】To【"+users+"】"); } }
3種消息類型:
package com.designpattern.bridge.solution_2;/** * 擴展抽象的消息 -- 普通消息 * @author Json*/public class CommonMessage extends AbstractMessage{ public CommonMessage(MessageImplementor impl) { super(impl); } public void sendMessage(String message, String toUser) { message = "*普通*:" + message; super.sendMessage(message, toUser); } }
package com.designpattern.bridge.solution_2;/** * 擴展抽象的消息 -- 加急消息 * @author Json*/public class UrgencyMessage extends AbstractMessage{ public UrgencyMessage(MessageImplementor impl) { super(impl); } public void sendMessage(String message, String toUser) { message = "*加急*:" + message; super.sendMessage(message, toUser); } }
package com.designpattern.bridge.solution_2;/** * 擴展抽象的消息 -- 特急消息 * @author Json*/public class VeryUrgencyMessage extends AbstractMessage{ public VeryUrgencyMessage(MessageImplementor impl) { super(impl); } public void sendMessage(String message, String toUser) { message = "*特急*:" + message; super.sendMessage(message, toUser); } /** * 擴展自己的新功能:特急任務,需要催促接收人盡快完成 * @param messageId 消息的編號 * @return */ public void urge(String messageId) { //發(fā)出催促的信息 ,比如:每隔半小時 發(fā)送一條催促消息 //TODO 邏輯 } }
測試:
package com.designpattern.bridge.solution_2;/** * 測試 * @author Json*/public class Client {
版權聲明,轉載請注明出處:http://www.cnblogs.com/JsonShare
http://www.cnblogs.com/JsonShare/p/7233342.html