本文通過老王和小王買車,引出設計模式中的結構型設計之橋接模式,接著說明設計型模式的概念和代碼實作,為了加深理解,會說明配接器設計模式在JDBC中的應用,最后談談橋接模式和配接器模式的總結,
讀者可以拉取完整代碼到本地進行學習,實作代碼均測驗通過后上傳到碼云,
一、引出問題
老王和小王去奔馳4S店買車,奔馳4S店的各種品牌型號琳瑯滿目,老王想試駕奔馳E、小王想試駕奔馳G,并且提出兩種奔馳型號的各種顏色都想體驗一把,這讓店小二犯了難,兩兩組合就是很多種,4S店壓根放不下,
無奈店小二求救經理,經理出了一個注意:將奔馳E和G開的品牌抽象出來,將顏色也抽象出來,通過品牌和顏色的組合代替繼承關系,減少了顏色和品牌的耦合,且減少了車的個數,只需要兩臺就夠了,
果然經理不愧是經理,
經理所說的其實就是橋接模式,這種模式涉及到一個作為橋接的介面,使得物體類的功能獨立于介面實作類,這兩種型別的類可被結構化改變而互不影響,
二、概念與使用
我們看一些概念:橋接(Bridge)是用于把抽象化與實作化解耦,使得二者可以獨立變化,這種型別的設計模式屬于結構型模式,它通過提供抽象化和實作化之間的橋接結構,來實作二者的解耦,
在該模式中應該涉及到四個角色:
①實作類介面(Implementor):定義實作角色的介面,供擴展抽象化角色使用,例如抽象出奔馳品牌benz 可以擴展出 benzE benzG
②具體實作角色(ConcreteImplementor):實作類的具體實作,例如各種奔馳品牌
③抽象化(Abstraction)角色:定義一個抽象類,其中參考了實作化角色(想要組合),例如汽車產品
④擴展抽象化(RefinedAbstraction)角色:抽象化角色子類,實作父類方法,且通過組合關系呼叫實作化角色中的業務方法,例如具體奔馳產品,紅色奔馳、白色奔馳
根據該模式的定義,我們將奔馳品牌抽象出來,然后各品牌有各自的實作,每個顏色的車把車品牌組合進來,在客戶端中每個相機型別和相機品牌都能兩兩組合,
我們看具體的代碼實作:
實作類介面:
/**
* 奔馳品牌類
* @author tcy
* @Date 05-08-2022
*/
public interface BenzBrand {
void showInfo();
}
具體實作角色1:
/**
* @author tcy
* @Date 05-08-2022
*/
public class BenzE implements BenzBrand{
@Override
public void showInfo() {
System.out.print("【奔馳E】顏色是:");
}
}
具體實作角色2:
/**
* @author tcy
* @Date 05-08-2022
*/
public class BenzG implements BenzBrand{
@Override
public void showInfo() {
System.out.print("【奔馳G】顏色是:");
}
}
抽象化角色:
/**
* 抽象奔馳類
* @author tcy
* @Date 05-08-2022
*/
public abstract class Benz {
// 將品牌組合進來
protected BenzBrand benzBrand;
public Benz(BenzBrand benzBrand) {
this.benzBrand = benzBrand;
}
public void showInfo(){
benzBrand.showInfo();
}
}
擴展抽象化1:
/**
* @author tcy
* @Date 05-08-2022
*/
public class BlackBenz extends Benz {
public BlackBenz(BenzBrand benzBrand) {
super(benzBrand);
}
@Override
public void showInfo() {
super.showInfo();
System.out.println("黑色...");
}
}
擴展抽象化2:
/**
* @author tcy
* @Date 05-08-2022
*/
public class RedBenz extends Benz {
public RedBenz(BenzBrand benzBrand) {
super(benzBrand);
}
@Override
public void showInfo() {
super.showInfo();
System.out.println("紅色...");
}
}
客戶端呼叫:
/**
* @author tcy
* @Date 05-08-2022
*/
public class Client {
public static void main(String[] args) {
// 黑色奔馳E
Benz benz1 = new BlackBenz(new BenzE());
benz1.showInfo();
// 黑色奔馳G
Benz benz2 = new BlackBenz(new BenzG());
benz2.showInfo();
// 紅色奔馳E
Benz benz3 = new RedBenz(new BenzE());
benz3.showInfo();
// 紅色奔馳G
Benz benz4 = new RedBenz(new BenzG());
benz4.showInfo();
}
【奔馳E】顏色是:黑色...
【奔馳G】顏色是:黑色...
【奔馳E】顏色是:紅色...
【奔馳G】顏色是:紅色...
這樣即使老王提出來新的顏色、新的車型,只需要增加相應的具體實作角色或者擴展抽象化角色即可,
顧名思義,橋接模式就像是一個橋,可以用來連接兩個不同地方,這兩個地方自由發展,中間的貿易是通過一座橋來連接,
這種方法的缺點也很顯著,汽車能很快的確立型號和顏色兩個維度,在實際業務開發中,識別出系統兩個獨立變化的維度就不簡單了,
不難看出,列舉的例子有些過于強求,在現實世界中是永遠不可能發生的,為了加深理解我找了大量在JDK亦或是Spirng等各種框架對橋接模式的應用,只找到了橋接模式在Jdbc中的應用,
三、應用
我們都知道通過JDBC可以完成Java對關系型資料庫的SQL操作,我們在連接資料資料庫時,想必都接觸過Driver,在連接MySQL和Oracle的Driver都是不同的,這些都是實作介面類,
我們看一下MySQL中實作的Driver類,
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
在該類中實際上有兩個作用,一是呼叫了DriverManager中的registerDriver方法來注冊驅動,二是當驅動注冊完成后,我們就會開始呼叫DriverManager中的getConnection方法了,
我們看DriverManager的完整代碼:
public class DriverManager {
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
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");
}
}
}
在Java中通過Connection提供給各個資料庫一樣的操作介面,這里的Connection可以看作抽象類,
可以說我們用來操作不同資料庫的方法都是相同的,不過MySQL有自己的ConnectionImpl類,同樣Oracle也有對應的實作類,
這里Driver和Connection之間是通過DriverManager類進行橋接的,這種橋接模式和我們上面可以清晰的看出來各個角色是不同的,
四、總結
橋接模式是很好理解的,相信認真看了實體的同學應該都可以看懂,但那并不代表你已經掌握了該設計模式,在我們使用JDBC的時候,想必有很多同學并不能看出來這是橋接模式,
紙上得來終覺淺,有一部分例子是為了說明橋接模式而“構想”出來的,各個角色都是清晰直觀,看了這樣的代碼,可以學會橋接模式,但是到了實際中很可能還是不會用,
最好的方法就是給出真實專案里的例子,但是這個難度確實很大,一到了真實專案里,就會遇到很多細節問題,從而影響對模式的理解,而且真實專案都帶有一定的業務環境,
看懂并且學會了設計模式是一回事,在實際開發中擇優選擇設計模式那是另外一回事,這不僅需要對各個設計模式理解到位,更多的是對業務的理解和代碼理念的把控,
推薦讀者,參考軟體設計七大原則 認真閱讀往期的文章,認真體會,
創建型設計模式:
一、設計模式之工廠方法和抽象工廠
二、設計模式之單例和原型
三、設計模式之建造者模式
結構型設計模式:
四、設計模式之代理模式
五、設計模式之配接器模式
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/501126.html
標籤:其他
