Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從Android代碼中來記憶23種設計模式

從Android代碼中來記憶23種設計模式

編輯:關於Android編程

本文轉載於  huachao1001的專欄

相信大家都曾經下定決心把23種設計模式牢記於心,每次看完之後過一段時間又忘記了~,又得回去看,腦子裡唯一依稀記得的是少數設計模式的大致的定義。其實,網上很多文章講得都非常好,我也曾經去看過各種文章。也曾一直苦惱這些難以永久記下的設計模式,直到我接觸到了《Android源碼設計模式解析與實戰》——何紅輝與關愛明著,發現原來其實我們在Android中都接觸過這些設計模式,只是我們不知道而已。既然我們都接觸過,我們只需一一對號入座,對設計模式的記憶就不用死記硬背了!這裡自願無償做個廣告,《Android源碼設計模式解析與實戰》這本書真心不錯,每個Android程序員最好都去翻翻…正如你所想的那樣,本文是從這本書中的總結,相信你也會跟我一樣,從中獲益。

面向對象的六大原則

首先,我們為什麼要學習設計模式。主要是這些模式是前人總結的經驗,使用這些模式能讓我們的程序更健壯、更穩定、容易擴展等等優點。在編寫面向對象程序時,我們需要遵循以下6個原則,能讓我們的程序維護起來更輕松~(當然還有其它好處)。

1 單一職責原則
單一原則很簡單,就是將一組相關性很高的函數、數據封裝到一個類中。換句話說,一個類應該有職責單一。

2 開閉原則

開閉原則理解起來也不復雜,就是一個類應該對於擴展是開放的,但是對於修改是封閉的。我們知道,在開放的app或者是系統中,經常需要升級、維護等,這就要對原來的代碼進行修改,可是修改時容易破壞原有的系統,甚至帶來一些新的難以發現的BUG。因此,我們在一開始編寫代碼時,就應該注意盡量通過擴展的方式實現新的功能,而不是通過修改已有的代碼實現。

3 裡氏替換原則

裡氏替換原則的定義為:所有引用基類的地方必須能透明地使用其子類對象。定義看起來很抽象,其實,很容易理解,本質上就是說,要好好利用繼承和多態。簡單地說,就是以父類的形式聲明的變量(或形參),賦值為任何繼承於這個父類的子類後不影響程序的執行。看一組代碼你就明白這個原則了:

//窗口類
public class Window(){
    public void show(View child){
        child.draw();
    }
}
public abstract class View(){
    public abstract void draw();
    public void measure(int widht,int height){
        //測量視圖大小
    }
}
public class Button extends View{
    public void draw(){
        //繪制按鈕
    }
}

public class TextView extends View{
    public void draw(){
        //繪制文本
    }
}

Window 類中show函數需要傳入View,並且調用View對象的draw函數。而每個繼承於View的子對象都有draw的實現,不存在繼承於View但是卻沒實現draw函數的子類(abstract方法必須實現)。我們在抽象類設計之時就運用到了裡氏替換原則。

4 依賴倒置原則

依賴倒置主要是實現解耦,使得高層次的模塊不依賴於低層次模塊的具體實現細節。怎麼去理解它呢,我們需要知道幾個關鍵點:

(1)高層模塊不應該依賴底層模塊(具體實現),二者都應該依賴其抽象(抽象類或接口)
(2)抽象不應該依賴細節(廢話,抽象類跟接口肯定不依賴具體的實現了)
(3)細節應該依賴於抽象(同樣廢話,具體實現類肯定要依賴其繼承的抽象類或接口)

其實,在我們用的Java語言中,抽象就是指接口或者抽象類,二者都是不能直接被實例化;細節就是實現類,實現接口或者繼承抽象類而產生的類,就是細節。使用Java語言描述就簡單了:就是各個模塊之間相互傳遞的參數聲明為抽象類型,而不是聲明為具體的實現類;

5 接口隔離原則

