04刑枝、適配器模式--Adapter

本節(jié)大綱

PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.reibang.com/p/1edf5d944abb
本文出自 TigerChain 簡書 人人都會(huì)設(shè)計(jì)模式

教程簡介

正文

一滤港、什么是適配器模式

1廊蜒、生活中的適配器

比如電腦轉(zhuǎn)接器「這里主要是指連接電腦和投影儀的」,以我的 MAC 電腦為例子溅漾,我們公司的投影支持 VGA 和 HDMI 山叮,但是我的 MAC 電腦只有一個(gè) MINI DP 接口,如何把 MINI DP 轉(zhuǎn)成 VGA 或 HDMI 添履,那么我就買了這個(gè)玩意「全稱 MINI DP 轉(zhuǎn) VGA & HDMI 適配器」屁倔,這東西就是一個(gè)適配器

MINI DP 轉(zhuǎn) VGA 或 HDMI

這個(gè)適配器就可以把 MAC 和有 VGA 或 HDMI 的設(shè)備連接起來了,如下:

適配器連接各個(gè)設(shè)備

類似的還有電腦電源適配器暮胧,變壓器「也是一種適配器」锐借,其實(shí)凈水器也可以看作是一種適配器「把雜水變成純凈水」,等等

2往衷、程序中的適配器

比如我們對(duì)接第三方的接口到我們的系統(tǒng)「對(duì)方給我們的接口钞翔,我們現(xiàn)在的接口對(duì)接不起來」

我們接口和三方接口

我們就需要寫一個(gè)中間層「適配器」,做為一個(gè)橋梁炼绘,把兩個(gè)接口連接起來

接口或系統(tǒng)之間的適配器

適配器模式的定義

通俗的說適配器模式就是把兩個(gè)不兼容的接口連接起來嗅战,類似一個(gè)橋梁的作用

適配器模式類比

注:適配器模式類比一個(gè)橋梁作用「它的作用不僅僅連接這么簡單,還有轉(zhuǎn)化等操作俺亮,橋梁就是為了方便理解」

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

角色 類別 說明
Target 目標(biāo)角色 是一個(gè)接口驮捍,也就是我們期待要轉(zhuǎn)化成的接口
Adaptee 源角色 原始的類或接口對(duì)象
Adapter 適配器角色 把源角色轉(zhuǎn)化成目標(biāo)角色的類

適配器模式的分類

  • 1、類適配器模式

類適配器簡單的 UML

類適配器簡單的 UML

總結(jié)一下就是:適配器「Adapter」繼承源類「Src」并且實(shí)現(xiàn)目標(biāo)「Dst」接口,來實(shí)現(xiàn) Src-->Dst 的轉(zhuǎn)換

  • 2脚曾、對(duì)象適配器模式

對(duì)象適配器簡單的 UML

對(duì)象適配器簡單的 UML

總結(jié)一下就是:適配器「Adapter」持有源類「Src」的引用,并實(shí)現(xiàn)目標(biāo)「Dst」接口,來實(shí)現(xiàn) Src--> Dst 的轉(zhuǎn)化

  • 3东且、接口適配器模式

對(duì)于這種模式「資料上也沒有說有這種模式,我是在寫代碼的過程中發(fā)現(xiàn)可以這樣寫」本讥,我持保留意見珊泳,如果有什么問題,大家完全可以說適配器模式的分類就有以上兩種模式拷沸,可我認(rèn)為這是適配器模式的一個(gè)變種

接口適配器簡單的 UML

接口適配器

*總結(jié)一下就是: 適配器實(shí)現(xiàn)源和目標(biāo)色查,把源轉(zhuǎn)化成目標(biāo)這么一個(gè)過程

二、適配器模式舉例

1撞芍、Mac 電腦連接投影儀適配器

以開頭的例子為例子, MAC 電腦要連接投影儀器秧了,需要一個(gè) MINI DP 轉(zhuǎn) VGA & HDMI 適配器,然后才能連接上投影儀

