從Android代碼中來(lái)記憶23種設(shè)計(jì)模式

我的CSDN博客同步發(fā)布:從Android代碼中來(lái)記憶23種設(shè)計(jì)模式

相信大家都曾經(jīng)下定決心把23種設(shè)計(jì)模式牢記于心,每次看完之后過(guò)一段時(shí)間又忘記了~,又得回去看,腦子里唯一依稀記得的是少數(shù)設(shè)計(jì)模式的大致的定義。其實(shí),網(wǎng)上很多文章講得都非常好,我也曾經(jīng)去看過(guò)各種文章。也曾一直苦惱這些難以永久記下的設(shè)計(jì)模式,直到我接觸到了《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》——何紅輝與關(guān)愛(ài)明著,發(fā)現(xiàn)原來(lái)其實(shí)我們?cè)贏ndroid中都接觸過(guò)這些設(shè)計(jì)模式,只是我們不知道而已。既然我們都接觸過(guò),我們只需一一對(duì)號(hào)入座,對(duì)設(shè)計(jì)模式的記憶就不用死記硬背了!這里自愿無(wú)償做個(gè)廣告,《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》這本書(shū)真心不錯(cuò),每個(gè)Android程序員最好都去翻翻...正如你所想的那樣,本文是從這本書(shū)中的總結(jié),相信你也會(huì)跟我一樣,從中獲益。

面向?qū)ο蟮牧笤瓌t

首先,我們?yōu)槭裁匆獙W(xué)習(xí)設(shè)計(jì)模式。主要是這些模式是前人總結(jié)的經(jīng)驗(yàn),使用這些模式能讓我們的程序更健壯、更穩(wěn)定、容易擴(kuò)展等等優(yōu)點(diǎn)。在編寫(xiě)面向?qū)ο蟪绦驎r(shí),我們需要遵循以下6個(gè)原則,能讓我們的程序維護(hù)起來(lái)更輕松~(當(dāng)然還有其它好處)。

1 單一職責(zé)原則
單一原則很簡(jiǎn)單,就是將一組相關(guān)性很高的函數(shù)、數(shù)據(jù)封裝到一個(gè)類(lèi)中。換句話說(shuō),一個(gè)類(lèi)應(yīng)該有職責(zé)單一。

2 開(kāi)閉原則

開(kāi)閉原則理解起來(lái)也不復(fù)雜,就是一個(gè)類(lèi)應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的。我們知道,在開(kāi)放的app或者是系統(tǒng)中,經(jīng)常需要升級(jí)、維護(hù)等,這就要對(duì)原來(lái)的代碼進(jìn)行修改,可是修改時(shí)容易破壞原有的系統(tǒng),甚至帶來(lái)一些新的難以發(fā)現(xiàn)的BUG。因此,我們?cè)谝婚_(kāi)始編寫(xiě)代碼時(shí),就應(yīng)該注意盡量通過(guò)擴(kuò)展的方式實(shí)現(xiàn)新的功能,而不是通過(guò)修改已有的代碼實(shí)現(xiàn)。

3 里氏替換原則

里氏替換原則的定義為:所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)對(duì)象。定義看起來(lái)很抽象,其實(shí),很容易理解,本質(zhì)上就是說(shuō),要好好利用繼承和多態(tài)。簡(jiǎn)單地說(shuō),就是以父類(lèi)的形式聲明的變量(或形參),賦值為任何繼承于這個(gè)父類(lèi)的子類(lèi)后不影響程序的執(zhí)行。看一組代碼你就明白這個(gè)原則了:

//窗口類(lèi)
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){
        //測(cè)量視圖大小
    }
}
public class Button extends View{
    public void draw(){
        //繪制按鈕
    }
}

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

Window 類(lèi)中show函數(shù)需要傳入View,并且調(diào)用View對(duì)象的draw函數(shù)。而每個(gè)繼承于View的子對(duì)象都有draw的實(shí)現(xiàn),不存在繼承于View但是卻沒(méi)實(shí)現(xiàn)draw函數(shù)的子類(lèi)(abstract方法必須實(shí)現(xiàn))。我們?cè)诔橄箢?lèi)設(shè)計(jì)之時(shí)就運(yùn)用到了里氏替換原則。

4 依賴(lài)倒置原則

依賴(lài)倒置主要是實(shí)現(xiàn)解耦,使得高層次的模塊不依賴(lài)于低層次模塊的具體實(shí)現(xiàn)細(xì)節(jié)。怎么去理解它呢,我們需要知道幾個(gè)關(guān)鍵點(diǎn):

