設(shè)計模式-適配器模式
定義
適配器模式(Adapter Pattern)又叫做變壓器模式,它的功能是將一個類的接口變成客戶端所期望的另一種接口,從而使原本因接口不匹配而導(dǎo)致無法在一起工作的兩個類能夠一起工作.
屬于結(jié)構(gòu)型設(shè)計模式.
也就是說,當(dāng)前系統(tǒng)存在兩種接口A和B,客戶只支持訪問A接口,但是當(dāng)前系統(tǒng)沒有A接口對象,但是有B接口對象,但客戶無法識別B接口,因此需要通過一個適配器C,將B接口內(nèi)容轉(zhuǎn)換成A接口,從而使得客戶能夠從A接口獲取得到B接口內(nèi)容.
在軟件開發(fā)中,基本上 任何問題都可以通過增加一個中間層進(jìn)行解決.適配器模式就是一個中間層.綜上,適配器模式其實起著轉(zhuǎn)化/委托的作用,將一種接口轉(zhuǎn)化為另一種符合需求的接口.
適配器模式一般包含3種角色:目標(biāo)角色、源角色、適配器.
1、目標(biāo)角色(Target):也就是我們期望的接口;
2、源角色(Adaptee):存在于系統(tǒng)中,內(nèi)容滿足客戶需求(需轉(zhuǎn)換),但借口不匹配的接口實例;
3、適配器(Adapter):將源角色(Adaptee)轉(zhuǎn)化為目標(biāo)角色(Target)的類實例.
適配器模式各角色之間的關(guān)系如下:
假設(shè)當(dāng)前系統(tǒng)中,客戶端需要訪問的是target接口,但target接口沒有一個實例符合需求,而Adaptee實例符合需求;但是客戶端無法直接使用Adaptee(接口不兼容);因此,我們需要一個適配器(Adapter)來進(jìn)行中轉(zhuǎn),讓Adaptee能轉(zhuǎn)化為Target接口形式.
適配器模式有3種形式:類適配器、對象適配器、接口適配器.
類適配器
類適配器的原理就是通過繼承來實現(xiàn)適配器的功能.具體做法:讓Adapter實現(xiàn)Target接口,并且繼承Adaptee,這樣Adapter就具備Target和Adaptee等特性,就可以將兩者進(jìn)行轉(zhuǎn)化.

下面我們以一個實例進(jìn)行講解,來看下該實例分別用類適配器、對象適配器和接口適配器是怎樣進(jìn)行代碼實現(xiàn).在中國民用電都是220V交流電,但我們手機(jī)使用的鋰電池使用的5V直流電.因此我們給手機(jī)充電時就需要使用電源適配器來進(jìn)行轉(zhuǎn)換.下面我們有代碼來還原這個生活場景,創(chuàng)建Adaptee角色,需要被轉(zhuǎn)換的對象AC220類,表示220V交流電:





上面的案例中,通過增加PowerAdapter電源適配器,實現(xiàn)了兩者的兼容.
對象適配器
對象適配器的原理就是通過組合來實現(xiàn)適配器的功能.具體做法:讓Adapter實現(xiàn)Target接口,然后內(nèi)部持有Adaptee實例,然后在Target接口規(guī)定的方法那轉(zhuǎn)換Adaptee.


接口適配器
接口適配器的關(guān)注點與類適配器和對象適配器不太一樣,類適配器和對象適配器著重于系統(tǒng)存在一個角色(Adaptee)轉(zhuǎn)化成目標(biāo)接口(Target)所需內(nèi)容,而接口適配器的使用場景是解決接口方法過多,如果直接實現(xiàn)接口,那么類會多出許多空實現(xiàn)的方法,類顯得很臃腫.此時,使用接口適配器就能讓我們只實現(xiàn)我們需要的接口方法,目標(biāo)更清晰.
接口適配器的主要原理就是利用抽象類實現(xiàn)接口,并且空實現(xiàn)接口眾多方法.下面我們來接口適配器的源碼實現(xiàn),首先創(chuàng)建Target角色DC5類:




重構(gòu)第三方登錄自由適配的業(yè)務(wù)場景
下面我們來一個實際的業(yè)務(wù)場景,利用適配器模式來解決實際問題.年紀(jì)稍微大一點的小伙伴一定經(jīng)歷過這樣一個過程,我們很早以前開發(fā)的老系統(tǒng)應(yīng)該都有登錄接口,但是隨著業(yè)務(wù)的發(fā)展和社會的進(jìn)步,單純的依賴用戶名密碼登錄顯然不能滿足用戶需求了.現(xiàn)在,我們大部分系統(tǒng)都支持多種登錄方式,如QQ登錄、微信登錄、手機(jī)登錄、微博登錄等等,同時保留用戶名密碼登錄方式.雖然登錄方式豐富了,但是登錄后的處理邏輯可以不必改,同樣是將登陸狀態(tài)保存到session,遵循開閉原則,我們開始創(chuàng)建統(tǒng)一的返回結(jié)果ResultMsg類:


為了遵循開閉原則,老系統(tǒng)的代碼我們不會去修改.那么下面開啟代碼重構(gòu)之路,先創(chuàng)建Member類:

運行非常穩(wěn)定的代碼我們不去改動,首先創(chuàng)建Target角色I(xiàn)PassportForThird接口:

然后創(chuàng)建適配器Adapter角色實現(xiàn)兼容,創(chuàng)建一個新的類PassportForThirdAdapter繼承原來的邏輯:


通過這么一個簡單的適配,完成了代碼兼容.當(dāng)然,我們代碼還可以更加優(yōu)雅,根據(jù)不同的登錄方式,創(chuàng)建不同的Adapter.首先,創(chuàng)建ILoginAdapter接口:

然后,創(chuàng)建一個抽象類AbstractAdapter繼承PassportService原有的功能,同時實現(xiàn)ILoginAdapter接口,然后分別實現(xiàn)不同的登錄適配.





然后,創(chuàng)建適配器PassportForThirdAdapter類,實現(xiàn)目標(biāo)接口IPassportForThird完成兼容,


最后我們看一下類圖:

至此,我們在遵循開閉原則的前提下,完整地實現(xiàn)了一個兼容多平臺登錄的業(yè)務(wù)場景.當(dāng)然,我們目前的這個設(shè)計也并不完美,僅供參考,感興趣的小伙伴們可以繼續(xù)完善這段代碼.例如適配器中的參數(shù)目前是寫死成String的,改為Object[]會更合理.
學(xué)習(xí)到這里,相信小伙伴們會有一個疑問:適配器模式跟策略模式好像區(qū)別不大?在這里我要強(qiáng)調(diào)一下,適配器模式主要解決的是功能兼容問題,單場景適配大家肯定不會和策略模式有對比.但多場景適配大家容易產(chǎn)生聯(lián)想和混淆.其實,大家有沒有發(fā)現(xiàn)一個細(xì)節(jié),我給每個適配器都加上了一個support()方法用來判斷是否兼容,support()方法的參數(shù)也是Object的,而support來自于接口.適配器內(nèi)的邏輯并不依賴于接口,我們完全可以講ILoginAdapter接口去掉.而加上接口,只是為了代碼規(guī)范.上面的代碼可以說是策略模式、簡單工廠和適配器模式的綜合運用.
適配器模式在源碼中的體現(xiàn)
Spring中適配器模式也應(yīng)用的非常廣泛,例如:SpringAOP中的AdvisorAdapter類,它有三個實現(xiàn)類MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter,先來看頂層接口AdvisorAdapter的源代碼:

再看MethodBeforeAdviceAdapter類:


剩余的那個類就不貼了,Spring會根據(jù)不同的AOP配置來確定使用對應(yīng)的Advice,跟策略模式不同的是:策略模式一個方法可以同時擁有多個Advice.
擴(kuò)展:SpringMVC中的HandlerAdapter類也是一個適配器模式的應(yīng)用實例.
適用場景
1、已經(jīng)存在的類,它的方法和需求不匹配(方法結(jié)果相同或相似)的情況.
2、適配器模式不是軟件設(shè)計階段考慮的設(shè)計模式,是隨著軟件維護(hù),由于不同產(chǎn)品、不同廠家造成功能類似而接口不相同情況下的解決方案.
生活中也存在非常多的應(yīng)用場景,例如電源轉(zhuǎn)換插頭、手機(jī)充電轉(zhuǎn)換頭,顯示器轉(zhuǎn)接頭等.
優(yōu)點
1、能提高類的透明性和復(fù)用性,現(xiàn)有的類復(fù)用但不需要改變;
2、目標(biāo)類和適配器類解耦,提高程序的擴(kuò)展性;
3、在很多業(yè)務(wù)場景中符合開閉原則.
缺點
1、適配器編寫過程需要全面考慮,可能會增加系統(tǒng)的復(fù)雜度;
2、增加代碼閱讀難度,降低代碼可讀性,過多使用適配器會使系統(tǒng)代碼變得凌亂.
適配器模式和裝飾器模式對比