接口隔離原則定義:類之間的依賴關系應該建立在最小的接口上。其原則是將非常龐大的、臃腫的接口拆分成更小的更具體的接口。

6 迪米特原則

描述的原則:一個對象應該對其他的對象有最少的了解。什麼意思呢?就是說一個類應該對自己調用的類知道的最少。還是不懂?其實簡單來說:假設類A實現了某個功能,類B需要調用類A的去執行這個功能,那麼類B應該只暴露一個函數給類A,這個函數表示是實現這個功能的函數,而不是讓類B把實現這個功能的所有細分的函數暴露給A。

開始學設計模式

學習了上面的六大原則之後,提前做了預熱。現在開始,一起學習設計模式吧~

1 單例模式

單例模式可以說是最容易理解的模式了,也是應用最廣的模式之一,先看看定義吧。

定義:確保單例類只有一個實例,並且這個單例類提供一個函數接口讓其他類獲取到這個唯一的實例。

什麼時候需要使用單例模式呢:如果某個類,創建時需要消耗很多資源,即new出這個類的代價很大;或者是這個類占用很多內存,如果創建太多這個類實例會導致內存占用太多。

關於單例模式,雖然很簡單,無需過多的解釋,但是這裡還要提個醒,其實單例模式裡面有很多坑。我們去會會單例模式。最簡單的單例模式如下:

public class Singleton{
    private static Singleton instance;
    //將默認的構造函數私有化,防止其他類手動new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null)
            instance=new Singleton();
         return instatnce;
    }
}

如果是單線程下的系統,這麼寫肯定沒問題。可是如果是多線程環境呢?這代碼明顯不是線程安全的,存在隱患:某個線程拿到的instance可能是null,可能你會想,這有什麼難得,直接在getInstance()函數上加sychronized關鍵字不就好了。可是你想過沒有,每次調用getInstance()時都要執行同步,這帶來沒必要的性能上的消耗。注意,在方法上加sychronized關鍵字時,一個線程訪問這個方法時,其他線程無法同時訪問這個類其他sychronized方法。的我們看看另外一種實現:

public class Singleton{
    private static Singleton instance;
    //將默認的構造函數私有化,防止其他類手動new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}

為什麼需要2次判斷是否為空呢?第一次判斷是為了避免不必要的同步,第二次判斷是確保在此之前沒有其他線程進入到sychronized塊創建了新實例。這段代碼看上去非常完美,但是,,,卻有隱患!問題出現在哪呢?主要是在instance=new Singleton();這段代碼上。這段代碼會編譯成多條指令,大致上做了3件事:

(1)給Singleton實例分配內存
(2)調用Singleton()構造函數,初始化成員字段
(3)將instance對象指向分配的內存(此時instance就不是null啦~)

上面的(2)和(3)的順序無法得到保證的,也就是說,JVM可能先初始化實例字段再把instance指向具體的內存實例,也可能先把instance指向內存實例再對實例進行初始化成員字段。其實一般情況下這兩種方式肯定不會影響到程序的正確執行,但是會影響到上面的單例模式代碼。在第二次判斷instance是否為null時,可能已經在內存中進行實例化了,就差第(3)步了,可是我們的判斷依然為true,導致這個線程在次new一個Singleton對象。
看看另外一種方式:

public class Singleton{
    private volatile static Singleton instance;
    //將默認的構造函數私有化,防止其他類手動new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}

相比前面的代碼,這裡只是對instance變量加了一個volatile關鍵字volatile關鍵字的作用是:線程每次使用到被volatile關鍵字修飾的變量時,都會去堆裡拿最新的數據。換句話說,就是每次使用instance時,保證了instance是最新的。注意:volatile關鍵字並不能解決並發的問題,關於volatile請查看其它相關文章。但是volatile能解決我們這裡的問題。

那麼在安卓中哪些地方用到了單例模式呢?其實,我們在調用系統服務時拿到的Binder對象就是個單例。比如:

//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  

其內部是通過單例的方式返回的,由於單例模式較簡單,這裡不去深究。

2 Builder模式

Builder模式是什麼情況呢?我不想去提它的定義,因為他的定義:將一個復雜對象的構造與它的表示分離,使得同樣的構造過程可以創建不同的表示。好吧,我還是提了。但是看了這個定義並沒有什麼luan用。我們看看具體在什麼情況下用到Builder模式:主要是在創建某個對象時,需要設定很多的參數(通過setter方法),但是這些參數必須按照某個順序設定,或者是設置步驟不同會得到不同結果。舉個非常簡單的例子:

public class MyData{
    private int id;
    private String num; 
    public void Test(){

    } 
    public void setId(int id){
        this.id=id;
    }

