無處不在的適配器模式

點(diǎn)擊上方「藍(lán)字」關(guān)注我們

0x01:適配器模式簡介
? ? ? ?對于適配器相信不會(huì)陌生,生活中的例子比比皆是,像耳機(jī)轉(zhuǎn)接線,充電器適配器,水管適配接口等等。通過類比很容易理解軟件中的適配器模式。
? ? ? ?客戶端需要一個(gè)target(目標(biāo))接口,但是不能直接重用已經(jīng)存在的adaptee(適配者)類,因?yàn)樗慕涌诤蛅arget接口不一致,所以需要adapter(適配器)將adaptee轉(zhuǎn)換為target接口。前提是target接口和已存在的適配者adaptee類所做的事情是相同或相似,只是接口不同且都不易修改。如果在設(shè)計(jì)之初,最好不要考慮這種設(shè)計(jì)模式。凡事都有例外,就是設(shè)計(jì)新系統(tǒng)的時(shí)候考慮使用第三方組件,因?yàn)闆]必要為了迎合第三方組件修改自己的軟件設(shè)計(jì)風(fēng)格,可以嘗試使用適配器模式。
下面是一個(gè)非常典型的使用適配器模式的場景:
? ? ? ?Sun公司在1996年公開了Java語言的數(shù)據(jù)庫連接工具JDBC,JDBC使得Java語言程序能夠與數(shù)據(jù)庫連接,并使用SQL語言來查詢和操作數(shù)據(jù)。JDBC給出一個(gè)客戶端通用的抽象接口,每一個(gè)具體數(shù)據(jù)庫廠商(如SQL Server、Oracle、MySQL等)的JDBC驅(qū)動(dòng)軟件都是一個(gè)介于JDBC接口和數(shù)據(jù)庫引擎接口之間的適配器軟件。抽象的JDBC接口和各個(gè)數(shù)據(jù)庫引擎API之間都需要相應(yīng)的適配器軟件,這就是為各個(gè)不同數(shù)據(jù)庫引擎準(zhǔn)備的驅(qū)動(dòng)程序。
? ? ? ?另外一個(gè)比較典型的適配器場景J2EE規(guī)范與J2EE規(guī)范實(shí)現(xiàn)的服務(wù)器。SUN公司提供了一套J2EE規(guī)范,然后不同廠商根據(jù)自己的理解實(shí)現(xiàn)了不同的應(yīng)用服務(wù)器。SUN公司提供了一套servlet api規(guī)范,然后實(shí)現(xiàn)這套規(guī)范的著名應(yīng)用服務(wù)器有Apache Tomcat、Jetty、Oracle 的 Weblogic、IBM 的 WebSphere 等。
適配器模式的UML類圖如下

