摘要:建議盡量使用對(duì)象的適配器模式,少用繼承。適配器模式也是一種包裝模式,它與裝飾模式同樣具有包裝的功能,此外,對(duì)象適配器模式還具有委托的意思。
概述適配器模式(Adapter Pattern)屬于結(jié)構(gòu)型模式的一種,把一個(gè)類(lèi)的接口變成客戶端所期待的另一種接口,從而使原本接口不匹配而無(wú)法一起工作的兩個(gè)類(lèi)能夠在一起工作...
當(dāng)你想使用一個(gè)已經(jīng)存在的類(lèi),而它的接口不符合你的需求,或者你想創(chuàng)建一個(gè)可重用的類(lèi)(與不兼容接口無(wú)關(guān)的類(lèi)),這時(shí)候可以考慮使用適配器模式。同時(shí)它也是一種包裝模式,它與裝飾模式同樣具有包裝的功能。
案例筆者算是小米的忠實(shí)用戶了,從大學(xué)期間起至今都是購(gòu)買(mǎi)的小米,期間發(fā)現(xiàn)小米5在推出的時(shí)候,它會(huì)送一個(gè)type-c的轉(zhuǎn)接口給我們,那會(huì)type-c數(shù)據(jù)線應(yīng)該還不算普及,這種做法還是蠻好的,在使用轉(zhuǎn)接口后Micro USB得以重復(fù)利用,這樣一來(lái)即使原裝的米5數(shù)據(jù)線丟了也沒(méi)關(guān)系,只要有type-c轉(zhuǎn)接口,一樣可以用Micro USB充電/連接電腦
類(lèi)適配器1.首先定義M4DataLine 代表是Micro USB,我們目的就是通過(guò)適配器能夠用米4數(shù)據(jù)線連接米5手機(jī)
class M4DataLine { public void connection() { System.out.println("使用小米4數(shù)據(jù)線連接..."); } }
2.定義客戶端使用的接口,與業(yè)務(wù)相關(guān)
interface Target { void connection(); } class M5DataLine implements Target { @Override public void connection() { System.out.println("使用小米5數(shù)據(jù)線連接..."); } }
3.創(chuàng)建適配器類(lèi),繼承了被適配類(lèi),同時(shí)實(shí)現(xiàn)標(biāo)準(zhǔn)接口
class M5DataLineAdapter extends M4DataLine implements Target { @Override public void connection() { System.out.println("插入 type-c 轉(zhuǎn)接頭"); super.connection(); } }
4.客戶端代碼,測(cè)試
public class AdapterMain { public static void main(String[] args) { Target target = new M5DataLine(); target.connection(); Target adapter = new M5DataLineAdapter(); adapter.connection(); } }
5.結(jié)果
使用小米5數(shù)據(jù)線連接... 插入 type-c 轉(zhuǎn)接頭 使用小米4數(shù)據(jù)線連接...對(duì)象適配器
創(chuàng)建適配器類(lèi),實(shí)現(xiàn)標(biāo)準(zhǔn)接口,將這個(gè)調(diào)用委托給實(shí)現(xiàn)新接口的對(duì)象來(lái)處理
class M5DataLineAdapter implements Target { private Target target; public M5DataLineAdapter(Target target) { this.target = target; } @Override public void connection() { System.out.println("插入 type-c 轉(zhuǎn)接頭"); target.connection(); } } public class AdapterMain { public static void main(String[] args) { // 使用特殊功能類(lèi),即適配類(lèi) Target adapter = new M5DataLineAdapter(new M5DataLine()); adapter.connection(); } }區(qū)別
類(lèi)適配器:對(duì)象繼承的方式,靜態(tài)的定義。
對(duì)象適配器:依賴(lài)于對(duì)象的組合,都是采用對(duì)象組合的方式,也就是對(duì)象適配器實(shí)現(xiàn)的方式。
JDK 中的適配器使用使用適配器模式的類(lèi)
java.util.Arrays#asList() java.io.InputStreamReader(InputStream) java.io.OutputStreamWriter(OutputStream)
Java I/O 庫(kù)大量使用了適配器模式,如 ByteArrayInputStream 是一個(gè)適配器類(lèi),它繼承了 InputStream 的接口,并且封裝了一個(gè) byte 數(shù)組。換言之,它將一個(gè) byte 數(shù)組的接口適配成 InputStream 流處理器的接口。
在 OutputStream 類(lèi)型中,所有的原始流處理器都是適配器類(lèi)。ByteArrayOutputStream 繼承了 OutputStream 類(lèi)型,同時(shí)持有一個(gè)對(duì) byte 數(shù)組的引用。它一個(gè) byte 數(shù)組的接口適配成 OutputString 類(lèi)型的接口,因此也是一個(gè)對(duì)象形式的適配器模式的應(yīng)用。
FileOutputStream 繼承了 OutputStream 類(lèi)型,同時(shí)持有一個(gè)對(duì) FileDiscriptor 對(duì)象的引用。這是一個(gè)將 FileDiscriptor 接口適配成 OutputStream 接口形式的對(duì)象型適配器模式。
Reader 類(lèi)型的原始流處理器都是適配器模式的應(yīng)用。StringReader 是一個(gè)適配器類(lèi),StringReader 類(lèi)繼承了 Reader 類(lèi)型,持有一個(gè)對(duì) String 對(duì)象的引用。它將 String 的接口適配成 Reader 類(lèi)型的接口。
Spring 中使用適配器模式的典型應(yīng)用在 Spring 的 AOP 里通過(guò)使用的 Advice(通知)來(lái)增強(qiáng)被代理類(lèi)的功能。Spring 實(shí)現(xiàn)這一 AOP 功能的原理就使用代理模式(1、JDK 動(dòng)態(tài)代理。2、CGLib 字節(jié)碼生成技術(shù)代理。)對(duì)類(lèi)進(jìn)行方法級(jí)別的切面增強(qiáng),即,生成被代理類(lèi)的代理類(lèi),并在代理類(lèi)的方法前,設(shè)置攔截器,通過(guò)執(zhí)行攔截器中的內(nèi)容增強(qiáng)了代理方法的功能,實(shí)現(xiàn)的面向切面編程。
Advice(通知)的類(lèi)型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每個(gè)類(lèi)型 Advice(通知)都有對(duì)應(yīng)的攔截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要將每個(gè) Advice(通知)都封裝成對(duì)應(yīng)的攔截器類(lèi)型,返回給容器,所以需要使用適配器模式對(duì) Advice 進(jìn)行轉(zhuǎn)換。
public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method method, Object[] args, Object target) throws Throwable; } public interface AdvisorAdapter { boolean supportsAdvice(Advice advice); MethodInterceptor getInterceptor(Advisor advisor); } class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { @Override public boolean supportsAdvice(Advice advice) { return (advice instanceof MethodBeforeAdvice); } @Override public MethodInterceptor getInterceptor(Advisor advisor) { MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); return new MethodBeforeAdviceInterceptor(advice); } }
默認(rèn)的適配器注冊(cè)表
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable { private final List總結(jié)adapters = new ArrayList (3); public DefaultAdvisorAdapterRegistry() { // 注冊(cè)適配器 registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); registerAdvisorAdapter(new AfterReturningAdviceAdapter()); registerAdvisorAdapter(new ThrowsAdviceAdapter()); } @Override public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { if (adviceObject instanceof Advisor) { return (Advisor) adviceObject; } if (!(adviceObject instanceof Advice)) { throw new UnknownAdviceTypeException(adviceObject); } Advice advice = (Advice) adviceObject; if (advice instanceof MethodInterceptor) { // So well-known it doesn"t even need an adapter. return new DefaultPointcutAdvisor(advice); } for (AdvisorAdapter adapter : this.adapters) { // 檢查是否支持,這里調(diào)用了適配器的方法 if (adapter.supportsAdvice(advice)) { return new DefaultPointcutAdvisor(advice); } } throw new UnknownAdviceTypeException(advice); } @Override public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List interceptors = new ArrayList (3); Advice advice = advisor.getAdvice(); if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice); } for (AdvisorAdapter adapter : this.adapters) { // 檢查是否支持,這里調(diào)用了適配器的方法 if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } return interceptors.toArray(new MethodInterceptor[interceptors.size()]); } @Override public void registerAdvisorAdapter(AdvisorAdapter adapter) { this.adapters.add(adapter); } }
優(yōu)點(diǎn)
可以讓任何兩個(gè)沒(méi)有關(guān)聯(lián)的類(lèi)一起運(yùn)行
提高了類(lèi)的復(fù)用,想使用現(xiàn)有的類(lèi),而此類(lèi)的接口標(biāo)準(zhǔn)又不符合現(xiàn)有系統(tǒng)的需要。通過(guò)適配器模式就可以讓這些功能得到更好的復(fù)用。
增加了類(lèi)的透明度,客戶端只關(guān)注結(jié)果
使用適配器的時(shí)候,可以調(diào)用自己開(kāi)發(fā)的功能,從而自然地?cái)U(kuò)展系統(tǒng)的功能。
缺點(diǎn)
過(guò)多使用會(huì)導(dǎo)致系統(tǒng)凌亂,追溯困難(內(nèi)部轉(zhuǎn)發(fā)導(dǎo)致,調(diào)用A適配成B)
適用場(chǎng)景
系統(tǒng)需要使用一些現(xiàn)有的類(lèi),而這些類(lèi)的接口(如方法名)不符合系統(tǒng)的需要,甚至沒(méi)有這些類(lèi)的源代碼。
想創(chuàng)建一個(gè)可以重復(fù)使用的類(lèi),用于與一些彼此之間沒(méi)有太大關(guān)聯(lián)的一些類(lèi),包括一些可能在將來(lái)引進(jìn)的類(lèi)一起工作。
小故事
魏文王問(wèn)名醫(yī)扁鵲說(shuō):“你們家兄弟三人,都精于醫(yī)術(shù),到底哪一位最好呢?”
扁鵲答:“大哥最好,二哥次之,我最差。”
文王再問(wèn):“那么為什么你最出名呢?”
扁鵲答說(shuō):“我大哥治病,是治病于病情發(fā)作之前。由于一般人不知道他率先能鏟除病因,所以他的名氣無(wú)法傳出去,只有我們家的人才知道。我二哥治病,是治病于病情初起之時(shí)。一般人以為他只能治輕微的小病,所以他的名氣只及于本鄉(xiāng)里。而我扁鵲治病,是治病于病情嚴(yán)重之時(shí)。一般人都看到我在經(jīng)脈上穿針管來(lái)放血、在皮膚上敷藥等大手術(shù),所以以為我的醫(yī)術(shù)高明,名氣因此響遍全國(guó)?!?
比較起來(lái),能防范于未然是最高明的,但往往因防范在前,不會(huì)出現(xiàn)惡果,使事物保持了原態(tài),沒(méi)有“明顯”的功績(jī)而被忽略。正如不見(jiàn)防火英雄,只有救火英雄一樣。高明者不見(jiàn)得一定名聲顯赫。
建議盡量使用對(duì)象的適配器模式,少用繼承。適配器模式也是一種包裝模式,它與裝飾模式同樣具有包裝的功能,此外,對(duì)象適配器模式還具有委托的意思??偟膩?lái)說(shuō),適配器模式屬于補(bǔ)償模式,專(zhuān)門(mén)用來(lái)在系統(tǒng)后期擴(kuò)展、修改時(shí)使用,但要注意不要過(guò)度使用適配器模式。
參考文獻(xiàn):《大話設(shè)計(jì)模式》
IBM developerWorks:適配器模式原理及實(shí)例介紹
- 說(shuō)點(diǎn)什么全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter5/battcn-adapter
個(gè)人QQ:1837307557
battcn開(kāi)源群(適合新手):391619659
微信公眾號(hào):battcn(歡迎調(diào)戲)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67949.html
摘要:適配器是將接口轉(zhuǎn)換為不同接口,而外觀模式是提供一個(gè)統(tǒng)一的接口來(lái)簡(jiǎn)化接口。 外觀模式(Facade Pattern)屬于結(jié)構(gòu)型模式的一種,為子系統(tǒng)中的一組接口提供一個(gè)統(tǒng)一的入口,它通過(guò)引入一個(gè)外觀角色來(lái)簡(jiǎn)化客戶端與子系統(tǒng)之間的交互... 概述 外觀模式是一種使用頻率非常高的結(jié)構(gòu)型設(shè)計(jì)模式,當(dāng)你要為一個(gè)復(fù)雜子系統(tǒng)提供一個(gè)簡(jiǎn)單接口時(shí)。子系統(tǒng)往往因?yàn)椴粩嘌莼兊迷絹?lái)越復(fù)雜。大多數(shù)模式使用時(shí)...
摘要:設(shè)計(jì)模式的分類(lèi)經(jīng)典應(yīng)用框架中常見(jiàn)的設(shè)計(jì)模式分為三類(lèi)創(chuàng)建型模式對(duì)類(lèi)的實(shí)例化過(guò)程的抽象。對(duì)象的結(jié)構(gòu)模式是動(dòng)態(tài)的。對(duì)象的行為模式則使用對(duì)象的聚合來(lái)分配行為。設(shè)計(jì)模式是個(gè)好東西,以后肯定還要進(jìn)一步的學(xué)習(xí),并且在項(xiàng)目中多實(shí)踐,提升自己的設(shè)計(jì)能力。 什么是設(shè)計(jì)模式? Christopher Alexander?說(shuō)過(guò):每一個(gè)模式描述了一個(gè)在我們周?chē)粩嘀貜?fù)發(fā)生的問(wèn)題,以及該問(wèn)題的解決方案的核心。這樣...
摘要:基礎(chǔ)知識(shí)復(fù)習(xí)后端掘金的作用表示靜態(tài)修飾符,使用修飾的變量,在中分配內(nèi)存后一直存在,直到程序退出才釋放空間。將對(duì)象編碼為字節(jié)流稱(chēng)之為序列化,反之將字節(jié)流重建成對(duì)象稱(chēng)之為反序列化。 Java 學(xué)習(xí)過(guò)程|完整思維導(dǎo)圖 - 后端 - 掘金JVM 1. 內(nèi)存模型( 內(nèi)存分為幾部分? 堆溢出、棧溢出原因及實(shí)例?線上如何排查?) 2. 類(lèi)加載機(jī)制 3. 垃圾回收 Java基礎(chǔ) 什么是接口?什么是抽象...
摘要:進(jìn)階多線程開(kāi)發(fā)關(guān)鍵技術(shù)后端掘金原創(chuàng)文章,轉(zhuǎn)載請(qǐng)務(wù)必將下面這段話置于文章開(kāi)頭處保留超鏈接。關(guān)于中間件入門(mén)教程后端掘金前言中間件 Java 開(kāi)發(fā)人員最常犯的 10 個(gè)錯(cuò)誤 - 后端 - 掘金一 、把數(shù)組轉(zhuǎn)成ArrayList 為了將數(shù)組轉(zhuǎn)換為ArrayList,開(kāi)發(fā)者經(jīng)常... Java 9 中的 9 個(gè)新特性 - 后端 - 掘金Java 8 發(fā)布三年多之后,即將快到2017年7月下一個(gè)版...
閱讀 3692·2021-09-30 09:59
閱讀 2357·2021-09-13 10:34
閱讀 588·2019-08-30 12:58
閱讀 1517·2019-08-29 18:42
閱讀 2213·2019-08-26 13:44
閱讀 2933·2019-08-23 18:12
閱讀 3330·2019-08-23 15:10
閱讀 1634·2019-08-23 14:37