所以這里目標(biāo)是 VGAORHDMI 序无,源是 MINI DP 適配器就是上面的那根線

類適配投影儀和 MAC 電腦簡單的 UML

適配投影儀和 MAC 電腦簡單的 UML

根據(jù) UML 擼碼

使用類適配器模式

  • 1验毡、定義目標(biāo)接口 VgaOrHdmi
/**
 * 目標(biāo)角色衡创,對(duì)投影儀來說就要 VAG 或 HDMI
 * @auther TigerChain
 */
public interface VgaOrHdmi {
    /**輸出 VGA 或是 Hdmi 接口*/
    String getVgaOrHdmi() ;
}

  • 2、定義源類 MiniDp
/**
 * 源角色晶通,MAC 電腦上的 MINIDP 接口
 * @auther TigerChain
 */
public class MiniDp {

    public String outPutMinkDp(){
        return "我是 mac 上的 MiniDp 輸入接口" ;
    }
}
  • 3璃氢、定義適配器類 MidiDp2VgaOrHdmiAdapter
/**
 * 適配器,既是 MINIDP 接口也是 VAGORHDMI 接口狮辽,這樣就可以把 MINIDP 轉(zhuǎn)成
 * VAG OR HDMI 接口
 * @auther TigerChain
 */
public class MidiDp2VgaOrHdmiAdapter extends MiniDp implements VgaOrHdmi{

    @Override
    public String getVgaOrHdmi() {
       return  convertMiniDp2VgaOrHdmi() ;
    }
    /**
     * 把 MINIDP 轉(zhuǎn)化成 VAG 或 HDMI 方法
     * @return
     */
    private String convertMiniDp2VgaOrHdmi(){
        //拿到源
        String str = outPutMinkDp();
        System.out.println(str+" \n 經(jīng)過適配器轉(zhuǎn)化 ");
        // 為這簡單起見一也,這里直接修改源
        str = "輸出變成  VGA 和 HDMI 接口" ;
        return str ;
    }
}
  • 4、定義打印機(jī)類 Projector
/**
 * 這是投影儀隘竭,我就是 VGA 和 HDMI 接口的
 * @auther TigerChain
 */
public class Projector {
        // 我要的就是 VGA 或者 HDMI 接口
        public String getVgaOrHdmi(VgaOrHdmi vgaOrHdmi){
            return vgaOrHdmi.getVgaOrHdmi() ;
        }
}

  • 5塘秦、定義測(cè)試類 Test
/**
 * 測(cè)試類
 * @auther TigerChain
 */
public class Test {
    public static void main(String args[]){
        //投影儀
        Projector projector = new Projector() ;
        //適配器
        VgaOrHdmi adapter = new MidiDp2VgaOrHdmiAdapter() ;
        // 最后得到投影儀想要的 VAG or HDMI 即可
        String str = projector.getVgaOrHdmi(adapter) ;
        System.out.println(str);
    }
}

  • 6讼渊、運(yùn)行查看結(jié)果
類適配器實(shí)現(xiàn) mini2vga

完美轉(zhuǎn)化了有木有

對(duì)象適配器實(shí)現(xiàn)上述例子

對(duì)象適配投影儀和 MAC 電腦簡單的 UML

對(duì)象適配投影儀和 MAC 電腦簡單的 UML

是不是和上面的圖一樣动看?錯(cuò),肯定不一樣爪幻,一樣我還貼出來「我又沒病」菱皆,只有一點(diǎn)改變,就是適配器不是繼承源挨稿,而是持有源的引用仇轻,代碼修改起來非常簡單,只是修改適配器即可「別的代碼都是一樣的」

  • 1奶甘、修改 MidiDp2VgaOrHdmiAdapter
/**
 * 適配器篷店,既是 MINIDP 接口也是 VAGORHDMI 接口,這樣就可以把 MINIDP 轉(zhuǎn)成
 * VAG OR HDMI 接口
 */