    public void setNum(String num){
        this.num=num+"id";
    }


}

當然了,沒有人會這麼去寫代碼。這裡只是舉例子,或者是有時候很多參數有這種類似的依賴關系時,通過構造函數未免太多參數了。回到主題,就是如果是上面的代碼,該怎麼辦呢?你可能會說,那還不簡單,先調用setId函數,再調用setNum函數。是的,沒錯。可是,萬一你一不小心先調用了setNum呢?這是比較簡單的示例,如果是比較復雜的,有很多變量之間依賴的關系,那你每次都得小心翼翼的把各個函數的執行步驟寫正確。
我們看看Builder模式是怎麼去做的:

public class MyBuilder{
    private int id;
    private String num;
    public MyData build(){
        MyData d=new MyData();
        d.setId(id);
        d.setNum(num);
        return t;
    }
    public MyBuilder setId(int id){
        this.id=id;
        return this;
    }
    public MyBuilder setNum(String num){
        this.num=num;
        return this;
    }

}

public class Test{
    public static void  main(String[] args){
        MyData d=new MyBuilder().setId(10).setNum("hc").build();
    }

}

注意到,Builer類的setter函數都會返回自身的引用this,這主要是用於鏈式調用,這也是Builder設計模式中的一個很明顯的特征。

Android中用過的代碼來記憶

記憶我這個例子沒啥意義,我們前面說過,要通過Android中用過的代碼來記憶,這樣才可以不用死記硬背。那麼在Android中哪裡用到了Builder設計模式呢?哈哈~在創建對話框時,是不是跟上面有點類似呢?

AlertDialog.Builer builder=new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
    .setTitle("title")
    .setMessage("message")
    .setPositiveButton("Button1", 
        new DialogInterface.OnclickListener(){
            public void onClick(DialogInterface dialog,int whichButton){
                setTitle("click");
            }   
        })
    .create()
    .show();

這裡的create()函數就想到上面代碼中的build函數。看到這裡是不是在內心中默默的把Builder設計模式拿下了?你並不用死記硬背~

3 原型模式

原型設計模式非常簡單,就是將一個對象進行拷貝。對於類A實例a,要對a進行拷貝,就是創建一個跟a一樣的類型A的實例b,然後將a的屬性全部復制到b。
什麼時候會用到原型模式呢?我個人認為,可以在類的屬性特別多,但是又要經常對類進行拷貝的時候可以用原型模式,這樣代碼比較簡潔,而且比較方便。

另外要注意的是,還有深拷貝和淺拷貝。深拷貝就是把對象裡面的引用的對象也要拷貝一份新的對象,並將這個新的引用對象作為拷貝的對象引用。說的比較繞哈~,舉個例子,假設A類中有B類的引用b,現在需要對A類實例進行拷貝,那麼深拷貝就是,先對b進行一次拷貝得到nb,然後把nb作為A類拷貝的對象的引用,如此一層一層迭代拷貝,把所有的引用都拷貝結束。淺拷貝則不是。

原型模式比較簡單,看看Android怎麼運用原型模式:

Uri uri=Uri.parse("smsto:10086");
Intent shareIntent=new Intent(Intent.ACTION_SENDTO,uri);

//克隆副本
Intent intent=(Intetn)shareIntent.clone();
startActivity(intent);

或許我們平時不會這麼去寫,但是Intent對象確實提供了原型模式的函數clone()

4 工廠方法模式

定義:定義一個創建對象的接口,讓子類決定實例化哪個類
先看一個例子:

public abstract class Product{
    public abstract void method();
} 

public class ConcreteProductA extends Prodect{
    public void method(){
        System.out.println("我是產品A!");
    }
}

public class ConcreteProductB extends Prodect{
    public void method(){
        System.out.println("我是產品B!");
    }
}
public  abstract class Factory{
    public abstract Product createProduct();
}

public class MyFactory extends Factory{