(1)高層模塊不應(yīng)該依賴(lài)底層模塊(具體實(shí)現(xiàn)),二者都應(yīng)該依賴(lài)其抽象(抽象類(lèi)或接口)
(2)抽象不應(yīng)該依賴(lài)細(xì)節(jié)(廢話,抽象類(lèi)跟接口肯定不依賴(lài)具體的實(shí)現(xiàn)了)
(3)細(xì)節(jié)應(yīng)該依賴(lài)于抽象(同樣廢話,具體實(shí)現(xiàn)類(lèi)肯定要依賴(lài)其繼承的抽象類(lèi)或接口)

其實(shí),在我們用的Java語(yǔ)言中,抽象就是指接口或者抽象類(lèi),二者都是不能直接被實(shí)例化;細(xì)節(jié)就是實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)接口或者繼承抽象類(lèi)而產(chǎn)生的類(lèi),就是細(xì)節(jié)。使用Java語(yǔ)言描述就簡(jiǎn)單了:就是各個(gè)模塊之間相互傳遞的參數(shù)聲明為抽象類(lèi)型,而不是聲明為具體的實(shí)現(xiàn)類(lèi);

5 接口隔離原則

接口隔離原則定義:類(lèi)之間的依賴(lài)關(guān)系應(yīng)該建立在最小的接口上。其原則是將非常龐大的、臃腫的接口拆分成更小的更具體的接口。

6 迪米特原則

描述的原則:一個(gè)對(duì)象應(yīng)該對(duì)其他的對(duì)象有最少的了解。什么意思呢?就是說(shuō)一個(gè)類(lèi)應(yīng)該對(duì)自己調(diào)用的類(lèi)知道的最少。還是不懂?其實(shí)簡(jiǎn)單來(lái)說(shuō):假設(shè)類(lèi)A實(shí)現(xiàn)了某個(gè)功能,類(lèi)B需要調(diào)用類(lèi)A的去執(zhí)行這個(gè)功能,那么類(lèi)A應(yīng)該只暴露一個(gè)函數(shù)給類(lèi)B,這個(gè)函數(shù)表示是實(shí)現(xiàn)這個(gè)功能的函數(shù),而不是讓類(lèi)A把實(shí)現(xiàn)這個(gè)功能的所有細(xì)分的函數(shù)暴露給B。

開(kāi)始學(xué)設(shè)計(jì)模式

學(xué)習(xí)了上面的六大原則之后,提前做了預(yù)熱。現(xiàn)在開(kāi)始,一起學(xué)習(xí)設(shè)計(jì)模式吧~

1 單例模式

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

定義:確保單例類(lèi)只有一個(gè)實(shí)例,并且這個(gè)單例類(lèi)提供一個(gè)函數(shù)接口讓其他類(lèi)獲取到這個(gè)唯一的實(shí)例。

什么時(shí)候需要使用單例模式呢:如果某個(gè)類(lèi),創(chuàng)建時(shí)需要消耗很多資源,即new出這個(gè)類(lèi)的代價(jià)很大;或者是這個(gè)類(lèi)占用很多內(nèi)存,如果創(chuàng)建太多這個(gè)類(lèi)實(shí)例會(huì)導(dǎo)致內(nèi)存占用太多。

關(guān)于單例模式,雖然很簡(jiǎn)單,無(wú)需過(guò)多的解釋?zhuān)沁@里還要提個(gè)醒,其實(shí)單例模式里面有很多坑。我們?nèi)?huì)會(huì)單例模式。最簡(jiǎn)單的單例模式如下:

public class Singleton{
    private static Singleton instance;
    //將默認(rèn)的構(gòu)造函數(shù)私有化,防止其他類(lèi)手動(dòng)new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null)
            instance=new Singleton();
         return instatnce;
    }
}

如果是單線程下的系統(tǒng),這么寫(xiě)肯定沒(méi)問(wèn)題。可是如果是多線程環(huán)境呢?這代碼明顯不是線程安全的,存在隱患:某個(gè)線程拿到的instance可能是null,可能你會(huì)想,這有什么難得,直接在getInstance()函數(shù)上加sychronized關(guān)鍵字不就好了。可是你想過(guò)沒(méi)有,每次調(diào)用getInstance()時(shí)都要執(zhí)行同步,這帶來(lái)沒(méi)必要的性能上的消耗。注意,在方法上加sychronized關(guān)鍵字時(shí),一個(gè)線程訪問(wèn)這個(gè)方法時(shí),其他線程無(wú)法同時(shí)訪問(wèn)這個(gè)類(lèi)其他sychronized方法。的我們看看另外一種實(shí)現(xiàn):

