Android的設(shè)計(jì)模式-適配器模式

前言

Android的設(shè)計(jì)模式系列文章介紹,歡迎關(guān)注,持續(xù)更新中:

Android的設(shè)計(jì)模式-設(shè)計(jì)模式的六大原則
一句話總結(jié)23種設(shè)計(jì)模式則
創(chuàng)建型模式:
Android的設(shè)計(jì)模式-單例模式
Android的設(shè)計(jì)模式-建造者模式
Android的設(shè)計(jì)模式-工廠方法模式
Android的設(shè)計(jì)模式-簡單工廠模式
Android的設(shè)計(jì)模式-抽象工廠模式
Android的設(shè)計(jì)模式-原型模式
行為型模式:
Android的設(shè)計(jì)模式-策略模式
Android的設(shè)計(jì)模式-狀態(tài)模式
Android的設(shè)計(jì)模式-責(zé)任鏈模式
Android的設(shè)計(jì)模式-觀察者模式
Android的設(shè)計(jì)模式-模板方法模式
Android的設(shè)計(jì)模式-迭代器模式
Android的設(shè)計(jì)模式-備忘錄模式
Android的設(shè)計(jì)模式-訪問者模式
Android的設(shè)計(jì)模式-中介者模式
Android的設(shè)計(jì)模式-解釋器模式
Android的設(shè)計(jì)模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計(jì)模式-代理模式
Android的設(shè)計(jì)模式-組合模式
Android的設(shè)計(jì)模式-適配器模式
Android的設(shè)計(jì)模式-裝飾者模式
Android的設(shè)計(jì)模式-享元模式
Android的設(shè)計(jì)模式-外觀模式
Android的設(shè)計(jì)模式-橋接模式

1.定義

將一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作杆勇。

2.介紹

  • 適配器模式屬于結(jié)構(gòu)型模式。
  • 適配器模式有類適配器模式和對象適配器模式這兩種。
  • 生活中的手機(jī)充電器就是一個適配器的例子趟畏,手機(jī)一般都是在5V的電壓下進(jìn)行充電,但是外部的電壓都是220V滩租,那怎么辦赋秀,這就需要充電器去適配了,將220V的電壓轉(zhuǎn)換為5V持际。
  • 實(shí)際開發(fā)中沃琅,我們常遇到兩個類之間的接口不兼容,但是又不想去改動接口蜘欲,這可以通過適配器模式來解決這個問題益眉。
  • 放張比較形象的圖:


    適配器示例.png

3.UML類圖

對象的適配器模式UML類圖.jpg
角色說明:
  • Adapter(適配器接口):即目標(biāo)角色,定義把其他類轉(zhuǎn)換為何種接口姥份,也就是我們期望的接口郭脂。
  • Adaptee(被適配角色):即源角色,一般是已存在的類澈歉,需要適配新的接口展鸡。
  • ConcreteAdapter(具體適配器):實(shí)現(xiàn)適配器接口,把源角色接口轉(zhuǎn)換為目標(biāo)角色期望的接口埃难。

4.對象適配器模式實(shí)現(xiàn)

首先來說下對象適配器模式的實(shí)現(xiàn)方式莹弊,就以電壓轉(zhuǎn)換為例子。

4.1 創(chuàng)建適配器接口

現(xiàn)在我們需要定義一個220V轉(zhuǎn)換成5V的接口:

    interface Adapter {//適配器類

        int convert_5v();//裝換成5V
    }
4.2 創(chuàng)建被適配角色

被適配角色涡尘,一般是已存在的類忍弛,需要適配新的接口。生活中的220V電源無處不在:

    public class Electric {// 電源

        public int output_220v() {//輸出220V
            return 220;
        }
    }
4.3 創(chuàng)建具體適配器

