適配器模式-通過(guò)適配器來(lái)復(fù)用

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

適配器模式

定義

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

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

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

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

適配器模式角色

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

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

對(duì)象適配器

類(lèi)適配器

image

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

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

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

使用場(chǎng)景

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

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

總的來(lái)說(shuō),適配器模式屬于補(bǔ)償模式,專(zhuān)門(mén)用來(lái)在系統(tǒng)后期擴(kuò)展、修改時(shí)使用,但要注意不要過(guò)度使用適配器模式

實(shí)例

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

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

public interface TypeC {
    void useTypeCPort();
}

Adaptee適配者 - 被適配的對(duì)象

public interface Headset {
    void listen();
}

public class CommonHeadset implements Headset {

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

Adapter適配器 - 把適配對(duì)象和目標(biāo)類(lèi)關(guān)聯(lián)起來(lái),達(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();
    }
}

測(cè)試

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

測(cè)試結(jié)果

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

上面的是對(duì)象適配器,下面的是類(lèi)適配器的做法

public class CommonHeadsetAdapter2 extends CommonHeadset implements TypeC {

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

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

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

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

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

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

Java I/O 庫(kù)使用了適配器模式

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

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 AOP中的適配器模式

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

Advice的類(lèi)型有:MethodBeforeAdvice、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)換

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

Adaptee適配者類(lèi)

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);
    // 獲取對(duì)應(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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容