適配器模式-通過適配器來復(fù)用

適配器模式是一種結(jié)構(gòu)型設(shè)計(jì)模式唤反,適配器模式主要是來解決接口不兼容的問題吟榴,使得原本沒有關(guān)系的類可以協(xié)同工作。就好像我們筆記本電腦的電源適配器昔案,在電壓110v-220v之間都是可以正常工作的(那么大一坨)乒验,這適配器的工作就是把外部不穩(wěn)定的電壓轉(zhuǎn)化成筆記本電腦能穩(wěn)定使用的直流電壓

適配器模式

定義

適配器模式(Adapter Pattern)- 將一個(gè)類的接口變換成客戶端所期待的另一種接口愚隧,從而使原本因接口不匹配而無法在一起工作的兩個(gè)類能夠在一起工作

使用適配器模式其實(shí)就是把一個(gè)接口或者類轉(zhuǎn)換成其他的接口和類,使其可以和其他模塊一起工作锻全,也可以被稱為包裝模式(Wrapper)狂塘,同樣有包裝的功能還有裝飾模式。適配器模式主要應(yīng)用于想要復(fù)用現(xiàn)有的類鳄厌,但是接口又與復(fù)用環(huán)境不一致的情況下

經(jīng)常換手機(jī)的人可能會(huì)知道荞胡,手機(jī)的充電接口有幾種,安卓的接口現(xiàn)在大多是Type-C了了嚎,一些低端機(jī)可能還在用Micro USB接口泪漂,而蘋果則是用Lightining接口,假如我現(xiàn)在買了個(gè)新手機(jī)是Type-C接口歪泳,那我以前的那個(gè)Micro USB數(shù)據(jù)線還能不能繼續(xù)用呢萝勤,是可以用的,只不過需要加上一個(gè)轉(zhuǎn)接頭呐伞,這個(gè)轉(zhuǎn)接頭便是一個(gè)適配器

適配器模式結(jié)構(gòu)

適配器模式角色

  • 目標(biāo)類(Target):定義客戶所需接口敌卓,可以是一個(gè)抽象類或接口,也可以是具體類
  • 適配者(Adaptee):或者叫源對象伶氢,需要被適配的角色趟径,它是已經(jīng)存在的類或?qū)ο蟊窭簦m配者類一般是一個(gè)具體類,包含了客戶希望使用的業(yè)務(wù)方法蜗巧,在某些情況下可能沒有適配者類的源代碼
  • 適配器(Adapter):它的職責(zé)就是要把適配者轉(zhuǎn)換成目標(biāo)角色掌眠,對Adaptee和Target進(jìn)行適配,在對象適配器中幕屹,它通過繼承Target并關(guān)聯(lián)一個(gè)Adaptee對象使二者產(chǎn)生聯(lián)系

適配器有兩種分類:對象適配器扇救、類(或接口)適配器。在對象適配器模式中香嗓,適配器與適配者之間是關(guān)聯(lián)關(guān)系;在類適配器模式中装畅,適配器與適配者之間是繼承(或?qū)崿F(xiàn))關(guān)系

對象適配器

類適配器

image

優(yōu)點(diǎn)和使用場景

優(yōu)點(diǎn)

  • 可以讓兩個(gè)沒有關(guān)系的類在一起運(yùn)行靠娱,引入一個(gè)適配器,可以不用修改原有代碼
  • 增加類的透明度掠兄,客戶端只調(diào)用Target像云,不關(guān)心具體實(shí)現(xiàn)
  • 提高類的復(fù)用度,原適配者類的功能還可以正常使用
  • 靈活性和擴(kuò)展性非常好蚂夕,符合“開閉原則”