我們需要一個具體適配器考抄,這個適配器就是變壓器细疚,能夠?qū)?20V轉(zhuǎn)為5V輸出:

     public class PhoneAdapter implements Adapter {//手機(jī)適配器類
        private Electric mElectric;//適配器持有源目標(biāo)對象

        public PhoneAdapter(Electric electric) {//通過構(gòu)造方法傳入對象
            mElectric = electric;
        }

        @Override
        public int convert_5v() {
            System.out.println("適配器開始工作:");
            System.out.println("輸入電壓:" + mElectric.output_220v());
            System.out.println("輸出電壓:" + 5);
            return 5;
        }
    }
4.4 客戶端測試:
     public void test() {
        Electric electric = new Electric();
        System.out.println("默認(rèn)電壓:" + electric.output_220v());

        Adapter phoneAdapter = new PhoneAdapter(electric);//傳遞一個對象給適配器
        System.out.println("適配轉(zhuǎn)換后的電壓:" + phoneAdapter.convert_5v());

    }
輸出結(jié)果:
默認(rèn)電壓:220
適配器開始工作:
輸入電壓:220
輸出電壓:5
適配轉(zhuǎn)換后的電壓:5
4.5 說明:

上面的例子就是對象適配器模式,適配的源目標(biāo)對象需要傳遞給適配器川梅。

5.類適配器模式實(shí)現(xiàn)

類適配器只要是通過繼承源目標(biāo)類來實(shí)現(xiàn)

5.1 創(chuàng)建適配器接口

跟4.1一樣

    interface Adapter {//適配器類

        int convert_5v();//裝換成5V
    }
5.2 創(chuàng)建被適配角色

跟4.2一樣

    public class Electric {// 電源

        public int output_220v() {//輸出220V
            return 220;
        }
    }
5.3 創(chuàng)建具體適配器

跟4.3有區(qū)別

    public class PhoneAdapter extends Electric implements Adapter {//通過繼承源目標(biāo)類的方式疯兼,不持有源目標(biāo)對象

        @Override
        public int convert_5v() {
            System.out.println("適配器開始工作:");
            System.out.println("輸入電壓:" + output_220v());
            System.out.println("輸出電壓:" + 5);
            return 5;
        }
    }
5.4 客戶端測試:

跟4.4有區(qū)別

    public void test() {
        Adapter phoneAdapter = new PhoneAdapter();
        System.out.println("適配轉(zhuǎn)換后的電壓:" + phoneAdapter.convert_5v());
    }
輸出結(jié)果:
適配器開始工作:
輸入電壓:220
輸出電壓:5
適配轉(zhuǎn)換后的電壓:5
5.5 說明:

類適配器模式只要通過繼承源目標(biāo)類來實(shí)現(xiàn),無需持有源目標(biāo)對象贫途。

5.6 對象適配器模式與類適配器模式比較
  • 類適配器采用了繼承的方式來實(shí)現(xiàn);而對象適配器是通過傳遞對象來實(shí)現(xiàn)吧彪,這是一種組合的方式。
  • 類適配器由于采用了繼承丢早,可以重寫父類的方法;對象適配器則不能修改對象本身的方法等姨裸。
  • 適配器通過繼承都獲得了父類的方法,客戶端使用時都會把這些方法暴露出去,增加了一定的使用成本;對象適配器則不會啦扬。
  • 類適配器只能適配他的父類中狂,這個父類的其他子類都不能適配到;而對象適配器可以適配不同的對象,只要這個對象的類型是同樣的扑毡。
  • 類適配器不需要額外的引用;對象適配器需要額外的引用來保存對象胃榕。

總的來說,使用對象適配器比較好瞄摊。當(dāng)然具體問題具體分析勋又。