public class Singleton{
    private static Singleton instance;
    //將默認(rèn)的構(gòu)造函數(shù)私有化,防止其他類(lèi)手動(dòng)new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}

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

(1)給Singleton實(shí)例分配內(nèi)存
(2)調(diào)用Singleton()構(gòu)造函數(shù),初始化成員字段
(3)將instance對(duì)象指向分配的內(nèi)存(此時(shí)instance就不是null啦~)

上面的(2)和(3)的順序無(wú)法得到保證的,也就是說(shuō),JVM可能先初始化實(shí)例字段再把instance指向具體的內(nèi)存實(shí)例,也可能先把instance指向內(nèi)存實(shí)例再對(duì)實(shí)例進(jìn)行初始化成員字段。考慮這種情況:一開(kāi)始,第一個(gè)線程執(zhí)行instance=new Singleton();這句時(shí),JVM先指向一個(gè)堆地址,而此時(shí),又來(lái)了一個(gè)線程2,它發(fā)現(xiàn)instance不是null,就直接拿去用了,但是堆里面對(duì)單例對(duì)象的初始化并沒(méi)有完成,最終出現(xiàn)錯(cuò)誤~ 。

看看另外一種方式:

public class Singleton{
    private volatile static Singleton instance;
    //將默認(rèn)的構(gòu)造函數(shù)私有化,防止其他類(lèi)手動(dòng)new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}

相比前面的代碼,這里只是對(duì)instance變量加了一個(gè)volatile關(guān)鍵字volatile關(guān)鍵字的作用是:線程每次使用到被volatile關(guān)鍵字修飾的變量時(shí),都會(huì)去堆里拿最新的數(shù)據(jù)。換句話說(shuō),就是每次使用instance時(shí),保證了instance是最新的。注意:volatile關(guān)鍵字并不能解決并發(fā)的問(wèn)題,關(guān)于volatile請(qǐng)查看其它相關(guān)文章。但是volatile能解決我們這里的問(wèn)題。

那么在安卓中哪些地方用到了單例模式呢?其實(shí),我們?cè)谡{(diào)用系統(tǒng)服務(wù)時(shí)拿到的Binder對(duì)象就是個(gè)單例。比如:

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

其內(nèi)部是通過(guò)單例的方式返回的,由于單例模式較簡(jiǎn)單,這里不去深究。

2 Builder模式

Builder模式是什么情況呢?我不想去提它的定義,因?yàn)樗亩x:將一個(gè)復(fù)雜對(duì)象的構(gòu)造與它的表示分離,使得同樣的構(gòu)造過(guò)程可以創(chuàng)建不同的表示。好吧,我還是提了。但是看了這個(gè)定義并沒(méi)有什么luan用。我們看看具體在什么情況下用到Builder模式:主要是在創(chuàng)建某個(gè)對(duì)象時(shí),需要設(shè)定很多的參數(shù)(通過(guò)setter方法),但是這些參數(shù)必須按照某個(gè)順序設(shè)定,或者是設(shè)置步驟不同會(huì)得到不同結(jié)果。舉個(gè)非常簡(jiǎn)單的例子:

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";
    }
 
     
}