使用場景

  • 系統(tǒng)需要使用一些現(xiàn)有的類迅诬,而這些類的接口(如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源代碼
  • 修改已投產(chǎn)的接口婿牍,可以考慮適配器模式

注意侈贷,適配器模式通常是用來解決系統(tǒng)擴(kuò)展的問題,在系統(tǒng)開發(fā)過程中或已經(jīng)項(xiàng)目維護(hù)中等脂,當(dāng)需要引入第三方的功能或要擴(kuò)展的內(nèi)容不符合原有設(shè)計(jì)的時(shí)候俏蛮,才會(huì)考慮通過適配器模式來減少代碼修改帶來的風(fēng)險(xiǎn)。另外上遥,項(xiàng)目一定要遵守依賴倒置原則里氏替換原則搏屑,這樣在使用適配器模式時(shí),才不會(huì)有太多改動(dòng)

總的來說粉楚,適配器模式屬于補(bǔ)償模式辣恋,專門用來在系統(tǒng)后期擴(kuò)展、修改時(shí)使用模软,但要注意不要過度使用適配器模式

實(shí)例

前些天剛?cè)胧忠慌_(tái)新手機(jī)伟骨,滿心歡喜啊,拿到手就忍不住開始“摩擦“撵摆,掂量一圈發(fā)現(xiàn)現(xiàn)在的手機(jī)是越來越好看底靠、越來越薄了呀,一開心就想著拿出我的耳機(jī)想要沉浸在音樂的世界里特铝,可下一秒蒙圈了暑中,泥馬沒有耳機(jī)孔壹瘟。原來啊,為了把手機(jī)做的更薄鳄逾、更美稻轨,手機(jī)廠家犧牲了傳統(tǒng)的3.mm耳機(jī)插孔,而使用復(fù)用充電接口Type-C雕凹,這下難道我又要去再搞一條Type-C接口的耳機(jī)嗎殴俱?答案是可以不用,可以買一個(gè)接口轉(zhuǎn)換器

Target目標(biāo)類 - 要求使用Type-C接口

public interface TypeC {
    void useTypeCPort();
}

Adaptee適配者 - 被適配的對象

public interface Headset {
    void listen();
}

public class CommonHeadset implements Headset {

    @Override
    public void listen() {
        System.out.println("使用3.5mm耳機(jī)享受音樂...");
    }
}

Adapter適配器 - 把適配對象和目標(biāo)類關(guān)聯(lián)起來枚抵,達(dá)到轉(zhuǎn)換的目的

public class CommonHeadsetAdapter implements TypeC {

    private CommonHeadset headset;

    public CommonHeadsetAdapter(CommonHeadset headset){
        this.headset = headset;
    }

    @Override
    public void useTypeCPort() {
        System.out.println("使用Type-C轉(zhuǎn)接頭");
        this.headset.listen();
    }
}

測試

@Test
public void adapterTest(){
    CommonHeadset headset = new CommonHeadset();
    CommonHeadsetAdapter headsetAdapter = new CommonHeadsetAdapter(headset);
    headsetAdapter.useTypeCPort();
}

測試結(jié)果

使用Type-C轉(zhuǎn)接頭
使用3.5mm耳機(jī)享受音樂...

上面的是對象適配器线欲,下面的是類適配器的做法

public class CommonHeadsetAdapter2 extends CommonHeadset implements TypeC {

    @Override
    public void useTypeCPort() {
        System.out.println("使用Type-C轉(zhuǎn)接頭");
        super.listen();
    }
}

適配器模式應(yīng)用

在我看來適配器模式主要是用在系統(tǒng)的擴(kuò)展,或接入第三方接口汽摹。我們都不想破壞原有的設(shè)計(jì)和結(jié)構(gòu)李丰,為了適應(yīng)新的需求,在它們之間增加一個(gè)適配層逼泣,這個(gè)適配器角色為我們轉(zhuǎn)換數(shù)據(jù)或做連接趴泌。這樣新的接口就能流暢調(diào)用舊的接口,達(dá)到復(fù)用的目的拉庶,符合開閉原則

要想理解適配器模式的應(yīng)用或自己使用適配器嗜憔,只要理清角色分工就很容易了。需要擴(kuò)展一個(gè)怎樣的新需求 (目標(biāo)角色)氏仗,但不改變原有的設(shè)計(jì)結(jié)構(gòu) (被適配角色)吉捶,這個(gè)被適配角色可以是現(xiàn)有的接口、對象廓鞠、類或第三方API帚稠,然后使用一個(gè)中間角色 (適配器)做數(shù)據(jù)轉(zhuǎn)換或功能調(diào)用,使其滿足新需求

JDBC - 驅(qū)動(dòng)程序 - 數(shù)據(jù)庫引擎API

Java程序是通過JDBC來跟數(shù)據(jù)庫連接的床佳,JDBC給出了一套抽象的接口滋早,即目標(biāo)接口。而各種數(shù)據(jù)庫就是我們要適配的對象的砌们,在JDBC接口和數(shù)據(jù)庫引擎API之間需要一個(gè)適配器杆麸,而這個(gè)適配器就是驅(qū)動(dòng)程序。(比如MySql的連接程序就是一個(gè)適配器浪感,它把數(shù)據(jù)庫的API操作適配成JDBC的Java操作)