    public Product createProduct(){
        return new ConcreteProductA();
    }
}

看到上面的代碼,是不是覺得工廠模式很簡單呢?還可以通過傳參的方式,讓MyFactory的createProduct方法根據傳入的參數決定是創建ConcreteProductA還是ConcreteProductB。

同樣的,我們不希望記住這個例子,而是通過Android中的代碼來記憶:
其實,在getSystemService方法中就是用到了工廠模式,他就是根據傳入的參數決定創建哪個對象,當然了,由於返回的都是以單例模式存在的對象,因此不用new了,直接把單例返回就好。

public Object getSystemService(String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException("System services not available to Activities before onCreate()");
    }
    //........
    if (WINDOW_SERVICE.equals(name)) {
         return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    //.......
    return super.getSystemService(name);
  }

5 抽象工廠模式

抽象工廠模式:為創建一組相關或者是相互依賴的對象提供一個接口,而不需要制定他們的具體類
看個例子吧,將它跟工廠方法模式做個對比:

public abstract class AbstractProductA{
    public abstract void method();
}
public abstract class AbstractProdectB{
    public abstract void method();
}

public class ConcreteProductA1 extends AbstractProductA{
    public void method(){
        System.out.println("具體產品A1的方法!");
    }
}
public class ConcreteProductA2 extends AbstractProductA{
    public void method(){
        System.out.println("具體產品A2的方法!");
    }
}
public class ConcreteProductB1 extends AbstractProductB{
    public void method(){
        System.out.println("具體產品B1的方法!");
    }
}
public class ConcreteProductB2 extends AbstractProductB{
    public void method(){
        System.out.println("具體產品B2的方法!");
    }
}

public abstract class AbstractFactory{
    public abstract AbstractProductA createProductA();

    public abstract AbstractProductB createProductB();
}

public  class ConcreteFactory1 extends AbstractFactory{
    public  AbstractProductA createProductA(){
        return new ConcreteProductA1();
    }

    public  AbstractProductB createProductB(){
        return new ConcreteProductB1();
    }
}

public  class ConcreteFactory2 extends AbstractFactory{
    public  AbstractProductA createProductA(){
        return new ConcreteProductA2();
    }

    public  AbstractProductB createProductB(){
        return new ConcreteProductB2();
    }
}

其實Android源碼中對抽象工廠出現的比較少,好在抽象工廠方法並不復雜,很容易記住,我們可以從Service中去理解,Service的onBind方法可以看成是一個工廠方法,從framework角度來看Service,可以看成是一個具體的工廠,這相當於一個抽象工廠方法模式的雛形。

 public class BaseService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent){
        return new Binder();
    }

}

6 策略模式

定義:有一系列的算法,將每個算法封裝起來(每個算法可以封裝到不同的類中),各個算法之間可以替換,策略模式讓算法獨立於使用它的客戶而獨立變化。

舉個例子來理解吧,比如,你現在又很多排序算法:冒泡、希爾、歸並、選擇等等。我們要根據實際情況來選擇使用哪種算法,有一種常見的方法是,通過if…else或者case…等條件判斷語句來選擇。但是這個類的維護成本會變高,維護時也容易發生錯誤。