當(dāng)然了,沒(méi)有人會(huì)這么去寫(xiě)代碼。這里只是舉例子,或者是有時(shí)候很多參數(shù)有這種類(lèi)似的依賴(lài)關(guān)系時(shí),通過(guò)構(gòu)造函數(shù)未免太多參數(shù)了。回到主題,就是如果是上面的代碼,該怎么辦呢?你可能會(huì)說(shuō),那還不簡(jiǎn)單,先調(diào)用setId函數(shù),再調(diào)用setNum函數(shù)。是的,沒(méi)錯(cuò)。可是,萬(wàn)一你一不小心先調(diào)用了setNum呢?這是比較簡(jiǎn)單的示例,如果是比較復(fù)雜的,有很多變量之間依賴(lài)的關(guān)系,那你每次都得小心翼翼的把各個(gè)函數(shù)的執(zhí)行步驟寫(xiě)正確。
我們看看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類(lèi)的setter函數(shù)都會(huì)返回自身的引用this,這主要是用于鏈?zhǔn)秸{(diào)用,這也是Builder設(shè)計(jì)模式中的一個(gè)很明顯的特征。

Android中用過(guò)的代碼來(lái)記憶

記憶我這個(gè)例子沒(méi)啥意義,我們前面說(shuō)過(guò),要通過(guò)Android中用過(guò)的代碼來(lái)記憶,這樣才可以不用死記硬背。那么在Android中哪里用到了Builder設(shè)計(jì)模式呢?哈哈~在創(chuàng)建對(duì)話框時(shí),是不是跟上面有點(diǎn)類(lèi)似呢?

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()函數(shù)就想到上面代碼中的build函數(shù)。看到這里是不是在內(nèi)心中默默的把Builder設(shè)計(jì)模式拿下了?你并不用死記硬背~

3 原型模式

原型設(shè)計(jì)模式非常簡(jiǎn)單,就是將一個(gè)對(duì)象進(jìn)行拷貝。對(duì)于類(lèi)A實(shí)例a,要對(duì)a進(jìn)行拷貝,就是創(chuàng)建一個(gè)跟a一樣的類(lèi)型A的實(shí)例b,然后將a的屬性全部復(fù)制到b。
什么時(shí)候會(huì)用到原型模式呢?我個(gè)人認(rèn)為,可以在類(lèi)的屬性特別多,但是又要經(jīng)常對(duì)類(lèi)進(jìn)行拷貝的時(shí)候可以用原型模式,這樣代碼比較簡(jiǎn)潔,而且比較方便。

另外要注意的是,還有深拷貝和淺拷貝。深拷貝就是把對(duì)象里面的引用的對(duì)象也要拷貝一份新的對(duì)象,并將這個(gè)新的引用對(duì)象作為拷貝的對(duì)象引用。說(shuō)的比較繞哈~,舉個(gè)例子,假設(shè)A類(lèi)中有B類(lèi)的引用b,現(xiàn)在需要對(duì)A類(lèi)實(shí)例進(jìn)行拷貝,那么深拷貝就是,先對(duì)b進(jìn)行一次拷貝得到nb,然后把nb作為A類(lèi)拷貝的對(duì)象的引用,如此一層一層迭代拷貝,把所有的引用都拷貝結(jié)束。淺拷貝則不是。

原型模式比較簡(jiǎn)單,看看Android怎么運(yùn)用原型模式:

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

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

或許我們平時(shí)不會(huì)這么去寫(xiě),但是Intent對(duì)象確實(shí)提供了原型模式的函數(shù)clone()

4 工廠方法模式

定義:定義一個(gè)創(chuàng)建對(duì)象的接口,讓子類(lèi)決定實(shí)例化哪個(gè)類(lèi)
先看一個(gè)例子:

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

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

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

public class MyFactory extends Factory{

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

看到上面的代碼,是不是覺(jué)得工廠模式很簡(jiǎn)單呢?還可以通過(guò)傳參的方式,讓MyFactory的createProduct方法根據(jù)傳入的參數(shù)決定是創(chuàng)建ConcreteProductA還是ConcreteProductB。

同樣的,我們不希望記住這個(gè)例子,而是通過(guò)Android中的代碼來(lái)記憶:
其實(shí),在getSystemService方法中就是用到了工廠模式,他就是根據(jù)傳入的參數(shù)決定創(chuàng)建哪個(gè)對(duì)象,當(dāng)然了,由于返回的都是以單例模式存在的對(duì)象,因此不用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 抽象工廠模式

抽象工廠模式:為創(chuàng)建一組相關(guān)或者是相互依賴(lài)的對(duì)象提供一個(gè)接口,而不需要制定他們的具體類(lèi)
看個(gè)例子吧,將它跟工廠方法模式做個(gè)對(duì)比:

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("具體產(chǎn)品A1的方法!");
    }
}
public class ConcreteProductA2 extends AbstractProductA{
    public void method(){
        System.out.println("具體產(chǎn)品A2的方法!");
    }
}
public class ConcreteProductB1 extends AbstractProductB{
    public void method(){
        System.out.println("具體產(chǎn)品B1的方法!");
    }
}
public class ConcreteProductB2 extends AbstractProductB{
    public void method(){
        System.out.println("具體產(chǎn)品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();
    }
}

其實(shí)Android源碼中對(duì)抽象工廠出現(xiàn)的比較少,好在抽象工廠方法并不復(fù)雜,很容易記住,我們可以從Service中去理解,Service的onBind方法可以看成是一個(gè)工廠方法,從framework角度來(lái)看Service,可以看成是一個(gè)具體的工廠,這相當(dāng)于一個(gè)抽象工廠方法模式的雛形。

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

6 策略模式

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

舉個(gè)例子來(lái)理解吧,比如,你現(xiàn)在又很多排序算法:冒泡、希爾、歸并、選擇等等。我們要根據(jù)實(shí)際情況來(lái)選擇使用哪種算法,有一種常見(jiàn)的方法是,通過(guò)if...else或者case...等條件判斷語(yǔ)句來(lái)選擇。但是這個(gè)類(lèi)的維護(hù)成本會(huì)變高,維護(hù)時(shí)也容易發(fā)生錯(cuò)誤。

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

不知道簡(jiǎn)單描述能不能讓你理解~

看看Android中哪里出現(xiàn)了策略模式,其中在屬性動(dòng)畫(huà)中使用時(shí)間插值器的時(shí)候就用到了。在使用動(dòng)畫(huà)時(shí),你可以選擇線性插值器LinearInterpolator、加速減速插值器AccelerateDecelerateInterpolator、減速插值器DecelerateInterpolator以及自定義的插值器。這些插值器都是實(shí)現(xiàn)根據(jù)時(shí)間流逝的百分比來(lái)計(jì)算出當(dāng)前屬性值改變的百分比。通過(guò)根據(jù)需要選擇不同的插值器,實(shí)現(xiàn)不同的動(dòng)畫(huà)效果。這些比較好理解,就不去粘貼Android源碼了。

7 狀態(tài)模式

狀態(tài)模式中,行為是由狀態(tài)來(lái)決定的,不同狀態(tài)下有不同行為。狀態(tài)模式和策略模式的結(jié)構(gòu)幾乎是一模一樣的,主要是他們表達(dá)的目的和本質(zhì)是不同。狀態(tài)模式的行為是平行的、不可替換的,策略模式的行為是彼此獨(dú)立可相互替換的。
舉個(gè)例子把,比如電視,電視有2個(gè)狀態(tài),一個(gè)是開(kāi)機(jī),一個(gè)是關(guān)機(jī),開(kāi)機(jī)時(shí)可以切換頻道,關(guān)機(jī)時(shí)切換頻道不做任何響應(yīng)。

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("調(diào)高音量");
    }
    public void turnDown(){
        System.out.println("調(diào)低音量"); 
    }
    
}

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("開(kāi)機(jī)啦");
    }
    public void powerOff(){
        setTvState(new PowerOffState());
        System.out.println("關(guān)機(jī)啦");
    }
    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();
        //調(diào)高音量,此時(shí)不會(huì)生效
        tvController.turnUp();
    }


}

