設(shè)計模式學(xué)習(xí)筆記:適配器模式-Adapter Pattern

適配器模式(Adapter Pattern):將一個接口轉(zhuǎn)換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)始腾。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對象結(jié)構(gòu)型模式空执。

適配器模式主要有兩種模式:1.類適配器模式2.對象適配器模式

其中:類適配器模式主要使用的是繼承的思想辨绊;對象適配器模式使用的組合引用的思想奶栖。
不管哪種模式下,適配者模式的主要組成部分有三部分:

  1. 目標(biāo)(Target)角色:這就是所期待得到的接口门坷。注意宣鄙,由于這里討論的是類適配器模式,因此目標(biāo)不可以是類默蚌。
  2. 源(Adaptee)角色:有目標(biāo)(Target)角色的部分功能冻晤,但是還是不滿足或者是源(Adaptee)直接不滿足目標(biāo)的接口功能,需要配置它敏簿。
  3. 適配器(Adapter)角色:適配器類是本模式的核心明也。適配器把源接口轉(zhuǎn)換成目標(biāo)接口宣虾。顯然,這一叫色不可以是接口温数,而必須是具體類绣硝。

UML圖解讀

下圖是類適配器的UML圖,其中Target是目標(biāo)接口撑刺,含有兩個待實現(xiàn)的方法鹉胖;Adaptee是源接口(需要被改造或者擴展),它有一個實現(xiàn)方法operateA够傍;然后適配器AdapterimplementsTarget接口甫菠,并且集成了Adaptee源接口。所以通過適配器類就可以有完整的Target接口功能了冕屯。最后在客戶端使用中就可以將Target接口的實例化指向Adapter類了寂诱。

類適配器模型

還有一種比較常見的適配器就是對象適配器了。Target接口和Adaptee源接口都沒有變化安聘;最大的變化還是適配器類了痰洒,首先Adapter類放棄使用繼承而是使用引用的方式來引入源接口,然后Adaptee的實例對象也是通過構(gòu)造方法延遲注入了浴韭;最后還是需要實現(xiàn)Target接口的兩個方法丘喻,只不過operateA是通過Adaptee源的實例對象執(zhí)行的。

對象適配器模型

類適配器

/**
 * @Description: 目標(biāo)接口 <br>
 */
public interface Target {
    //1.operateA是在源(Adaptee)中有實現(xiàn)的
    void operateA();
    //2.operateB在源(Adaptee)中沒有實現(xiàn)念颈,需要使用適配器
    void operateB();
}

/**
 * @Description: 源(Adaptee)角色 <br>
 */
public class Adaptee {
    public void operateA(){
        System.out.println("我是已經(jīng)實現(xiàn)的operateA");
    }
}

/**
 * @Description: 適配器(Adapter)角色 <br>
 */
public class Adapter extends Adaptee implements Target {
    //其中operateA通過繼承的方式已經(jīng)集成到Adapter中了
    @Override
    public void operateB() {
        System.out.println("我是通過適配器實現(xiàn)的operateB");
    }
}

/**
 * @Description: 類適配器模式 <br>
 */
public class AdapterClient {
    public static void main(String[] args) {
        //目標(biāo)Target是通過適配器Adapter獲得的
        Target adapter = new Adapter();
        adapter.operateA();
        adapter.operateB();
    }
}

結(jié)果:
我是已經(jīng)實現(xiàn)的operateA
我是通過適配器實現(xiàn)的operateB

對象適配器

public interface Target {
    //1.operateA是在源(Adaptee)中有實現(xiàn)的
    void operateA();
    //2.operateB在源(Adaptee)中沒有實現(xiàn)泉粉,需要使用適配器
    void operateB();
}

public class Adaptee {
    public void operateA(){
        System.out.println("在源(Adaptee)中實現(xiàn)的operateA");
    }
}

public class Adapter implements Target {

    private Adaptee adaptee;

    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }

    @Override
    public void operateA() {
        //通過傳入的源對象實現(xiàn)operateA
        this.adaptee.operateA();
    }

    @Override
    public void operateB() {
        System.out.println("我是通過適配器的實現(xiàn)接口實現(xiàn)的operateB");
    }
}