如何使用策略模式呢,我不打算寫示例代碼了,簡單描述一下,就將前面說的算法選擇進行描述。我們可以定義一個算法抽象類AbstractAlgorithm,這個類定義一個抽象方法sort()。每個具體的排序算法去繼承AbstractAlgorithm類並重寫sort()實現排序。在需要使用排序的類Client類中,添加一個setAlgorithm(AbstractAlgorithm al);方法將算法設置進去,每次Client需要排序而是就調用al.sort()。

不知道簡單描述能不能讓你理解~

看看Android中哪裡出現了策略模式,其中在屬性動畫中使用時間插值器的時候就用到了。在使用動畫時,你可以選擇線性插值器LinearInterpolator、加速減速插值器AccelerateDecelerateInterpolator、減速插值器DecelerateInterpolator以及自定義的插值器。這些插值器都是實現根據時間流逝的百分比來計算出當前屬性值改變的百分比。通過根據需要選擇不同的插值器,實現不同的動畫效果。這些比較好理解,就不去粘貼Android源碼了。

7 狀態模式

狀態模式中,行為是由狀態來決定的,不同狀態下有不同行為。狀態模式和策略模式的結構幾乎是一模一樣的,主要是他們表達的目的和本質是不同。狀態模式的行為是平行的、不可替換的,策略模式的行為是彼此獨立可相互替換的。
舉個例子把,比如電視,電視有2個狀態,一個是開機,一個是關機,開機時可以切換頻道,關機時切換頻道不做任何響應。

public interface TvState{
    public void nextChannerl();
    public void prevChannerl();
    public void turnUp();
    public void turnDown();
}

public class PowerOffState implements TvState{
    public void nextChannel(){}
    public void prevChannel(){}
    public void turnUp(){}
    public void turnDown(){}

}


public class PowerOnState implements TvState{
    public void nextChannel(){
        System.out.println("下一頻道");
    }
    public void prevChannel(){
        System.out.println("上一頻道");
    }
    public void turnUp(){
        System.out.println("調高音量");
    }
    public void turnDown(){
        System.out.println("調低音量"); 
    }

}

public interface PowerController{
    public void powerOn();
    public void powerOff();
}

public class TvController implements PowerController{
    TvState mTvState;
    public void setTvState(TvStete tvState){
        mTvState=tvState;
    }
    public void powerOn(){
        setTvState(new PowerOnState());
        System.out.println("開機啦");
    }
    public void powerOff(){
        setTvState(new PowerOffState());
        System.out.println("關機啦");
    }
    public void nextChannel(){
        mTvState.nextChannel();
    }
    public void prevChannel(){
        mTvState.prevChannel();
    }
    public void turnUp(){
        mTvState.turnUp();
    }
    public void turnDown(){
        mTvState.turnDown();
    }

}


public class Client{
    public static void main(String[] args){
        TvController tvController=new TvController();
        tvController.powerOn();
        tvController.nextChannel();
        tvController.turnUp();

        tvController.powerOff();
        //調高音量,此時不會生效
        tvController.turnUp();
    }


}

在Android源碼中,哪裡有用到狀態模式呢?其實很多地方用到了,舉一個地方例子,就是WIFI管理模塊。當WIFI開啟時,自動掃描周圍的接入點,然後以列表的形式展示;當wifi關閉時則清空。這裡wifi管理模塊就是根據不同的狀態執行不同的行為。由於代碼太多,我就不手打敲入了~我們只要知道大致Android裡面在哪裡用到了以及大概是怎麼用的就好。

8 責任鏈模式

定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者直接的耦合關系,將這些對象連成一條鏈,並沿這條鏈傳遞該請求,直到有對象處理它為止。

相信聰明的你很容易理解吧,基本不需要例子來解釋了,直接進如到Android源碼中哪裡用到了責任鏈:在Android處理點擊事件時,父View先接收到點擊事件,如果父View不處理則交給子View,依次往下傳遞~

9 解釋器模式

定義:給定一個語言,定義它的語法,並定義一個解釋器,這個解釋器用於解析語言。