在Android源碼中,哪里有用到狀態(tài)模式呢?其實(shí)很多地方用到了,舉一個(gè)地方例子,就是WIFI管理模塊。當(dāng)WIFI開(kāi)啟時(shí),自動(dòng)掃描周?chē)慕尤朦c(diǎn),然后以列表的形式展示;當(dāng)wifi關(guān)閉時(shí)則清空。這里wifi管理模塊就是根據(jù)不同的狀態(tài)執(zhí)行不同的行為。由于代碼太多,我就不手打敲入了~我們只要知道大致Android里面在哪里用到了以及大概是怎么用的就好。

8 責(zé)任鏈模式

定義:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者直接的耦合關(guān)系,將這些對(duì)象連成一條鏈,并沿這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止。

相信聰明的你很容易理解吧,基本不需要例子來(lái)解釋了,直接進(jìn)如到Android源碼中哪里用到了責(zé)任鏈:在Android處理點(diǎn)擊事件時(shí),父View先接收到點(diǎn)擊事件,如果父View不處理則交給子View,依次往下傳遞~

9 解釋器模式

定義:給定一個(gè)語(yǔ)言,定義它的語(yǔ)法,并定義一個(gè)解釋器,這個(gè)解釋器用于解析語(yǔ)言。

從定義中看起來(lái)比較抽象,其實(shí),很簡(jiǎn)單,很容易理解!就是相當(dāng)于自定義一個(gè)格式的文件,然后去解析它。不用理解的那么復(fù)雜!

我們看看Android中哪里用到了,從我們第一次學(xué)Android時(shí)就知道,四大組件需要在AndroidManifest.xml中定義,其實(shí)AndroidManifest.xml就定義了<Activity><Service>等標(biāo)簽(語(yǔ)句)的屬性以及其子標(biāo)簽,規(guī)定了具體的使用(語(yǔ)法),通過(guò)PackageManagerService(解釋器)進(jìn)行解析。

10 命令模式

定義:命令模式將每個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而讓用戶(hù)使用不同的請(qǐng)求把客戶(hù)端參數(shù)化;將請(qǐng)求進(jìn)行排隊(duì)或者記錄請(qǐng)求日志,以及支持可撤銷(xiāo)操作。

舉個(gè)例子來(lái)理解:當(dāng)我們點(diǎn)擊“關(guān)機(jī)”命令,系統(tǒng)會(huì)執(zhí)行一系列操作,比如暫停事件處理、保存系統(tǒng)配置、結(jié)束程序進(jìn)程、調(diào)用內(nèi)核命令關(guān)閉計(jì)算機(jī)等等,這些命令封裝從不同的對(duì)象,然后放入到隊(duì)列中一個(gè)個(gè)去執(zhí)行,還可以提供撤銷(xiāo)操作。

那么Android中哪里用到了命令模式呢?在framework層還真不多。但是在底層卻用到了,一個(gè)比較典型的例子就是在Android事件機(jī)制中,底層邏輯對(duì)事件的轉(zhuǎn)發(fā)處理。每次的按鍵事件會(huì)被封裝成NotifyKeyArgs對(duì)象。通過(guò)InputDispatcher封裝具體的事件操作。

11 觀察者模式

定義:定義了對(duì)象之間的一對(duì)多的關(guān)系,其實(shí)就是1對(duì)n,當(dāng)“1”發(fā)生變化時(shí),“n”全部得到通知,并更新。