6. 應(yīng)用場景

  • 當(dāng)想使用一個已經(jīng)存在的類,但它的接口不符合需求時换帜。
  • 當(dāng)想創(chuàng)建一個可以復(fù)用的類楔壤,該類可以與其他不相關(guān)的類或不可預(yù)見的類協(xié)同工作。

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

  • 提高了類的復(fù)用性惯驼,適配器能讓一個類有更廣泛的用途蹲嚣。
  • 提高了靈活性,更換適配器就能達(dá)到不同的效果祟牲。不用時也可以隨時刪掉適配器隙畜,對原系統(tǒng)沒影響。
  • 符合開放封閉原則说贝,不用修改原有代碼议惰。沒有任何關(guān)系的類通過增加適配器就能聯(lián)系在一起。

8. 缺點(diǎn)

  • 過多的使用適配器乡恕,會讓系統(tǒng)非常零亂言询,不易整體進(jìn)行把握。明明調(diào)用A接口傲宜,卻被適配成B接口运杭。

9. Android中的源碼分析

說到適配器,ListViewRecyclerView就再熟悉不過了蛋哭,ListView現(xiàn)在應(yīng)該很少用了吧县习,這里就以RecyclerView來分析涮母。

9.1 RecyclerView的使用

先來一個RecyclerView的簡單使用例子:

Activity布局文件 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_main_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"/>
</LinearLayout>

Item 布局文件 item_rv.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="40dp">

    <TextView
        android:id="@+id/tv_item"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        tools:text="item"/>

</LinearLayout>

Activity代碼

public class MainActivity extends Activity {

    private RecyclerView mRecyclerView;

    private RecyclerView.Adapter mAdapter;

    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initVar();
        initView();
    }

    private void initVar() {
        mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        mAdapter = new MyAdapter(getData());
    }

    private void initView() {
        mRecyclerView = findViewById(R.id.rv_main_test);
        // 設(shè)置布局管理器
        mRecyclerView.setLayoutManager(mLayoutManager);
        // 設(shè)置adapter
        mRecyclerView.setAdapter(mAdapter);
    }

    private ArrayList<String> getData() {//獲取要展示的數(shù)據(jù)
        ArrayList<String> data = new ArrayList<>();
        String temp = " item";
        for (int i = 0; i < 20; i++) {
            data.add(i + temp);
        }

        return data;
    }

}

Adapter代碼

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {//繼承RecyclerView.Adapter
    private ArrayList<String> mData;

    public MyAdapter(ArrayList<String> data) {
        this.mData = data;//初始化數(shù)據(jù)
    }


    @Override
    //創(chuàng)建一個我們自定義的ViewHolder
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 實(shí)例化要展示的view布局
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv, parent, false);
        // 實(shí)例化viewholder
        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }


    @Override
    //綁定ViewHolder
    public void onBindViewHolder(ViewHolder holder, int position) {
        // ViewHolder中的view與數(shù)據(jù)進(jìn)行綁定
        holder.mTv.setText(mData.get(position));
    }

    @Override
    //Item數(shù)量
    public int getItemCount() {
        return mData == null ? 0 : mData.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        TextView mTv;

        public ViewHolder(View itemView) {//根據(jù)實(shí)際需求谆趾,持有不同的對象。
            super(itemView);
            mTv = itemView.findViewById(R.id.tv_item);
        }
    }
}

從上面的例子可以看到叛本,使用RecyclerView時我們要傳遞一個Adapter進(jìn)去沪蓬,然后通過復(fù)寫Adapter中的onCreateViewHolder()onBindViewHolder()来候、getItemCount()等方法以及內(nèi)部類ViewHolder跷叉。通過不同的Adapter,我們能夠?qū)崿F(xiàn)不同的布局。

9.2 總結(jié)

上面的MyAdapter是繼承RecyclerView.Adapter這個類的云挟,而Adapter實(shí)際上是RecyclerView的內(nèi)部類梆砸。這里的Adapter就充當(dāng)了適配器接口,即目標(biāo)角色园欣,RecyclerView內(nèi)部通過Adapter獲取它需要的接口和數(shù)據(jù)等帖世。至于MyAdapter,就是具體的適配器沸枯,通過它把不同的布局跟RecyclerView聯(lián)系起來日矫。最后就是被適配角色,即源角色绑榴,這里就是各種不同的item布局哪轿。