public class MidiDp2VgaOrHdmiAdapter  implements VgaOrHdmi{
    
    // 修改之處 1 
    private MiniDp miniDp ;
    // 修改之處 2
    public MidiDp2VgaOrHdmiAdapter(MiniDp miniDp){
        this.miniDp = miniDp ;
    }

    @Override
    public String getVgaOrHdmi() {
       return  convertMiniDp2VgaOrHdmi() ;
    }
    /**
     * 把 MINIDP 轉(zhuǎn)化成 VAG 或 HDMI 方法
     * @return
     */
    private String convertMiniDp2VgaOrHdmi(){
        // 修改之處 3  拿到源
        String str = miniDp.outPutMinkDp();
        System.out.println(str+" \n 經(jīng)過適配器轉(zhuǎn)化 ");
        // 為這簡單起見臭家,這里直接修改源
        str = "輸出變成  VGA 和 HDMI 接口" ;
        return str ;
    }
}
  • 2疲陕、修改測(cè)試類,并運(yùn)行查看結(jié)果

修改測(cè)試類

對(duì)象適配器測(cè)試類

結(jié)果和上面是一樣的

對(duì)象適配器實(shí)現(xiàn) mini2vga

適配器模式一般情況下不是軟件設(shè)計(jì)的時(shí)候就要考慮的一種模式钉赁,它是一種隨著軟件的維護(hù)可能由于不同的開發(fā)人員蹄殃,不同的產(chǎn)品,不同的廠家造成的功能類似而接口不相同的情況下一種解決方案「只有碰到無法改變?cè)性O(shè)計(jì)和代碼的情況下你踩,才考慮適配」

2诅岩、成龍初探好萊塢

我們的功夫明星成龍初闖好萊塢的時(shí)候有一個(gè)最大的障礙就是語言問題「英文不太熟悉」,那么最早的時(shí)候都是有翻譯者的带膜,那么這個(gè)翻譯員就充當(dāng)了適配器的角色「把英文翻譯成中文吩谦,或者把中文翻譯成英文」

翻譯員簡單的 UML

翻譯員簡單的 UML

根據(jù) UML 擼代碼

  • 1、新建 ISpeakEn 接口
/**
 * Created by tigerchain on 11/12/17.
 */
public interface ISpeakEn {
    // 說英文
    String speakEnglish(String str) ;
}
  • 2膝藕、新建 ISpeakCn 接口
/**
 * Created by tigerchain on 11/12/17.
 */
public interface ISpeakCn {
    // 說中文
    String speakCn(String str) ;
}
  • 3式廷、翻譯的接口「適配器」 Interpreter
/**
 * Created by tigerchain on 11/12/17.
 * 翻譯的接口
 */
public interface Interpreter {

    // 中文翻譯成英文
    void chinese2English(String str) ;
    // 英文翻譯成中文
    void english2Chinese(String str) ;
}
  • 4、具體的翻譯員小張 ZhangTranslation
/**
 * Created by tigerchain on 11/12/17.
 * 舉個(gè)例子束莫,成龍有一個(gè)張翻譯懒棉,能把英文翻譯成中文草描,也能把中文翻譯成英文
 */
public class ZhangTranslation implements Iinterpreter,ISpeakCn,ISpeakEn{

    @Override
    public void chinese2English(String str) {
        translationC2E(speakCn(str));
    }

    @Override
    public void english2Chinese(String str) {
        translationE2C(str) ;
    }
    // 翻譯英文--> 中文
    private void translationE2C(String str) {
        System.out.println("小張把 "+str+" 翻譯成中文");
    }

    // 翻譯中文--> 英文
    private void translationC2E(String str){
        System.out.println("小張把 "+str+" 翻譯成英文");
    }
    @Override
    public String speakCn(String str) {
        return str ;
    }

