一 概述
定義:適配器模式將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口表示,主的目的是兼容性,讓原本因接口不匹配不能一起工作的兩個類可以協(xié)同工作。其別名為包裝器(Wrapper)。
屬于結(jié)構(gòu)型模式
主要分為三類:類適配器模式、對象的適配器模式、接口的適配器模式。
本文定義:
需要被適配的類、接口、對象(我們有的),簡稱src(source)
最終需要的輸出(我們想要的),簡稱dst(destination,即Target)
適配器稱之為Adapter。
一句話描述適配器模式的感覺:src->Adapter->dst,即src以某種形式(三種形式分別對應三種適配器模式)給到Adapter里,最終轉(zhuǎn)化成了dst。
拿我們Android開發(fā)最熟悉的展示列表數(shù)據(jù)的三大控件:ListView,GridView,RecyclerView的Adapter來說,它們?nèi)齻€控件需要的是View(dst),而我們有的一般是datas(src),所以適配器Adapter就是完成了數(shù)據(jù)源datas 轉(zhuǎn)化成 ItemView的工作。
帶入src->Adapter->dst中,即datas->Adapter->View.
使用場景:
1 系統(tǒng)需要使用現(xiàn)有的類,而這些類的接口不符合系統(tǒng)的需要。
2 想要建立一個可以重復使用的類,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進的類一起工作。
3 需要一個統(tǒng)一的輸出接口,而輸入端的類型不可預知。
二 類適配器模式:
一句話描述:Adapter類,通過繼承src類,實現(xiàn)dst 類接口,完成src->dst的適配。
別的文章都用生活中充電器的例子來講解適配器,的確,這是個極佳的舉例,本文也不能免俗:
充電器本身相當于Adapter,220V交流電相當于src,我們的目dst標是5V直流電。
UserAdapter不僅實現(xiàn)了UserInterface接口,同時還繼承了UserInfo類。在實現(xiàn)接口的getName()和getTelNumber()方法中,分別調(diào)用了UserInfo類中的相應方法并取得結(jié)果。由此可以滿足需求。在上述定義中,按照UserInterface、UserInfo和UserAdapter在場景中的目的不同,可以具體劃分成如下角色:
UserInterface:目標角色——目標接口,系統(tǒng)所期待實現(xiàn)的目標;
UserInfo:源角色——當前已經(jīng)存在的原有的實現(xiàn)類,即將被適配的類;
UserAdapter:適配器角色——將原有實現(xiàn)裝換為目標接口的實現(xiàn)。
簡單點說,適配器模式是指:定義一個類,將一個已經(jīng)存在的類,轉(zhuǎn)換成目標接口所期望的行為形式。
在具體的實現(xiàn)過程中,又可以基于其實現(xiàn)層次是類層次還是對象層次,將其分為類適配器和對象適配器。如上所寫的是類適配器。
對象適配器使用組合代替繼承,將源角色視為適配器角色的屬性:
總體而言:適配器模式是指定義一個適配器類,將一個已經(jīng)存在的類,轉(zhuǎn)換成目標接口所期望的行為形式。同時,一般來說,基于更多的推薦使用組合而不是繼承,因此,對象適配器可能使用更多。
類適配器和對象適配器的選擇
從實現(xiàn)上:類適配器使用對象繼承的方式,屬于靜態(tài)的定義方式。對象適配器使用對象組合的方式,屬于動態(tài)組合的方式;
從工作模式上:類適配器直接繼承了 Adaptee,使得適配器不能和 Adaptee 的子類一起工作。對象適配器允許一個 Adapter 和多個 Adaptee,包括 Adaptee 和它所有的子類一起工作;
從定義角度:類適配器可以重定義 Adaptee 的部分行為,相當于子類覆蓋父類的部分實現(xiàn)方法。對象適配器要重定義 Adaptee 很困難;
從開發(fā)角度:類適配器僅僅引入了一個對象,并不需要額外的引用來間接得到 Adaptee。對象適配器需要額外的引用來間接得到 Adaptee。
總的來說,建議使用對象適配器方式。
適配器模式使用注意事項
充當適配器角色的類就是:實現(xiàn)已有接口的抽象類;
為什么要用抽象類?此類是不要被實例化的。而只充當適配器的角色,也就為其子類提供了一個共同的接口,但其子類又可以將精力只集中在其感興趣的地方。
適配器模式中被適配的接口 Adaptee 和適配成為的接口 Target 是沒有關(guān)聯(lián)的,Adaptee 和 Target 中的方法既可以是相同的,也可以是不同的。
適配器在適配的時候,可以適配多個 Apaptee,也就是說實現(xiàn)某個新的 Target 的功能的時候,需要調(diào)用多個模塊的功能,適配多個模塊的功能才能滿足新接口的要求。
適配器有一個潛在的問題,就是被適配的對象不再兼容 Adaptee 的接口,因為適配器只是實現(xiàn)了 Target 的接口。這導致并不是所有 Adaptee 對象可以被使用的地方都能是使用適配器,雙向適配器解決了這個問題。
優(yōu)點
適配器模式也是一種包裝模式,它與裝飾模式同樣具有包裝的功能,此外,對象適配器模式還具有委托的意思??偟膩碚f,適配器模式屬于補償模式,專用來在系統(tǒng)后期擴展、修改時使用。
缺點
過多的使用適配器,會讓系統(tǒng)非常零亂,不易整體進行把握。比如,明明看到調(diào)用的是 A 接口,其實內(nèi)部被適配成了 B 接口的實現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統(tǒng)進行重構(gòu)。
適配器模式應用場景
在軟件開發(fā)中,也就是系統(tǒng)的數(shù)據(jù)和行為都正確,但接口不相符時,我們應該考慮用適配器,目的是使控制范圍之外的一個原有對象與某個接口匹配。適配器模式主要應用于希望復用一些現(xiàn)存的類,但是接口又與復用環(huán)境要求不一致的情況。比如在需要對早期代碼復用一些功能等應用上很有實際價值。適用場景大致包含三類:
1、已經(jīng)存在的類的接口不符合我們的需求;
2、創(chuàng)建一個可以復用的類,使得該類可以與其他不相關(guān)的類或不可預見的類(即那些接口可能不一定兼容的類)協(xié)同工作;
3、在不對每一個都進行子類化以匹配它們的接口的情況下,使用一些已經(jīng)存在的子類。
Java I/O 庫大量使用了適配器模式,例如 ByteArrayInputStream 是一個適配器類,它繼承了 InputStream 的接口,并且封裝了一個 byte 數(shù)組。換言之,它將一個 byte 數(shù)組的接口適配成 InputStream 流處理器的接口。
我們知道 Java 語言支持四種類型:Java 接口,Java 類,Java 數(shù)組,原始類型(即 int,float 等)。前三種是引用類型,類和數(shù)組的實例是對象,原始類型的值不是對象。也即,Java 語言的數(shù)組是像所有的其他對象一樣的對象,而不管數(shù)組中所存儲的元素類型是什么。這樣一來的話,ByteArrayInputStream 就符合適配器模式的描述,是一個對象形式的適配器類。FileInputStream 是一個適配器類。在 FileInputStream 繼承了 InputStrem 類型,同時持有一個對 FileDiscriptor 的引用。這是將一個 FileDiscriptor 對象適配成 InputStrem 類型的對象形式的適配器模式。
同樣地,在 OutputStream 類型中,所有的原始流處理器都是適配器類。ByteArrayOutputStream 繼承了 OutputStream 類型,同時持有一個對 byte 數(shù)組的引用。它一個 byte 數(shù)組的接口適配成 OutputString 類型的接口,因此也是一個對象形式的適配器模式的應用。
FileOutputStream 繼承了 OutputStream 類型,同時持有一個對 FileDiscriptor 對象的引用。這是一個將 FileDiscriptor 接口適配成 OutputStream 接口形式的對象型適配器模式。
Reader 類型的原始流處理器都是適配器模式的應用。StringReader 是一個適配器類,StringReader 類繼承了 Reader 類型,持有一個對 String 對象的引用。它將 String 的接口適配成 Reader 類型的接口。
Spring 中使用適配器模式的典型應用
在 Spring 的 AOP 里通過使用的 Advice(通知)來增強被代理類的功能。Spring 實現(xiàn)這一 AOP 功能的原理就使用代理模式(1、JDK 動態(tài)代理。2、CGLib 字節(jié)碼生成技術(shù)代理。)對類進行方法級別的切面增強,即,生成被代理類的代理類,并在代理類的方法前,設置攔截器,通過執(zhí)行攔截器中的內(nèi)容增強了代理方法的功能,實現(xiàn)的面向切面編程。
Advice(通知)的類型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每個類型 Advice(通知)都有對應的攔截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要將每個 Advice(通知)都封裝成對應的攔截器類型,返回給容器,所以需要使用適配器模式對 Advice 進行轉(zhuǎn)換。
http://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html