觀察者模式一個(gè)比較經(jīng)典的應(yīng)用就是:訂閱——發(fā)布系統(tǒng)。很容易理解,發(fā)布消息時(shí),將消息發(fā)送給每個(gè)訂閱者。我們常用的微信公眾號(hào)就是典型,當(dāng)我們關(guān)注某個(gè)公眾號(hào)時(shí),每當(dāng)公眾號(hào)推送消息時(shí),我們就會(huì)去接收到消息,當(dāng)然了,每個(gè)訂閱(關(guān)注)公眾號(hào)的的人都能接收到公眾號(hào)推送的消息。

那么Android哪里用到了觀察者模式呢?我們看看ListView的適配器,有個(gè)函數(shù)notifyDataSetChanged()函數(shù),這個(gè)函數(shù)其實(shí)就是通知ListView的每個(gè)Item,數(shù)據(jù)源發(fā)生了變化,請(qǐng)各位Item重新刷新一下。

12 備忘錄模式

備忘錄模式定義:在不破壞封閉的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在對(duì)象之外保存這個(gè)狀態(tài),這樣,以后就可將對(duì)象恢復(fù)到原先保存的狀態(tài)中。

其實(shí)就是相當(dāng)于一個(gè)提前備份,一旦出現(xiàn)啥意外,能夠恢復(fù)。像我們平時(shí)用的word軟件,意外關(guān)閉了,它能幫我們恢復(fù)。其實(shí)就是它自動(dòng)幫我們備份過(guò)。

那么Android哪里用到了備忘錄模式呢?ActivityonSaveInstanceStateonRestoreInstanceState就是用到了備忘錄模式,分別用于保存和恢復(fù)。

13 迭代器模式

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

相信熟悉Java的你肯定知道,Java中就有迭代器Iterator類(lèi),本質(zhì)上說(shuō),它就是用迭代器模式。

按照慣例,看看Android中哪里用到了迭代器模式,Android源碼中,最典型的就是Cursor用到了迭代器模式,當(dāng)我們使用SQLiteDatabasequery方法時(shí),返回的就是Cursor對(duì)象,通過(guò)如下方式去遍歷:

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

14 模板方法模式

定義:定義一個(gè)操作中的算法框架,而將一些步驟延遲到子類(lèi)中,使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定的步驟。

不用解釋太多,感覺(jué)越解釋越糊涂,直接拿Android中的源碼來(lái)說(shuō)事!

我們知道,啟動(dòng)一個(gè)Activity過(guò)程非常復(fù)雜,如果讓開(kāi)發(fā)者每次自己去調(diào)用啟動(dòng)Activity過(guò)程無(wú)疑是一場(chǎng)噩夢(mèng)。好在啟動(dòng)Activity大部分代碼時(shí)不同的,但是有很多地方需要開(kāi)發(fā)者定制。也就是說(shuō),整體算法框架是相同的,但是將一些步驟延遲到子類(lèi)中,比如ActivityonCreateonStart等等。這樣子類(lèi)不用改變整體啟動(dòng)Activity過(guò)程即可重定義某些具體的操作了~。

15 訪問(wèn)者模式

定義:封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中各元素的操作,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。

訪問(wèn)者模式是23種設(shè)計(jì)模式中最復(fù)雜的一個(gè),但他的使用率并不高,大部分情況下,我們不需要使用訪問(wèn)者模式,少數(shù)特定的場(chǎng)景才需要。

Android中運(yùn)用訪問(wèn)者模式,其實(shí)主要是在編譯期注解中,編譯期注解核心原理依賴(lài)APT(Annotation Processing Tools),著名的開(kāi)源庫(kù)比如ButterKnife、Dagger、Retrofit都是基于APT。APT的詳細(xì)使用這里不提,后面我會(huì)寫(xiě)關(guān)于APT相關(guān)的文章,敬請(qǐng)期待~

16 中介者模式

定義:中介者模式包裝了一系列對(duì)象相互作用的方式,使得這些對(duì)象不必相互明顯調(diào)用,從而使他們可以輕松耦合。當(dāng)某些對(duì)象之間的作用發(fā)生改變時(shí),不會(huì)立即影響其他的一些對(duì)象之間的作用保證這些作用可以彼此獨(dú)立的變化,中介者模式將多對(duì)多的相互作用轉(zhuǎn)為一對(duì)多的相互作用。

什么時(shí)候用中介者模式呢?其實(shí),中介者對(duì)象是將系統(tǒng)從網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)為以調(diào)停者為中心的星型結(jié)構(gòu)。