從定義中看起來比較抽象,其實,很簡單,很容易理解!就是相當於自定義一個格式的文件,然後去解析它。不用理解的那麼復雜!

我們看看Android中哪裡用到了,從我們第一次學Android時就知道,四大組件需要在AndroidManifest.xml中定義,其實AndroidManifest.xml就定義了等標簽(語句)的屬性以及其子標簽,規定了具體的使用(語法),通過PackageManagerService(解釋器)進行解析。

10 命令模式

定義:命令模式將每個請求封裝成一個對象,從而讓用戶使用不同的請求把客戶端參數化;將請求進行排隊或者記錄請求日志,以及支持可撤銷操作。

舉個例子來理解:當我們點擊“關機”命令,系統會執行一系列操作,比如暫停事件處理、保存系統配置、結束程序進程、調用內核命令關閉計算機等等,這些命令封裝從不同的對象,然後放入到隊列中一個個去執行,還可以提供撤銷操作。

那麼Android中哪裡用到了命令模式呢?在framework層還真不多。但是在底層卻用到了,一個比較典型的例子就是在Android事件機制中,底層邏輯對事件的轉發處理。每次的按鍵事件會被封裝成NotifyKeyArgs對象。通過InputDispatcher封裝具體的事件操作。

11 觀察者模式

定義:定義了對象之間的一對多的關系,其實就是1對n,當“1”發生變化時,“n”全部得到通知,並更新。

觀察者模式一個比較經典的應用就是:訂閱——發布系統。很容易理解,發布消息時,將消息發送給每個訂閱者。我們常用的微信公眾號就是典型,當我們關注某個公眾號時,每當公眾號推送消息時,我們就會去接收到消息,當然了,每個訂閱(關注)公眾號的的人都能接收到公眾號推送的消息。

那麼Android哪裡用到了觀察者模式呢?我們看看ListView的適配器,有個函數notifyDataSetChanged()函數,這個函數其實就是通知ListView的每個Item,數據源發生了變化,請各位Item重新刷新一下。

12 備忘錄模式

備忘錄模式定義:在不破壞封閉的前提下,捕獲一個對象的內部狀態,並在對象之外保存這個狀態,這樣,以後就可將對象恢復到原先保存的狀態中。

其實就是相當於一個提前備份,一旦出現啥意外,能夠恢復。像我們平時用的word軟件,意外關閉了,它能幫我們恢復。其實就是它自動幫我們備份過。

那麼Android哪裡用到了備忘錄模式呢?ActivityonSaveInstanceStateonRestoreInstanceState就是用到了備忘錄模式,分別用於保存和恢復。

13 迭代器模式

迭代器模式定義:提供一種方法順序訪問一個容器對象中的各個元素,而不需要暴露該對象的內部表示。

相信熟悉Java的你肯定知道,Java中就有迭代器Iterator類,本質上說,它就是用迭代器模式。

按照慣例,看看Android中哪裡用到了迭代器模式,Android源碼中,最典型的就是Cursor用到了迭代器模式,當我們使用SQLiteDatabasequery方法時,返回的就是Cursor對象,通過如下方式去遍歷:

cursor.moveToFirst();
do{
//cursor.getXXX(int);
}while(cursor.moveToNext);

14 模板方法模式

定義:定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重定義該算法的某些特定的步驟。

不用解釋太多,感覺越解釋越糊塗,直接拿Android中的源碼來說事!

我們知道,啟動一個Activity過程非常復雜,如果讓開發者每次自己去調用啟動Activity過程無疑是一場噩夢。好在啟動Activity大部分代碼時不同的,但是有很多地方需要開發者定制。也就是說,整體算法框架是相同的,但是將一些步驟延遲到子類中,比如ActivityonCreateonStart等等。這樣子類不用改變整體啟動Activity過程即可重定義某些具體的操作了~。

15 訪問者模式