為什么Android要這么設(shè)計(jì)呢,想想在一個列表中翔怎,我們要展示的布局可能是簡單的窃诉,也可能是超復(fù)雜的,這個布局是未知的,是根據(jù)要展示的數(shù)據(jù)集來決定的;通過適配器模式赤套,就能夠把不同的東西都可以轉(zhuǎn)化成同樣的接口褐奴,系統(tǒng)處理起來就有套路了,同時也無需關(guān)心各種千變?nèi)f化的布局于毙。

相關(guān)文章閱讀
Android的設(shè)計(jì)模式-設(shè)計(jì)模式的六大原則
一句話總結(jié)23種設(shè)計(jì)模式則
創(chuàng)建型模式:
Android的設(shè)計(jì)模式-單例模式
Android的設(shè)計(jì)模式-建造者模式
Android的設(shè)計(jì)模式-工廠方法模式
Android的設(shè)計(jì)模式-簡單工廠模式
Android的設(shè)計(jì)模式-抽象工廠模式
Android的設(shè)計(jì)模式-原型模式
行為型模式:
Android的設(shè)計(jì)模式-策略模式
Android的設(shè)計(jì)模式-狀態(tài)模式
Android的設(shè)計(jì)模式-責(zé)任鏈模式
Android的設(shè)計(jì)模式-觀察者模式
Android的設(shè)計(jì)模式-模板方法模式
Android的設(shè)計(jì)模式-迭代器模式
Android的設(shè)計(jì)模式-備忘錄模式
Android的設(shè)計(jì)模式-訪問者模式
Android的設(shè)計(jì)模式-中介者模式
Android的設(shè)計(jì)模式-解釋器模式
Android的設(shè)計(jì)模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計(jì)模式-代理模式
Android的設(shè)計(jì)模式-組合模式
Android的設(shè)計(jì)模式-適配器模式
Android的設(shè)計(jì)模式-裝飾者模式
Android的設(shè)計(jì)模式-享元模式
Android的設(shè)計(jì)模式-外觀模式
Android的設(shè)計(jì)模式-橋接模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敦冬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子唯沮,更是在濱河造成了極大的恐慌脖旱,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件介蛉,死亡現(xiàn)場離奇詭異萌庆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)币旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門践险,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吹菱,你說我怎么就攤上這事巍虫。” “怎么了鳍刷?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵占遥,是天一觀的道長。 經(jīng)常有香客問我输瓜,道長瓦胎,這世上最難降的妖魔是什么芬萍? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮搔啊,結(jié)果婚禮上柬祠,老公的妹妹穿的比我還像新娘。我一直安慰自己负芋,他們只是感情好瓶盛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著示罗,像睡著了一般惩猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚜点,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天轧房,我揣著相機(jī)與錄音,去河邊找鬼绍绘。 笑死奶镶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陪拘。 我是一名探鬼主播厂镇,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼左刽!你這毒婦竟也來了捺信?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤欠痴,失蹤者是張志新(化名)和其女友劉穎迄靠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喇辽,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掌挚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了菩咨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吠式。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抽米,靈堂內(nèi)的尸體忽然破棺而出特占,到底是詐尸還是另有隱情,我是刑警寧澤缨硝,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布摩钙,位于F島的核電站罢低,受9級特大地震影響查辩,放射性物質(zhì)發(fā)生泄漏胖笛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一宜岛、第九天 我趴在偏房一處隱蔽的房頂上張望长踊。 院中可真熱鬧,春花似錦萍倡、人聲如沸身弊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阱佛。三九已至,卻和暖如春戴而,著一層夾襖步出監(jiān)牢的瞬間凑术,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工所意, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淮逊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓扶踊,卻偏偏與公主長得像泄鹏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子秧耗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359