舉個(gè)簡(jiǎn)單的例子,一臺(tái)電腦包括:CPU、內(nèi)存、顯卡、IO設(shè)備。其實(shí),要啟動(dòng)一臺(tái)計(jì)算機(jī),有了CPU和內(nèi)存就夠了。當(dāng)然,如果你需要連接顯示器顯示畫(huà)面,那就得加顯卡,如果你需要存儲(chǔ)數(shù)據(jù),那就要IO設(shè)備,但是這并不是最重要的,它們只是分割開(kāi)來(lái)的普通零件而已,我們需要一樣?xùn)|西把這些零件整合起來(lái),變成一個(gè)完整體,這個(gè)東西就是主板。主板就是起到中介者的作用,任何兩個(gè)模塊之間的通信都會(huì)經(jīng)過(guò)主板協(xié)調(diào)。

那么Android中那些地方用到了中介者模式呢?在Binder機(jī)制中,就用到了中介者模式,對(duì)Binder不是很熟悉的童鞋請(qǐng)參考我的《 簡(jiǎn)單明了,徹底地理解Binder》。我們知道系統(tǒng)啟動(dòng)時(shí),各種系統(tǒng)服務(wù)會(huì)向ServiceManager提交注冊(cè),即ServiceManager持有各種系統(tǒng)服務(wù)的引用 ,當(dāng)我們需要獲取系統(tǒng)的Service時(shí),比如ActivityManagerWindowManager等(它們都是Binder),首先是向ServiceManager查詢(xún)指定標(biāo)示符對(duì)應(yīng)的Binder,再由ServiceManager返回Binder的引用。并且客戶(hù)端和服務(wù)端之間的通信是通過(guò)Binder驅(qū)動(dòng)來(lái)實(shí)現(xiàn),這里的ServiceManagerBinder驅(qū)動(dòng)就是中介者。

17 代理模式

定義:為其他類(lèi)提供一種代理以控制這個(gè)對(duì)象的訪問(wèn)。
其實(shí)代理模式我們平時(shí)用的也比較多,其實(shí)比較好理解,就是當(dāng)我們需要對(duì)一個(gè)對(duì)象進(jìn)行訪問(wèn)時(shí),我們不直接對(duì)這個(gè)對(duì)象進(jìn)行訪問(wèn),而是訪問(wèn)這個(gè)類(lèi)的代理類(lèi),代理類(lèi)能幫我們執(zhí)行我們想要的操作。代理模式比較容易理解,既然你來(lái)看這篇文章相信你對(duì)代理模式不陌生。

我們直接看看代理模式在Android中的應(yīng)用,如果你查看AIDL生成的代碼就知道,它會(huì)根據(jù)當(dāng)前的線程判斷是否要跨進(jìn)程訪問(wèn),如果不需要跨進(jìn)程就直接返回實(shí)例,如果需要跨進(jìn)程則返回一個(gè)代理,這個(gè)代理干什么事情呢?我們?cè)?a href="http://www.lxweimin.com/p/04a034cbbc27" target="_blank">《 簡(jiǎn)單明了,徹底地理解Binder》提到,在跨進(jìn)程通信時(shí),需要把參數(shù)寫(xiě)入到Parcelable對(duì)象,然后再執(zhí)行transact函數(shù),我們要寫(xiě)的代碼挺多的。AIDL通過(guò)生成一個(gè)代理類(lèi),代理類(lèi)中自動(dòng)幫我們寫(xiě)好這些操作。

18 組合模式

定義:將對(duì)象組成成樹(shù)形結(jié)構(gòu),以表示“部分-整體”的層次結(jié)構(gòu),使得用戶(hù)對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。

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

19 適配器模式

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

其實(shí)適配器模式很容易理解,我們?cè)贏ndroid開(kāi)發(fā)時(shí)也經(jīng)常用到。比較典型的有ListView和RecyclerView。為什么ListView需要使用適配器呢?主要是,ListView只關(guān)心它的每個(gè)ItemView,而不關(guān)心這個(gè)ItemView具體顯示的是什么。而我們的數(shù)據(jù)源存放的是要顯示的內(nèi)容,它保存了每一個(gè)ItemView要顯示的內(nèi)容。ListView和數(shù)據(jù)源之間沒(méi)有任何關(guān)系,這時(shí)候,需要通過(guò)適配器,適配器提供getView方法給ListView使用,每次ListView只需提供位置信息給getView函數(shù),然后getView函數(shù)根據(jù)位置信息向數(shù)據(jù)源獲取對(duì)應(yīng)的數(shù)據(jù),根據(jù)數(shù)據(jù)返回不同的View。

20 裝飾模式

定義:動(dòng)態(tài)的給一個(gè)對(duì)象添加額外的智者,就增加功能來(lái)說(shuō),裝飾模式比子類(lèi)繼承的方式更靈活。
通過(guò)簡(jiǎn)單代碼來(lái)理解裝飾模式:

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