    @Override
    public String speakEnglish(String str) {
        return str;
    }
}

  • 5、來一個(gè)老外「要對(duì)話肯定要有關(guān)建人物呀」 Foreigner
/**
 * Created by tigerchain on 11/12/17.
 * 一個(gè)老外用英文給成龍打招呼
 */
public class Foreigner implements ISpeakEn {

    @Override
    public String speakEnglish(String str) {
        String say = "Wills say:"+str ;
        System.out.println(say);
        return say ;
    }
}
  • 6策严、成龍上場(chǎng)「另一個(gè)關(guān)建人物」 JackieChan
/**
 * Created by tigerchain on 11/12/17.
 */
public class JackieChan implements ISpeakCn {

    @Override
    public String speakCn(String str) {
        String say = "成龍說:"+str ;
        System.out.println(say);
        return say ;
    }
}
  • 7穗慕、測(cè)試對(duì)話 Test
/**
 * Created by tigerchain on 11/12/17.
 * 這是一個(gè)成龍對(duì)話老外的測(cè)試類
 */
public class Test {

    public static void main(String args[]){
        // 成龍說了一句話
        JackieChan jackieChan = new JackieChan() ;
        String str = jackieChan.speakCn("你好 wills");

        // 老外說了一句
        Foreigner foreigner = new Foreigner() ;
        String str2 = foreigner.speakEnglish("Hello Jackie Chain");

        // 張翻譯翻譯
        ZhangTranslation zhangTranslation = new ZhangTranslation() ;
        zhangTranslation.chinese2English(str);
        zhangTranslation.english2Chinese(str2);
    }
}
  • 8、運(yùn)行查看結(jié)果
成龍對(duì)話結(jié)果

怎么樣這個(gè)張翻譯「適配器」還不錯(cuò)吧妻导,當(dāng)然適配器模式也會(huì)進(jìn)化逛绵,會(huì)變種,但是萬變不離其宗「上面 Demo 就可以看作是一個(gè)變種的適配器模式」

三倔韭、Android 源碼中的適配器模式

ListAdapter

沒有搞錯(cuò)吧术浪,上一節(jié)不是說了 ListAdapter 是一種策略模式嗎?沒錯(cuò)它也是一種適配器模式「從名字就可以看出來」

ListAdapter 適配器簡單的 UML

ListAdapter 適配器 UML

從上圖可以看出寿酌,BaseAdapter 是一個(gè)基礎(chǔ)適配器胰苏,下面子類是具體各自的適配器,這些適配器的作用就是把數(shù)據(jù) List<T>醇疼,Cusor 等轉(zhuǎn)化成 ListAdapter 接口硕并,最終讓客戶端 ListView 來調(diào)用「可以通俗的說就是把數(shù)據(jù)適配到 View 上面」

以 ArrayAdapter<T> 源碼分析一下

  • 1、先看看 Adapter
public interface Adapter {
int getCount();

    Object getItem(int var1);

    long getItemId(int var1);

    boolean hasStableIds();

    View getView(int var1, View var2, ViewGroup var3);

    int getItemViewType(int var1);

    int getViewTypeCount();

    boolean isEmpty();
 }
  • 2秧荆、ListAdapter 繼承 Adapter 接口倔毙,所以擁有 Adapter 所有功能
  • 3、BaseAdapter 實(shí)現(xiàn) ListAdapter 所以不僅擁有 ListAdapteer 的所有能力
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter{

    //省略苦干代碼
    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEnabled(int position) {
        return true;
    }

    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }

    public int getItemViewType(int position) {
        return 0;
    }

    public int getViewTypeCount() {
        return 1;
    }
    
    public boolean isEmpty() {
        return getCount() == 0;
    }   
}
  • 3乙濒、再來看看 ArrayAdapter<T>