從類圖上看主要包含如下角色:
目標(biāo)角色(target):這是客戶所期待的接口。目標(biāo)可以是具體的或抽象的類,也可以是接口;
適配者角色(adaptee):已存在接口(可以理解是第三方提供的接口),但是和客戶端期待的接口不兼容;
適配器角色(adapter):將已有接口轉(zhuǎn)換成目標(biāo)接口(可以理解我方需要的接口);
0x02:適配器模式的實(shí)現(xiàn)
類適配器模式(class adapter pattern)
通過繼承進(jìn)行適配(類間繼承)。類適配器模式在編譯時(shí)實(shí)現(xiàn)target(目標(biāo))接口。這種適配器模式使用了多個(gè)實(shí)現(xiàn)了期待的接口或者已經(jīng)存在的接口的多態(tài)接口。比較典型的就是:target接口被創(chuàng)建為一個(gè)純粹的接口,Java不支持多繼承的語言。
Target:Target目標(biāo)角色,該角色定義把其他類轉(zhuǎn)換為何種接口,也就是期望接口,通常情況下是一個(gè)接口或一個(gè)抽象類,一般不會(huì)是實(shí)現(xiàn)類
public?interface?Target?{
?????public?void?request();
}
Adaptee:Adaptee源角色,想把誰轉(zhuǎn)換為目標(biāo)角色,這個(gè)“誰”就是源角色,它是已經(jīng)存在的、運(yùn)行良好的類或?qū)ο?/span>
public?class?Adaptee?{
????public?void?specificRequest()?{
????????System.out.println("我是已經(jīng)存在的運(yùn)行良好的第三方廠商");
????}
}
Adapter:Adapter適配器角色,是適配器模式的核心角色,它的職責(zé)是通過繼承或是類關(guān)聯(lián)的方式把源角色轉(zhuǎn)換為目標(biāo)角色
public?class?Adapter?extends?Adaptee?implements?Target?{
????@Override
????public?void?request()?{
????????super.specificRequest();
????}
}
ConcreteTarget:目標(biāo)角色的實(shí)現(xiàn)類
public?class?ConcreteTarget?implements?Target?{
????@Override
????public?void?request()?{
????????System.out.println("沒有增加適配器的我方普通實(shí)現(xiàn)邏輯");
????}
}
類適配器模式測試代碼
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????//原有無適配器的業(yè)務(wù)邏輯
????????Target?target?=?new?ConcreteTarget();
????????target.request();
????????//增加適配器后的業(yè)務(wù)邏輯
????????Target?target2?=?new?Adapter();
????????target2.request();
????}
}對象適配器模式(object adapter pattern)
通過對象層次的關(guān)聯(lián)關(guān)系進(jìn)行委托(對象的合成關(guān)系/關(guān)聯(lián)關(guān)系)。對象適配器模式在運(yùn)行時(shí)實(shí)現(xiàn)target(目標(biāo))接口。在這種適配器模式中,適配器包裝了一個(gè)類實(shí)例。在這種情況下,適配器調(diào)用包裝對象實(shí)例的方法。
Target:客戶所期待的接口。目標(biāo)可以是具體的或抽象的類,也可以是接口
public?class?Target?{
????public?void?request()?{
????????System.out.println("沒有適配器的普通請求");
????}
}
Adaptee:需要適配的類
public?class?Adaptee?{
????public?void?specificRequest()?{
????????System.out.println("適配器類實(shí)現(xiàn)的特殊請求");
????}
}
Adapter:通過在內(nèi)部包裝一個(gè)Adaptee對象,把源接口轉(zhuǎn)換成目標(biāo)接口
public?class?Adapter?extends?Target?{
????private?Adaptee?adaptee?=?new?Adaptee();
????@Override
????public?void?request()?{
????????//替換原理的邏輯,調(diào)用適配類的邏輯
????????adaptee.specificRequest();
????}
}
對象適配器模式測試代碼
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????Target?target?=?new?Adapter();
????????target.request();
????}
}
缺省適配器模式(default adapter pattern),也叫默認(rèn)適配器模式、接口適配器模式
當(dāng)不需要全部實(shí)現(xiàn)接口提供的方法時(shí),可以設(shè)計(jì)一個(gè)適配器抽象類實(shí)現(xiàn)接口,并為接口中的每個(gè)方法提供默認(rèn)方法實(shí)現(xiàn)或者空實(shí)現(xiàn)(如果大家做過GUI編程,就可以經(jīng)常遇到這種實(shí)現(xiàn),特別是各種控件的事件監(jiān)聽都提供了適配器類),抽象類的子類就可以有選擇的覆蓋父類的某些方法實(shí)現(xiàn)需求,它適用于一個(gè)接口不想使用所有的方法的情況。在java8后,接口中可以有default方法,就不需要這種缺省適配器模式了。接口中方法都設(shè)置為default,實(shí)現(xiàn)為空,這樣同樣同樣可以達(dá)到缺省適配器模式同樣的效果。
target:包含了很多沒有實(shí)現(xiàn)的操作接口
public?interface?Target?{
?????public?abstract?void?operation1();
?????public?abstract?void?operation2();
?????public?abstract?void?operation3();
}
Adapter:默認(rèn)實(shí)現(xiàn)了所有操作抽象類,只是所有的實(shí)現(xiàn)都是空實(shí)現(xiàn)
public?abstract?class?DefaultAdapter?implements?Target{
????@Override
????public?void?operation1()?{
????}
????@Override
????public?void?operation2()?{
????}
????@Override
????public?void?operation3()?{
????}
}
測試缺省適配器模式需要用到的類(相當(dāng)于GUI編程的一個(gè)組件,比如按鈕Button)
public?class?Operator?{
????private?Target?target;
????public?void?addOperation(Target?target)?{
????????this.target=?target;
????}
????public?void?operation1()?{
????????target.operation1();
????}
????public?void?operation2()?{
????????target.operation2();
????}
????public?void?operation3()?{
????????target.operation3();
????}
}
缺省適配器模式測試代碼
public?class?Client{
????public?static?void?main(String[]?args)?{
????????//?原來要實(shí)現(xiàn)所有操作類的操作
????????Operator?operator1=?new?Operator();
????????operator1.addOperation(new?Target()?{
????????????@Override
????????????public?void?operation1()?{}
????????????@Override
????????????public?void?operation2()?{
????????????????System.out.println("invoke?operation2");
????????????}
????????????@Override
????????????public?void?operation3()?{}
????????});
????????operator1.operation2();
????????//?2、使用缺省適配器只需要實(shí)現(xiàn)需要用到的接口方法
????????Operator?operator2?=?new?Operator();
????????operator2.addOperation(new?DefaultAdapter()?{
????????????@Override
????????????public?void?operation2()?{
????????????????System.out.println("invoke?operation2");
????????????}
????????});
????????operator2.operation2();
????}
}適配器模式本質(zhì)上是現(xiàn)有的不兼容的接口轉(zhuǎn)換為需要的接口。類適配器模式以繼承現(xiàn)有類的方式轉(zhuǎn)換;對象適配器模式以聚合對象實(shí)例的方式轉(zhuǎn)換;接口適配器模式以實(shí)現(xiàn)接口的方式轉(zhuǎn)換。適配器模式是在現(xiàn)有的類和系統(tǒng)都不易修改的情況下才使用,在系統(tǒng)設(shè)計(jì)之初慎用該設(shè)計(jì)模式。

掃碼二維碼
獲取更多精彩
Java樂園