Java I/O 庫使用了適配器模式

ByteArrayInputStream是一個(gè)適配器類昔头,它繼承了InputStream的接口,并且封裝了一個(gè) byte 數(shù)組影兽。它將一個(gè) byte 數(shù)組的接口適配成InputStream流處理器的接口揭斧;

FileOutputStream 繼承了 OutputStream 類型,同時(shí)持有一個(gè)對 FileDiscriptor 對象的引用。這是一個(gè)將 FileDiscriptor 接口適配成 OutputStream 接口形式的對象適配器模式讹开;

Reader類型的原始流處理器都是適配器模式的應(yīng)用盅视。StringReader是一個(gè)適配器類,StringReader類繼承了Reader類型旦万,持有一個(gè)對String對象的引用闹击。它將String的接口適配成 Reader 類型的接口

Spring AOP中的適配器模式

在Spring的Aop中,使用Advice(通知)來增強(qiáng)被代理類的功能成艘。

Advice的類型有:MethodBeforeAdvice赏半、AfterReturningAdviceThrowsAdvice淆两,而每個(gè)類型的Advice都有對應(yīng)的攔截器:MethodBeforeAdviceInterceptor断箫、AfterReturningAdviceInterceptorThrowsAdviceInterceptor秋冰。Spring需要將每個(gè)Advice都封裝成對應(yīng)的攔截器類型瑰枫,返回給容器,所以需要使用適配器模式對Advice進(jìn)行轉(zhuǎn)換

在這個(gè)應(yīng)用中丹莲,Advice增強(qiáng)類其實(shí)只是普通的對象(被適配角色),而我們的目標(biāo)是通過攔截器對切入點(diǎn)增強(qiáng)功能尸诽,所以通過適配器將我們的Advice適配成攔截器達(dá)到增強(qiáng)切入點(diǎn)的目的

Adaptee適配者類

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

public interface AfterReturningAdvice extends AfterAdvice {
    void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}

public interface ThrowsAdvice extends AfterAdvice {
}

Target目標(biāo)接口

public interface AdvisorAdapter {
    // 判斷是否支持該增強(qiáng)甥材,即是否匹配
    boolean supportsAdvice(Advice advice);
    // 獲取對應(yīng)的攔截器
    MethodInterceptor getInterceptor(Advisor advisor);
}

Adapter適配器

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);
    }
}

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof AfterReturningAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    }
}

class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof ThrowsAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        return new ThrowsAdviceInterceptor(advisor.getAdvice());
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市性含,隨后出現(xiàn)的幾起案子洲赵,更是在濱河造成了極大的恐慌,老刑警劉巖商蕴,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠萍,死亡現(xiàn)場離奇詭異,居然都是意外死亡绪商,警方通過查閱死者的電腦和手機(jī)苛谷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來格郁,“玉大人腹殿,你說我怎么就攤上這事±椋” “怎么了锣尉?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長决采。 經(jīng)常有香客問我自沧,道長,這世上最難降的妖魔是什么树瞭? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任拇厢,我火速辦了婚禮爱谁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旺嬉。我一直安慰自己管行,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布邪媳。 她就那樣靜靜地躺著捐顷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雨效。 梳的紋絲不亂的頭發(fā)上迅涮,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機(jī)與錄音徽龟,去河邊找鬼叮姑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛据悔,可吹牛的內(nèi)容都是我干的传透。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼极颓,長吁一口氣:“原來是場噩夢啊……” “哼朱盐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起菠隆,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤兵琳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后骇径,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躯肌,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年破衔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了清女。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晰筛,死狀恐怖校仑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情传惠,我是刑警寧澤迄沫,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站卦方,受9級(jí)特大地震影響羊瘩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一尘吗、第九天 我趴在偏房一處隱蔽的房頂上張望逝她。 院中可真熱鬧,春花似錦睬捶、人聲如沸黔宛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臀晃。三九已至,卻和暖如春介劫,著一層夾襖步出監(jiān)牢的瞬間徽惋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工座韵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留险绘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓誉碴,卻偏偏與公主長得像宦棺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子黔帕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容