public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {

 private List<T> mObjects;  
    //列出其中一個(gè)構(gòu)造方法 
     public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
            @IdRes int textViewResourceId) {
        this(context, resource, textViewResourceId, new ArrayList<>());
    }
    
    @Override
    public int getCount() {
        return mObjects.size();
    }

    @Override
    public @Nullable T getItem(int position) {
        return mObjects.get(position);
    }

    public int getPosition(@Nullable T item) {
        return mObjects.indexOf(item);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public @NonNull View getView(int position, @Nullable View convertView,
            @NonNull ViewGroup parent) {
        return createViewFromResource(mInflater, position, convertView, parent, mResource);
    }
    //其它代碼流省略
}

我們通過源碼可以看到 ArrayAdapter<T> 就是把 List<T> 的數(shù)據(jù)源采用一系列方法轉(zhuǎn)化成 ListAdapter 需要的幾種方法 getView getPosition 等等「這就是一個(gè)適配的過程」

ListAdapter 既是策略模式又是適配器模式

根據(jù)選擇模式使用那種適配器 ListAdapter 就是策略模式陕赃,但是根據(jù)每個(gè)策略所實(shí)現(xiàn)功能「它就是適配器模式」

四、適配器模式的優(yōu)缺點(diǎn)

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

  • 1颁股、客戶端只關(guān)心適配器么库,對(duì)客戶端來說更簡單
  • 2、現(xiàn)有類的復(fù)用而不需要改變豌蟋,解決了現(xiàn)有類和目標(biāo)類環(huán)境不一致的問題
  • 3廊散、解耦「目標(biāo)類和適配器解耦」,不用改變?cè)械拇a梧疲,再一個(gè)就是某天目標(biāo)大變了允睹,那么我們?cè)倬帉懸粋€(gè)適配器就可以了「原來的適配器可以扔掉了,就像某天你的 MAC 筆記本壞了幌氮,電源適配器就可以扔了--這是一個(gè)玩笑缭受,除非是適配器不適用新買的 MAC」

缺點(diǎn)

  • 1、適配器編寫過程需要多方考慮「可能會(huì)很復(fù)雜」
  • 2该互、適配器把一個(gè)接口轉(zhuǎn)化成另一個(gè)接口米者,在客戶端會(huì)給人誤導(dǎo),明明傳入的是 A 接口,最后成 B 了蔓搞,讓人很暈

到此為止胰丁,我們就介紹完了適配器模式,點(diǎn)贊是一種美德

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喂分,一起剝皮案震驚了整個(gè)濱河市锦庸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒲祈,老刑警劉巖甘萧,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梆掸,居然都是意外死亡扬卷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門酸钦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怪得,“玉大人,你說我怎么就攤上這事钝鸽』阈簦” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵拔恰,是天一觀的道長。 經(jīng)常有香客問我基括,道長颜懊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任风皿,我火速辦了婚禮河爹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桐款。我一直安慰自己咸这,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布魔眨。 她就那樣靜靜地躺著媳维,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遏暴。 梳的紋絲不亂的頭發(fā)上侄刽,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音朋凉,去河邊找鬼州丹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的墓毒。 我是一名探鬼主播吓揪,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼所计!你這毒婦竟也來了磺芭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤醉箕,失蹤者是張志新(化名)和其女友劉穎钾腺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讥裤,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡放棒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了己英。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片间螟。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖损肛,靈堂內(nèi)的尸體忽然破棺而出厢破,到底是詐尸還是另有隱情,我是刑警寧澤治拿,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布摩泪,位于F島的核電站,受9級(jí)特大地震影響劫谅,放射性物質(zhì)發(fā)生泄漏见坑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一捏检、第九天 我趴在偏房一處隱蔽的房頂上張望荞驴。 院中可真熱鬧,春花似錦贯城、人聲如沸熊楼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鲫骗。三九已至,卻和暖如春悲雳,著一層夾襖步出監(jiān)牢的瞬間挎峦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工合瓢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坦胶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像顿苇,于是被迫代替她去往敵國和親峭咒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344