public class AdapterClient {
    public static void main(String[] args) {
        //1.實例化源 Adaptee
        Adaptee adaptee = new Adaptee();
        //2.創(chuàng)建適配者對象,并且傳入源
        Target target = new Adapter(adaptee);
        target.operateA();
        target.operateB();
    }
}

結(jié)果:
在源(Adaptee)中實現(xiàn)的operateA
我是通過適配器的實現(xiàn)接口實現(xiàn)的operateB

應(yīng)用場景

Spring 中也有適配器模式的典型應(yīng)用榴芳。

SpringAOP 中嗡靡,使用的 Advice (通知)來增強被代理類的功能。Spring 實現(xiàn)這一 AOP 功能的原理就使用代理模式(1翠语、JDK動態(tài)代理叽躯。2、CGLib字節(jié)碼生成技術(shù)代理肌括。)對類進行方法級別的切面增強点骑,即,生成被代理類的代理類谍夭, 并在代理類的方法前黑滴,設(shè)置攔截器,通過執(zhí)行攔截器的內(nèi)容增強了代理方法的功能紧索,實現(xiàn)的面向切面編程袁辈。

Advice(通知)的類型有:MethodBeforeAdviceAfterReturningAdvice珠漂、ThrowsAdvice的晚缩。

在每個類型Advice(通知)都有對應(yīng)的攔截器尾膊,MethodBeforeAdviceInterceptorAfterReturningAdviceInterceptor荞彼、ThrowsAdviceInterceptor冈敛。

Spring 需要將每個Advice(通知)都封裝成對應(yīng)的攔截器類型,返回給容器鸣皂,所以需要使用適配器模式對Advice進行轉(zhuǎn)換抓谴。下面我們看看具體的代碼。

MethodBeforeAdvice類:Adaptee

Adapter類接口:Target

MethodBeforeAdviceAdapter類寞缝,Adapter

DefaultAdvisorAdapterRegistry類癌压,Client

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
 
    private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
 
 
    /**
     * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
     */
    public DefaultAdvisorAdapterRegistry() {//這里注冊了適配器
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }
 
 
    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) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {//這里調(diào)用了適配器的方法
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }
 
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {//這里調(diào)用了適配器的方法
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }
 
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }
 
}

參考文獻

  1. 設(shè)計模式干貨系列:(七)適配器模式【學(xué)習(xí)難度:★★☆☆☆,使用頻率:★★★★☆】
  2. Spring中的設(shè)計模式-適配器模式
  3. 設(shè)計模式(二) 三種適配器模式 總結(jié)和使用場景
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荆陆,一起剝皮案震驚了整個濱河市滩届,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慎宾,老刑警劉巖丐吓,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浅悉,死亡現(xiàn)場離奇詭異趟据,居然都是意外死亡,警方通過查閱死者的電腦和手機术健,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門汹碱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荞估,你說我怎么就攤上這事咳促。” “怎么了勘伺?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵跪腹,是天一觀的道長。 經(jīng)常有香客問我飞醉,道長冲茸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任缅帘,我火速辦了婚禮轴术,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钦无。我一直安慰自己逗栽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布失暂。 她就那樣靜靜地躺著彼宠,像睡著了一般鳄虱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凭峡,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天醇蝴,我揣著相機與錄音,去河邊找鬼想罕。 笑死悠栓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的按价。 我是一名探鬼主播惭适,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼楼镐!你這毒婦竟也來了癞志?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤框产,失蹤者是張志新(化名)和其女友劉穎凄杯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秉宿,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡戒突,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了描睦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膊存。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忱叭,靈堂內(nèi)的尸體忽然破棺而出隔崎,到底是詐尸還是另有隱情,我是刑警寧澤韵丑,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布爵卒,位于F島的核電站,受9級特大地震影響撵彻,放射性物質(zhì)發(fā)生泄漏钓株。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一千康、第九天 我趴在偏房一處隱蔽的房頂上張望享幽。 院中可真熱鬧,春花似錦拾弃、人聲如沸值桩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奔坟。三九已至携栋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咳秉,已是汗流浹背婉支。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澜建,地道東北人向挖。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像炕舵,于是被迫代替她去往敵國和親何之。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354