我的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哪里用到了備忘錄模式呢?Activity
的onSaveInstanceState
和onRestoreInstanceState
就是用到了備忘錄模式,分別用于保存和恢復(fù)。
13 迭代器模式
迭代器模式定義:提供一種方法順序訪問(wèn)一個(gè)容器對(duì)象中的各個(gè)元素,而不需要暴露該對(duì)象的內(nèi)部表示。
相信熟悉Java的你肯定知道,Java中就有迭代器Iterator
類(lèi),本質(zhì)上說(shuō),它就是用迭代器模式。
按照慣例,看看Android中哪里用到了迭代器模式,Android源碼中,最典型的就是Cursor
用到了迭代器模式,當(dāng)我們使用SQLiteDatabase
的query
方法時(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)中,比如Activity
的onCreate
、onStart
等等。這樣子類(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í),比如ActivityManager
、WindowManager
等(它們都是Binder
),首先是向ServiceManager
查詢(xún)指定標(biāo)示符對(duì)應(yīng)的Binder,再由ServiceManager
返回Binder
的引用。并且客戶(hù)端和服務(wù)端之間的通信是通過(guò)Binder驅(qū)動(dòng)來(lái)實(shí)現(xiàn),這里的ServiceManager
和Binder
驅(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)如下圖所示:
在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)圖如下:
它降低了View與Model之間的耦合。徹底將View與Model分離。MVP不是一種標(biāo)準(zhǔn)化的模式,它由很多種實(shí)現(xiàn)。
MVVM
全稱(chēng)是Mode View ViewModel,它的結(jié)構(gòu)如下所示:
我們?cè)谑褂肔istView時(shí),會(huì)自定義一個(gè)ViewHolder,在RecyclerView中是必須使用ViewHolder,這主要是提高性能,因?yàn)椴恍枰看稳フ{(diào)用findViewById來(lái)獲取View。其實(shí)ViewHolder就是個(gè)ViewModel。