定義:封裝一些作用於某種數據結構中各元素的操作,它可以在不改變這個數據結構的前提下定義作用於這些元素的新的操作。

訪問者模式是23種設計模式中最復雜的一個,但他的使用率並不高,大部分情況下,我們不需要使用訪問者模式,少數特定的場景才需要。

Android中運用訪問者模式,其實主要是在編譯期注解中,編譯期注解核心原理依賴APT(Annotation Processing Tools),著名的開源庫比如ButterKnife、Dagger、Retrofit都是基於APT。APT的詳細使用這裡不提,後面我會寫關於APT相關的文章,敬請期待~

16 中介者模式

定義:中介者模式包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯調用,從而使他們可以輕松耦合。當某些對象之間的作用發生改變時,不會立即影響其他的一些對象之間的作用保證這些作用可以彼此獨立的變化,中介者模式將多對多的相互作用轉為一對多的相互作用。

什麼時候用中介者模式呢?其實,中介者對象是將系統從網狀結構轉為以調停者為中心的星型結構。

舉個簡單的例子,一台電腦包括:CPU、內存、顯卡、IO設備。其實,要啟動一台計算機,有了CPU和內存就夠了。當然,如果你需要連接顯示器顯示畫面,那就得加顯卡,如果你需要存儲數據,那就要IO設備,但是這並不是最重要的,它們只是分割開來的普通零件而已,我們需要一樣東西把這些零件整合起來,變成一個完整體,這個東西就是主板。主板就是起到中介者的作用,任何兩個模塊之間的通信都會經過主板協調。

那麼Android中那些地方用到了中介者模式呢?在Binder機制中,就用到了中介者模式。我們知道系統啟動時,各種系統服務會向ServiceManager提交注冊,即ServiceManager持有各種系統服務的引用 ,當我們需要獲取系統的Service時,比如ActivityManagerWindowManager等(它們都是Binder),首先是向ServiceManager查詢指定標示符對應的Binder,再由ServiceManager返回Binder的引用。並且客戶端和服務端之間的通信是通過Binder驅動來實現,這裡的ServiceManagerBinder驅動就是中介者。

17 代理模式

定義:為其他類提供一種代理以控制這個對象的訪問。
其實代理模式我們平時用的也比較多,其實比較好理解,就是當我們需要對一個對象進行訪問時,我們不直接對這個對象進行訪問,而是訪問這個類的代理類,代理類能幫我們執行我們想要的操作。代理模式比較容易理解,既然你來看這篇文章相信你對代理模式不陌生。

我們直接看看代理模式在Android中的應用,如果你查看AIDL生成的代碼就知道,它會根據當前的線程判斷是否要跨進程訪問,如果不需要跨進程就直接返回實例,如果需要跨進程則返回一個代理,這個代理干什麼事情呢?在跨進程通信時,需要把參數寫入到Parcelable對象,然後再執行transact函數,我們要寫的代碼挺多的。AIDL通過生成一個代理類,代理類中自動幫我們寫好這些操作。

18 組合模式

定義:將對象組成成樹形結構,以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。

上面的定義不太好理解,我們直接從Android中用到的組合模式說起。我們知道,Android中View的結構是樹形結構,每個ViewGroup包含一系列的View,而ViewGroup本身又是View。這是Android中非常典型的組合模式。

19 適配器模式

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

其實適配器模式很容易理解,我們在Android開發時也經常用到。比較典型的有ListView和RecyclerView。為什麼ListView需要使用適配器呢?主要是,ListView只關心它的每個ItemView,而不關心這個ItemView具體顯示的是什麼。而我們的數據源存放的是要顯示的內容,它保存了每一個ItemView要顯示的內容。ListView和數據源之間沒有任何關系,這時候,需要通過適配器,適配器提供getView方法給ListView使用,每次ListView只需提供位置信息給getView函數,然後getView函數根據位置信息向數據源獲取對應的數據,根據數據返回不同的View。

20 裝飾模式