public class ConcreteComponent extends Component{
    public void operate(){
        //具體的實(shí)現(xiàn)
    }

}

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哪里出現(xiàn)了裝飾模式呢?我們平時(shí)經(jīng)常用到Context類(lèi),但是其實(shí)Context類(lèi)只是個(gè)抽象類(lèi),具體實(shí)現(xiàn)是ContextImpl,那么誰(shuí)是ContextImpl的裝飾類(lèi)呢?我們知道Activity是個(gè)Context,但是Activity 并不是繼承于Context,而是繼承于ContextThremeWrapper.而ContextThremeWrapper繼承于ContextWrapper,ContextWrapper繼承Context.說(shuō)了這么多,跟裝飾模式有啥關(guān)系?主要是引入ContextWrapper這個(gè)類(lèi)。ContextWrapper內(nèi)部有個(gè)Context引用mContext,并且ContextWrapper中對(duì)Context的每個(gè)方法都有實(shí)現(xiàn),在實(shí)現(xiàn)中調(diào)用的就是mContext相同的方法。

21 享元模式

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

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

在Android哪里用到了享元模式呢?線程通信中的Message,每次我們獲取Message時(shí)調(diào)用Message.obtain()其實(shí)就是從消息池中取出可重復(fù)使用的消息,避免產(chǎn)生大量的Message對(duì)象。

22 外觀模式

定義:要求一個(gè)子系統(tǒng)的外部與其內(nèi)部的通信必須通過(guò)一個(gè)統(tǒng)一的對(duì)象進(jìn)行。

怎么理解呢,舉個(gè)例子,我們?cè)趩?dòng)計(jì)算機(jī)時(shí),只需按一下開(kāi)關(guān)鍵,無(wú)需關(guān)系里面的磁盤(pán)、內(nèi)存、cpu、電源等等這些如何工作,我們只關(guān)心他們幫我啟動(dòng)好了就行。實(shí)際上,由于里面的線路太復(fù)雜,我們也沒(méi)辦法去具體了解內(nèi)部電路如何工作。主機(jī)提供唯一一個(gè)接口“開(kāi)關(guān)鍵”給用戶(hù)就好。

那么Android哪里使用到了外觀模式呢?依然回到Context,Android內(nèi)部有很多復(fù)雜的功能比如startActivty、sendBroadcast、bindService等等,這些功能內(nèi)部的實(shí)現(xiàn)非常復(fù)雜,如果你看了源碼你就能感受得到,但是我們無(wú)需關(guān)心它內(nèi)部實(shí)現(xiàn)了什么,我們只關(guān)心它幫我們啟動(dòng)Activity,幫我們發(fā)送了一條廣播,綁定了Activity等等就夠了。

23 橋接模式

定義:將抽象部分與實(shí)現(xiàn)部分分離,使他們獨(dú)立地進(jìn)行變化。
其實(shí)就是,一個(gè)類(lèi)存在兩個(gè)維度的變化,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展。

在Android中橋接模式用的很多,舉個(gè)例子,對(duì)于一個(gè)View來(lái)說(shuō),它有兩個(gè)維度的變化,一個(gè)是它的描述比如Button、TextView等等他們是View的描述維度上的變化,另一個(gè)維度就是將View真正繪制到屏幕上,這跟Display、HardwareLayer和Canvas有關(guān)。這兩個(gè)維度可以看成是橋接模式的應(yīng)用。

24 MVC、MVP、MVVP模式

MVC
全稱(chēng)為Model-View-Controller,也就是模型-視圖-控制器。MVC結(jié)構(gòu)如下圖所示:

MVC

在Android中對(duì)MVC的應(yīng)用很經(jīng)典,我們的布局文件如main.xml就是對(duì)應(yīng)View層,本地的數(shù)據(jù)庫(kù)數(shù)據(jù)或者是網(wǎng)絡(luò)下載的數(shù)據(jù)就是對(duì)應(yīng)Model層,而Activity對(duì)應(yīng)Controller層。

MVP
MVP全稱(chēng)為Model View Presenter,目前MVP在Android應(yīng)用開(kāi)發(fā)中越來(lái)越重要了,它的結(jié)構(gòu)圖如下:

MVP

它降低了View與Model之間的耦合。徹底將View與Model分離。MVP不是一種標(biāo)準(zhǔn)化的模式,它由很多種實(shí)現(xiàn)。

MVVM

全稱(chēng)是Mode View ViewModel,它的結(jié)構(gòu)如下所示:


MVVM

我們?cè)谑褂肔istView時(shí),會(huì)自定義一個(gè)ViewHolder,在RecyclerView中是必須使用ViewHolder,這主要是提高性能,因?yàn)椴恍枰看稳フ{(diào)用findViewById來(lái)獲取View。其實(shí)ViewHolder就是個(gè)ViewModel。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容