定義
將抽象部分與它的具體實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立的變化视粮。(一定程度上解耦)
通過組合的方式建立兩個(gè)類之間的聯(lián)系细办,而不是繼承
適用場(chǎng)景
- 抽象和具體實(shí)現(xiàn)之間增加更多的靈活性。
- 一個(gè)類存在兩個(gè)(或多個(gè))獨(dú)立變化的維度蕾殴,且這兩個(gè)(或多個(gè))維度都需要獨(dú)立進(jìn)行擴(kuò)展笑撞。
- 不希望使用繼承,或因?yàn)槎鄬永^承導(dǎo)致系統(tǒng)類的個(gè)數(shù)劇增钓觉。
優(yōu)點(diǎn)
- 分離抽象部分及其具體實(shí)現(xiàn)部分茴肥。(抽象和實(shí)現(xiàn)不在同一維度)
- 提高了系統(tǒng)可擴(kuò)展性
- 符合開閉原則
- 合成復(fù)用原則
缺點(diǎn)
- 增加了系統(tǒng)和理解設(shè)計(jì)難度。
- 需要正確的識(shí)別出系統(tǒng)兩個(gè)獨(dú)立變化的維度议谷。
代碼
橋接模式說起來很難理解炉爆,其核心就是抽象與實(shí)現(xiàn)分離使用組合的方式分離開來。我們來實(shí)現(xiàn)這么一個(gè)業(yè)務(wù)場(chǎng)景卧晓,來加深理解一下橋接模式芬首。
目前我們使用手機(jī),都有兩大主系統(tǒng)逼裆,安卓和IOS系統(tǒng)郁稍。但是這不影響我們使用微信,QQ或者其他社交軟件進(jìn)行聊天胜宇。
我們使用橋接模式來實(shí)現(xiàn)這個(gè)使用不同系統(tǒng)以及不同的社交軟件進(jìn)行聊天的業(yè)務(wù)場(chǎng)景耀怜。
首先需要進(jìn)行抽象:
- 社交軟件
- 打開軟件
- 聊天功能
- 系統(tǒng)
- 使用系統(tǒng)打開軟件
下面我們來進(jìn)行具體的實(shí)現(xiàn)
社交軟件代碼
社交軟件抽象接口SocialSofware
public interface SocialSofware {
/**
* 打開軟件
* @return
*/
SocialSofware openSoftware();
/**
* 開始聊天
*/
void chat();
}
我們的抽象已經(jīng)完成恢着,接下來,讓我們具體實(shí)現(xiàn)兩個(gè)社交軟件QQ
和weichat
QQ軟件QQSoftware
/**
* QQ軟件
*/
public class QQSoftware implements SocialSofware {
@Override
public SocialSofware openSoftware() {
System.out.println("打開QQ");
return new QQSoftware();
}
@Override
public void chat() {
System.out.println("使用QQ進(jìn)行聊天");
}
}
微信軟件WeichatSoftware
/**
* 微信軟件
*/
public class WeichatSoftware implements SocialSofware {
@Override
public SocialSofware openSoftware() {
System.out.println("打開微信");
return new WeichatSoftware();
}
@Override
public void chat() {
System.out.println("使用微信進(jìn)行聊天");
}
}
操作系統(tǒng)代碼
抽象類操作系統(tǒng)PhoneOs
這里為什么使用抽象類呢财破,因?yàn)槲覀儗⒘奶燔浖氲讲僮飨到y(tǒng)中掰派,需要使用組合模式,讓子類來具體實(shí)現(xiàn)左痢。
/**
* 手機(jī)操作系統(tǒng)
*/
public abstract class PhoneOs {
/**
* 只有子類可以調(diào)用
*/
protected SocialSofware socialSofware;
public PhoneOs(SocialSofware socialSofware) {
this.socialSofware = socialSofware;
}
/**
* 打開軟件
* @return
*/
abstract SocialSofware open();
}
蘋果操作系統(tǒng)IosPhone
/**
* 蘋果操作系統(tǒng)
*/
public class IosPhone extends PhoneOs{
public IosPhone(SocialSofware socialSofware) {
super(socialSofware);
}
@Override
SocialSofware open() {
System.out.println("蘋果系統(tǒng)打開軟件");
return socialSofware.openSoftware();
}
}
安卓操作系統(tǒng)AndroidPhone
/**
* 安卓手機(jī)
*/
public class AndroidPhone extends PhoneOs {
public AndroidPhone(SocialSofware socialSofware) {
super(socialSofware);
}
@Override
SocialSofware open() {
System.out.println("安卓系統(tǒng)打開軟件");
return socialSofware.openSoftware();
}
}
我們可以看到這兩個(gè)操作系統(tǒng)具體實(shí)現(xiàn)類靡羡,通過組合將社交軟件引入進(jìn)來,并且調(diào)用其打開方法進(jìn)行打開俊性,這樣無論社交軟件打開方式怎么變略步,具體實(shí)現(xiàn)類不受影響。
UML類圖
代碼測(cè)試
BridgeTest
public class BridgeTest {
public static void main(String[] args) {
/**
* IOS打開QQ
*/
PhoneOs iosQQ = new IosPhone(new QQSoftware());
QQSoftware qqSoftware = (QQSoftware) iosQQ.open();
qqSoftware.chat();
/**
* 安卓打開weichat
*/
PhoneOs androidWeiChat = new AndroidPhone(new WeichatSoftware());
WeichatSoftware weichatSoftware = (WeichatSoftware) androidWeiChat.open();
weichatSoftware.chat();
}
}
我們來使用不同系統(tǒng)打開不同社交軟件看一下結(jié)果如何:
我們可以看到 我們創(chuàng)建了某個(gè)系統(tǒng)并且調(diào)用了open
方法來委托社交軟件來執(zhí)行openSofeware
方法定页。所以我們這個(gè) 橋 就搭建完成了趟薄。
其他源碼使用
我們來看一下java.sql.DriverManager
類中的 registerDriver(java.sql.Driver, java.sql.DriverAction)
方法
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
這個(gè)方法相當(dāng)于將每個(gè)數(shù)據(jù)庫驅(qū)動(dòng)抽象。然后通過getConnection
方法來進(jìn)獲取對(duì)應(yīng)的連接典徊。
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
小結(jié)
橋接模式與 適配器模式或者組合模式都比較相像杭煎,希望我們可以區(qū)分開來,并且適當(dāng)?shù)倪\(yùn)用卒落。