定義:動態的給一個對象添加額外的智者,就增加功能來說,裝飾模式比子類繼承的方式更靈活。
通過簡單代碼來理解裝飾模式:

public abstract class Component{
    public abstract void operate();
}

public class ConcreteComponent extends Component{
    public void operate(){
        //具體的實現
    }

}

public class Decorator{
    private Component component;
    public Decorator(Component component){
        this.component=component;
    }
    public void operate(){
        operateA();
        component.operate();
        operateB();
    }
    public void operateA(){
        //具體操作
    }
    public void operateB(){
        //具體操作
    }
}

那麼在Android哪裡出現了裝飾模式呢?我們平時經常用到Context類,但是其實Context類只是個抽象類,具體實現是ContextImpl,那麼誰是ContextImpl的裝飾類呢?我們知道Activity是個Context,但是Activity 並不是繼承於Context,而是繼承於ContextThremeWrapper.而ContextThremeWrapper繼承於ContextWrapper,ContextWrapper繼承Context.說了這麼多,跟裝飾模式有啥關系?主要是引入ContextWrapper這個類。ContextWrapper內部有個Context引用mContext,並且ContextWrapper中對Context的每個方法都有實現,在實現中調用的就是mContext相同的方法。

21 享元模式

定義:使用享元對象有效地支持大量的細粒度對象。

享元模式我們平時接觸真的很多,比如Java中的常量池,線程池等。主要是為了重用對象。

在Android哪裡用到了享元模式呢?線程通信中的Message,每次我們獲取Message時調用Message.obtain()其實就是從消息池中取出可重復使用的消息,避免產生大量的Message對象。

22 外觀模式

定義:要求一個子系統的外部與其內部的通信必須通過一個統一的對象進行。

怎麼理解呢,舉個例子,我們在啟動計算機時,只需按一下開關鍵,無需關系裡面的磁盤、內存、cpu、電源等等這些如何工作,我們只關心他們幫我啟動好了就行。實際上,由於裡面的線路太復雜,我們也沒辦法去具體了解內部電路如何工作。主機提供唯一一個接口“開關鍵”給用戶就好。

那麼Android哪裡使用到了外觀模式呢?依然回到Context,Android內部有很多復雜的功能比如startActivty、sendBroadcast、bindService等等,這些功能內部的實現非常復雜,如果你看了源碼你就能感受得到,但是我們無需關心它內部實現了什麼,我們只關心它幫我們啟動Activity,幫我們發送了一條廣播,綁定了Activity等等就夠了。

23 橋接模式

定義:將抽象部分與實現部分分離,使他們獨立地進行變化。
其實就是,一個類存在兩個維度的變化,且這兩個維度都需要進行擴展。

在Android中橋接模式用的很多,舉個例子,對於一個View來說,它有兩個維度的變化,一個是它的描述比如Button、TextView等等他們是View的描述維度上的變化,另一個維度就是將View真正繪制到屏幕上,這跟Display、HardwareLayer和Canvas有關。這兩個維度可以看成是橋接模式的應用。

24 MVC、MVP、MVVP模式

MVC
全稱為Model-View-Controller,也就是模型-視圖-控制器。MVC結構如下圖所示:
MVC

在Android中對MVC的應用很經典,我們的布局文件如main.xml就是對應View層,本地的數據庫數據或者是網絡下載的數據就是對應Model層,而Activity對應Controller層。

MVP
MVP全稱為Model View Presenter,目前MVP在Android應用開發中越來越重要了,它的結構圖如下:
MVP
它降低了View與Model之間的耦合。徹底將View與Model分離。MVP不是一種標准化的模式,它由很多種實現。

MVVM

全稱是Mode View ViewModel,它的結構如下所示:

MVVM
我們在使用ListView時,會自定義一個ViewHolder,在RecyclerView中是必須使用ViewHolder,這主要是提高性能,因為不需要每次去調用findViewById來獲取View。其實ViewHolder就是個ViewModel。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved