Android 源碼中的設計模式

面向對象的六大原則

  • 單一職責原則所謂職責是指類變化的原因。如果一個類有多於一個的動機被改變,那麼這個類就具有多於一個的職責。而單一職責原則就是指一個類或者模塊應該有且只有一個改變的原因。通俗的說,即一個類只負責一項職責,將一組相關性很高的函數、數據封裝到一個類中。
  • 開閉原則對於擴展是開放的,這意味着模塊的行爲是可以擴展的。當應用的需求改變時,我們可以對模塊進行擴展,使其具有滿足那些改變的新行爲。
    對於修改是關閉的,對模塊行爲進行擴展時,不必改動模塊的源代碼。通俗的說,儘量通過擴展的方式實現系統的升級維護和新功能添加,而不是通過修改已有的源代碼。
  • 里氏替換原則使用“抽象(Abstraction)”和“多態(Polymorphism)”將設計中的靜態結構改爲動態結構,維持設計的封閉性。任何基類可以出現的地方,子類一定可以出現。在軟件中將一個基類對象替換成它的子類對象,程序將不會產生任何錯誤和異常,反過來則不成立。在程序中儘量使用基類類型來對對象進行定義,而在運行時再確定其子類類型,用子類對象來替換父類對象。
  • 依賴倒置原則高層次的模塊不應該依賴於低層次的模塊,他們都應該依賴於抽象。抽象不應該依賴於具體實現,具體實現應該依賴於抽象。程序要依賴於抽象接口,不要依賴於具體實現。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合(各個模塊之間相互傳遞的參數聲明爲抽象類型,而不是聲明爲具體的實現類)。
  • 接口隔離原則一個類對另一個類的依賴應該建立在最小的接口上。其原則是將非常龐大的、臃腫的接口拆分成更小的更具體的接口。
  • 迪米特原則又叫作最少知識原則,就是說一個對象應當對其他對象有儘可能少的瞭解。
    通俗地講,一個類應該對自己需要耦合或調用的類知道得最少,不關心被耦合或調用的類的內部實現,只負責調用你提供的方法。下面開始設計模式學習...

1. Singleton(單例模式)

作用:

保證在Java應用程序中,一個類Class只有一個實例存在。

好處:

由於單例模式在內存中只有一個實例,減少了內存開銷。

單例模式可以避免對資源的多重佔用,例如一個寫文件時,由於只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。

單例模式可以再系統設置全局的訪問點,優化和共享資源訪問。

使用情況:

建立目錄 數據庫連接的單線程操作

某個需要被頻繁訪問的實例對象

1.1 使用方法

第一種形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
 
    /* 持有私有靜態實例,防止被引用,此處賦值爲null,目的是實現延遲加載 */
    private static Singleton instance = null;
 
    /* 私有構造方法,防止被實例化 */
    private Singleton() {
    }
 
    /* 懶漢式:第一次調用時初始Singleton,以後就不用再生成了
    靜態方法,創建實例 */
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

但是這有一個問題,不同步啊!在對據庫對象進行的頻繁讀寫操作時,不同步問題就大了。

第二種形式

既然不同步那就給getInstance方法加個鎖唄!我們知道使用synchronized關鍵字可以同步方法和同步代碼塊,所以:

1
2
3
4
5
6
public static synchronized Singleton getInstance() {
    if(instance == null) {
        instance = new Singleton();
    }
    return instance;
}

或是

1
2
3
4
5
6
7
8
public static Singleton getInstance() {
     synchronized(Singleton.class) {
         if(instance == null) {
             instance = new Singleton();
         }
     }
     return instance;
 }

獲取Singleton實例:

1
Singleton.getInstance().方法()

1.2android中的Singleton

軟鍵盤管理的 InputMethodManager

源碼(以下的源碼都是5.1的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class InputMethodManager {
//.........
     static InputMethodManager sInstance;
//.........
     public static InputMethodManager getInstance() {
         synchronized(InputMethodManager.class) {
             if(sInstance == null) {
                 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
                 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
                 sInstance = new InputMethodManager(service, Looper.getMainLooper());
             }
             return sInstance;
         }
     }

使用的是第二種同步代碼塊的單例模式(可能涉及到多線程),類似的還有
AccessibilityManager(View獲得點擊、焦點、文字改變等事件的分發管理,對整個系統的調試、問題定位等)
BluetoothOppManager等。

當然也有同步方法的單例實現,比如:CalendarDatabaseHelper

1
2
3
4
5
6
public static synchronized CalendarDatabaseHelper getInstance(Context context) {
    if(sSingleton == null) {
        sSingleton = new CalendarDatabaseHelper(context);
    }
    return sSingleton;
}

注意Application並不算是單例模式

1
2
3
4
5
6
public class Application extends ContextWrapper implements ComponentCallbacks2 {
 
     public Application() {
         super(null);
     }
}

在Application源碼中,其構造方法是公有的,意味着可以生出多個Application實例,但爲什麼Application能實現一個app只存在一個實例呢?請看下面:

在ContextWrapper源碼中:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ContextWrapper extends Context {
    Context mBase;
 
    public ContextWrapper(Context base) {
        mBase = base;
    }
 
    protected void attachBaseContext(Context base) {
        if(mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

ContextWrapper構造函數傳入的base爲null, 就算有多個Application實例,但是沒有通過attach()綁定相關信息,沒有上下文環境,三個字。

然並卵

2. Factory(工廠模式)

 定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
對同一個接口的實現類進行管理和實例化創建

這裏寫圖片描述

假設我們有這樣一個需求:

動物Animal,它有行爲move()。有兩個實現類cat和dog。爲了統一管理和創建我們設計一個工廠模式。
同時兩個子類有各自的行爲,Cat有eatFish(),Dog有eatBone().

結構圖:
這裏寫圖片描述

Animal接口:

1
2
3
interface animal {
    void move();
}

Cat類:

1
2
3
4
5
6
7
8
9
10
11
public class Cat implement sAnimal{
 
    @Override
    public void move() {
        // TODO Auto-generated method stub
        System.out.println("我是隻肥貓,不愛動");
    }
    public void eatFish() {
        System.out.println("愛吃魚");
    }
}

Dog類:

1
2
3
4
5
6
7
8
9
10
11
public class Dog implements Animal{
 
    @Override
    public void move() {
        // TODO Auto-generated method stub
        System.out.println("我是狗,跑的快");
    }
    public void eatBone() {
        System.out.println("愛吃骨頭");
    }
}

那麼現在就可以建一個工廠類(Factory.java)來對實例類進行管理和創建了.

1
2
3
4
5
6
7
8
9
10
11
public class Factory {
    //靜態工廠方法
    //多處調用,不需要實例工廠類
     public static Cat produceCat() {
            return new Cat();
        }
     public static Dog produceDog() {
            return new Dog();
        }
//當然也可以一個方法,通過傳入參數,switch實現
}

使用:

1
2
3
4
5
6
Animal cat = Factory.produceCat();
cat.move();
//-----------------------------
Dog dog = Factory.produceDog();
dog.move();
dog.eatBone();

工廠模式在業界運用十分廣泛,如果都用new來生成對象,隨着項目的擴展,animal還可以生出許多其他兒子來,當然兒子還有兒子,同時也避免不了對以前代碼的修改(比如加入後來生出兒子的實例),怎麼管理,想着就是一團糟。

1
Animal cat = Factory.produceCat();

這裏實例化了Animal但不涉及到Animal的具體子類(減少了它們之間的偶合聯繫性),達到封裝效果,也就減少錯誤修改的機會。

Java面向對象的原則,封裝(Encapsulation)和分派(Delegation)告訴我們:具體事情做得越多,越容易範錯誤,

一般來說,這樣的普通工廠就可以滿足基本需求。但是我們如果要新增一個Animal的實現類panda,那麼必然要在工廠類裏新增了一個生產panda的方法。就違背了 閉包的設計原則(對擴展要開放對修改要關閉) ,於是有了抽象工廠模式。

2.1 Abstract Factory(抽象工廠)

抽象工廠模式提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
啥意思?就是把生產抽象成一個接口,每個實例類都對應一個工廠類(普通工廠只有一個工廠類),同時所有工廠類都繼承這個生產接口

生產接口Provider:

1
2
3
interface Provider {
    Animal produce();
}

每個產品都有自己的工廠
CatFactory:

1
2
3
4
5
6
7
8
public class CatFactory implements Provider{
 
    @Override
    public Animal produce() {
        // TODO Auto-generated method stub
        return new Cat();
    }
}

DogFactory:

1
2
3
4
5
6
7
8
public class DogFactory implements Provider{
 
    @Override
    public Animal produce() {
        // TODO Auto-generated method stub
        return new Dog();
    }
}

產品生產:

1
2
3
Provider provider = new CatFactory();
Animal cat =provider.produce();
cat.move();

現在我們要加入panda,直接新建一個pandaFactory就行了,這樣我們系統就非常靈活,具備了動態擴展功能。

2.1 Android中的Factory

比如AsyncTask的抽象工廠實現:

工廠的抽象:

1
2
3
4
public interface ThreadFactory {
//省略爲備註
    Thread new Thread(Runnable r);
 }

產品的抽象(new Runnable就是其實現類):

1
2
3
4
   public interface Runnable {
//省略爲備註
    public abstract voidrun();
 }

AsyncTask中工廠類的實現:

1
2
3
4
5
6
7
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);
 
    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

我們可以創建另外類似的工廠,生產某種專門的線程(多線程),非常容易擴展。
當然,android中的應用還有很多(比如BitmapFactory),有興趣的小夥伴可以去扒一扒。

3. Adapter(適配器模式)

將一個類的接口轉換成客戶希望的另外一個接口

我們經常碰到要將兩個沒有關係的類組合在一起使用,第一解決方案是:修改各自類的接口,但是如果我們沒有源代碼,或者,我們不願意爲了一個應用而修改各自的接口。 怎麼辦?

使用Adapter,在這兩種接口之間創建一個混合接口。

模式中的角色

需要適配的類(Adaptee):需要適配的類。

適配器(Adapter):通過包裝一個需要適配的對象,把原接口轉換成目標接口。

目標接口(Target):客戶所期待的接口。可以是具體的或抽象的類,也可以是接口。

這裏寫圖片描述

1
2
3
4
5
6
7
8
9
10
11
// 需要適配的類
class Adaptee {
    public void specificRequest() {
        System.out.println("需要適配的類");
    }
}
 
// 目標接口
interface Target {
    public void request();
}

實現方式:

①、對象適配器(採用對象組合方式實現)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 適配器類實現標準接口
class Adapter implements Target{
    // 直接關聯被適配類
    private Adaptee adaptee;
 
    // 可以通過構造函數傳入具體需要適配的被適配類對象
    public Adapter (Adaptee adaptee) {
        this.adaptee = adaptee;
    }
 
    public void request() {
        // 這裏是使用委託的方式完成特殊功能
        this.adaptee.specificRequest();
    }
}
 
// 測試類
public class Client {
    public static void main(String[] args) {
        // 需要先創建一個被適配類的對象作爲參數
        Target adapter = new Adapter(new Adaptee());
        adapter.request();
    }
}

如果Target不是接口而是一個具體的類的情況,這裏的Adapter直接繼承Target就可以了。

②、類的適配器模式(採用繼承實現)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 適配器類繼承了被適配類同時實現標準接口
class Adapter extends Adaptee implements Target{
    public void request() {
        super.specificRequest();
    }
}
 
// 測試類
    public static void main(String[] args) {
        // 使用適配類
        Target adapter = new Adapter();
        adapter.request();
    }

如果Target和 Adaptee都是接口,並且都有實現類。 可以通過Adapter實現兩個接口來完成適配。
還有一種叫PluggableAdapters,可以動態的獲取幾個adapters中一個。使用Reflection技術,可以動態的發現類中的Public方法。

優點

系統需要使用現有的類,而此類的接口不符合系統的需要。那麼通過適配器模式就可以讓這些功能得到更好的複用
將目標類和適配者類解耦,通過引入一個適配器類重用現有的適配者類,而無需修改原有代碼,更好的擴展性

缺點

過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實內部被適配成了B接口的實現。如果不是必要,不要使用適配器,而是直接對系統進行重構。

3.1 Android中的Adapter

android中的Adapter就有很多了,這個大家都經常用。
Adapter是AdapterView視圖與數據之間的橋樑,Adapter提供對數據的訪問,也負責爲每一項數據產生一個對應的View。

Adapter的繼承結構

這裏寫圖片描述

BaseAdapter的部分源碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract classBaseAdapter implements ListAdapter, SpinnerAdapter {
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
 
    public boolean hasStableIds() {
        return false;
    }
 
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
 
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

ListAdapter, SpinnerAdapter都是Target ,數據是Adaptee ,採用對象組合方式。

4. Chain of Responsibility(責任鏈模式)

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。

編程中的小體現:

1
2
3
4
5
6
7
8
9
10
11
12
if(a<10){
    ...
}
else if(a<20){
    ...
}
else if(a<30){
    ...
}
else{
    ...
}

程序必須依次掃描每個分支進行判斷,找到對應的分支進行處理。

責任鏈模式的優點

可以降低系統的耦合度(請求者與處理者代碼分離),簡化對象的相互連接,同時增強給對象指派職責的靈活性,增加新的請求處理類也很方便;

責任鏈模式的缺點

不能保證請求一定被接收,且對於比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響,而且在進行代碼調試時不太方便。
每次都是從鏈頭開始,這也正是鏈表的缺點。

4.1 Android中的Chain of Responsibility

觸摸、按鍵等各種事件的傳遞

這裏寫圖片描述

有興趣的可以看一下這篇文章View事件分發機制源碼分析,我這就不多說了。

5. Observer(觀察者模式)

有時被稱作發佈/訂閱模式,觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。

將一個系統分割成一個一些類相互協作的類有一個不好的副作用,那就是需要維護相關對象間的一致性。我們不希望爲了維持一致性而使各類緊密耦合,這樣會給維護、擴展和重用都帶來不便。觀察者就是解決這類的耦合關係的(依賴關係並未完全解除,抽象通知者依舊依賴抽象的觀察者。)。

觀察者模式的組成

①抽象主題(Subject)

它把所有觀察者對象的引用保存到一個聚集裏,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。

②具體主題(ConcreteSubject)

將有關狀態存入具體觀察者對象;在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。

③抽象觀察者(Observer)

爲所有的具體觀察者定義一個接口,在得到主題通知時更新自己。

④具體觀察者(ConcreteObserver)

實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題狀態協調。

這裏寫圖片描述

言語蒼白,上代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//抽象觀察者
public interface Observer
{
  public void update(String str);
 
}
 
//具體觀察者
public class ConcreteObserver implements Observer{
    @Override
    public void update(String str) {
        // TODO Auto-generated method stub
        System.out.println(str);
    }
}
 
//抽象主題
public interface Subject
{
    public void addObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers(String str);
}
 
//具體主題
public class ConcreteSubject implements Subject{
    // 存放觀察者
    private List&lt;Observer&gt; list = new ArrayList&lt;Observer&gt;();
    @Override
    public void addObserver(Observer observer) {
        // TODO Auto-generated method stub
        list.add(observer);
    }
 
    @Override
    public void removeObserver(Observer observer) {
        // TODO Auto-generated method stub
        list.remove(observer);
    }
 
    @Override
    public void notifyObservers(String str) {
        // TODO Auto-generated method stub
        for(Observer observer:list){
            observer.update(str);
        }
    }
}

下面是測試類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * @author fanrunqi
 */
public class Test {
     public static void main(String[] args) {
        //一個主題
         ConcreteSubject eatSubject = new ConcreteSubject();
         //兩個觀察者
         ConcreteObserver personOne = new ConcreteObserver();
         ConcreteObserver personTwo = new ConcreteObserver();
         //觀察者訂閱主題
         eatSubject.addObserver(personOne);
         eatSubject.addObserver(personTwo);
 
         //通知開飯了
         eatSubject.notifyObservers("開飯啦");
    }
}

“關於代碼你有什麼想說的?”
“沒有,都在代碼裏了”
“(⊙o⊙)哦.....”

5.1 Android中的Observer

觀察者模式在android中運用的也比較多,最熟悉的ContentObserver。

① 抽象類ContentResolver中(Subject)

註冊觀察者:

1
2
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
        ContentObserver observer, int userHandle)

取消觀察者:

1
public final void unregisterContentObserver(ContentObserver observer)

抽象類ContentObserver中(Observer)

1
2
3
4
5
6
7
8
9
10
11
public void onChange(boolean selfChange) {
     // Do nothing.  Subclass should override.
 }
 
public void onChange(boolean selfChange, Uri uri, int userId) {
    onChange(selfChange, uri);
}
 
public void onChange(boolean selfChange, Uri uri) {
    onChange(selfChange);
}

觀察特定Uri引起的數據庫的變化,繼而做一些相應的處理(最終都調用的第一個函數).

② DataSetObserver,其實這個我們一直在用,只是沒意識到。

我們再看到BaseAdapter的部分源碼:

1
2
3
4
5
6
7
public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
}
 
public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
}

上面兩個方法分別向向BaseAdater註冊、註銷一個DataSetObserver實例。

DataSetObserver 的源碼:

1
2
3
4
5
6
7
8
9
10
public abstract class DataSetObserver {
 
  public void onChanged() {
        // Do nothing
    }
 
    public void onInvalidated() {
        // Do nothing
    }
}

DataSetObserver就是一個觀察者,它一旦發現BaseAdapter內部數據有變量,就會通過回調方法DataSetObserver.onChanged和DataSetObserver.onInvalidated來通知DataSetObserver的實現類。

6. Builder(建造者模式)

建造者模式:是將一個複雜的對象的構建與它的表示分離(同構建不同表示),使得同樣的構建過程可以創建不同的表示。

  一個人活到70歲以上,都會經歷這樣的幾個階段:嬰兒,少年,青年,中年,老年。並且每個人在各個階段肯定是不一樣的,世界上不存在兩個人在人生的這5個階段的生活完全一樣,但是活到70歲以上的人,都經歷了這幾個階段是肯定的。實際上這是一個比較經典的建造者模式的例子了。

將複雜的內部創建封裝在內部,對於外部調用的人來說,只需要傳入建造者和建造工具,對於內部是如何建造成成品的,調用者無需關心。

建造者模式通常包括下面幾個角色:

① Builder:一個抽象接口,用來規範產品對象的各個組成成分的建造。

② ConcreteBuilder:實現Builder接口,針對不同的商業邏輯,具體化複雜對象的各部分的創建,在建造過程完成後,提供產品的實例。

③ Director:指導者,調用具體建造者來創建複雜對象的各個部分,不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建。

④ Product:要創建的複雜對象。

與抽象工廠的區別:在建造者模式裏,有個指導者,由指導者來管理建造者,用戶是與指導者聯繫的,指導者聯繫建造者最後得到產品。即建造模式可以強制實行一種分步驟進行的建造過程。

這裏寫圖片描述

Product和產品的部分Part接口

1
2
public interface Product { }
public interface Part { }

Builder:

1
2
3
4
5
6
public interface Builder {
void buildPartOne();
void buildPartTwo();
 
Product getProduct();
}

ConcreteBuilder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//具體建造工具
public class ConcreteBuilder implements Builder {
Part partOne, partTwo;
 
public void buildPartOne() {
//具體構建代碼
};
public void buildPartTwo() {
//具體構建代碼
};
public Product getProduct() {
//返回最後組裝的產品
};
}

Director :

1
2
3
4
5
6
7
8
9
10
11
public class Director {
private Builder builder;
 
public Director( Builder builder ) {
this.builder = builder;
}
public void construct() {
builder.buildPartOne();
builder.buildPartTwo();
}
}

建造:

1
2
3
4
5
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director(builder);
//開始各部分建造
director.construct();
Product product = builder.getResult();

優點:

客戶端不必知道產品內部組成的細節。

具體的建造者類之間是相互獨立的,對系統的擴展非常有利。

由於具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響。

使用場合:

創建一些複雜的對象時,這些對象的內部組成構件間的建造順序是穩定的,但是對象的內部組成構件面臨着複雜的變化

要創建的複雜對象的算法,獨立於該對象的組成部分,也獨立於組成部分的裝配方法時。

6.1 Android中的Builder

android中的Dialog就使用了Builder Pattern,下面來看看AlertDialog的部分源碼。

1
2
3
4
5
6
7
8
9
public static class Builder {
    private final AlertController.AlertParams P;
    private int mTheme;
 
    public Builder(Context context, int theme) {
        P = new AlertController.AlertParams(new ContextThemeWrapper(
                context, resolveDialogTheme(context, theme)));
        mTheme = theme;
    }

AlertDialog的Builder 是一個靜態內部類,沒有定義Builder 的抽象接口。
對AlertDialog設置的屬性會保存在Build類的成員變量P(AlertController.AlertParams)中。

Builder類中部分方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Builder setTitle(int titleId) {
    P.mTitle = P.mContext.getText(titleId);
    return this;
}
public Builder setMessage(int messageId) {
    P.mMessage = P.mContext.getText(messageId);
    return this;
}
 
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
    P.mPositiveButtonText = text;
    P.mPositiveButtonListener = listener;
    return this;
}

而show()方法會返回一個結合上面設置的dialog實例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        public AlertDialog show() {
            AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
    }
 
}
        public AlertDialog create() {
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if(P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if(P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }

簡單建造:

1
2
3
4
5
new AlertDialog.Builder(context)
 .setTitle("標題")
 .setMessage("消息框")
 .setPositiveButton("確定"null)
 .show();

7. Memento(備忘錄模式)

備忘錄模式又叫做快照模式(Snapshot Pattern)或Token模式,是對象的行爲模式。

備忘錄對象是一個用來存儲另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。

備忘錄模式所涉及的角色有三個:

① Originator(發起人): 負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態。Originator可根據需要決定Memento存儲Originator的哪些內部狀態。

② Memento(備忘錄): 負責存儲Originnator對象的內部狀態,並可防止Originator以外的其他對象訪問備忘錄Memento,備忘錄有兩個接口,Caretaker只能看到備忘錄的窄接口,它只能將備忘錄傳遞給其他對象。

③、 Caretaker(管理者):負責保存好備忘錄Memento,不能對備忘錄的內容進行操作或檢查。

這裏寫圖片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class Originator {
 
    private String state;
    /**
     * 工廠方法,返回一個新的備忘錄對象
     */
    public Memento createMemento(){
        return new Memento(state);
    }
    /**
     * 將發起人恢復到備忘錄對象所記載的狀態
     */
    public void restoreMemento(Memento memento){
        this.state = memento.getState();
    }
 
    public String getState() {
        return state;
    }
 
    public void setState(String state) {
        this.state = state;
        System.out.println("當前狀態:"this.state);
    }
 
}
 
public class Memento {
 
    private String state;
 
    public Memento(String state){
        this.state = state;
    }
 
    public String getState() {
        returnstate;
    }
 
    public void setState(String state) {
        this.state = state;
    }
 
}
 
public class Caretaker {
 
    private Memento memento;
    /**
     * 備忘錄的取值方法
     */
    public Memento retrieveMemento(){
        return this.memento;
    }
    /**
     * 備忘錄的賦值方法
     */
    public void saveMemento(Memento memento){
        this.memento = memento;
    }
}

使用:

1
2
3
4
5
6
7
8
9
10
Originator o = new Originator();
Caretaker c = new Caretaker();
//改變負責人對象的狀態
o.setState("On");
//創建備忘錄對象,並將發起人對象的狀態儲存起來
 c.saveMemento(o.createMemento());
//修改發起人的狀態
o.setState("Off");
//恢復發起人對象的狀態
 o.restoreMemento(c.retrieveMemento());

不需要了解對象的內部結構的情況下備份對象的狀態,方便以後恢復。

7.1 Android中的Memento

Activity的onSaveInstanceState和onRestoreInstanceState就是通過Bundle(相當於備忘錄對象)這種序列化的數據結構來存儲Activity的狀態,至於其中存儲的數據結構,這兩個方法不用關心。

還是看一下源碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    Parcelable p = mFragments.saveAllState();
    if(p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if(mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if(windowState != null) {
            mWindow.restoreHierarchyState(windowState);
        }
    }
}

8. Prototype(原型模式)

原型模式,能快速克隆出一個與已經存在對象類似的另外一個我們想要的新對象。
工作原理是:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建。

分爲深拷貝和淺拷貝。深拷貝就是把對象裏面的引用的對象也要拷貝一份新的對象,並將這個新的引用對象作爲拷貝的對象引用(多讀兩遍)。

一般使用原型模式有個明顯的特點,就是實現cloneable的clone()方法。

在Intent源碼中:

1
2
3
4
@Override
public Object clone() {
    return new Intent(this);
}

這裏Intent通過實現Cloneable接口來實現原型拷貝。

9. Strategy(策略模式)

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

舉例:
一個影碟機,你往裏面插什麼碟子,就能放出什麼電影。
屬性動畫,設置不同的插值器對象,就可以得到不同的變化曲線。
返回值解析,傳入什麼樣的解析器,就可以把二進制數據轉換成什麼格式的數據,比如String、Json、XML。

策略模式其實就是多態的一個淋漓精緻的體現。

在android中不同Animation動畫的實現,主要是依靠Interpolator的不同而實現的。

1
2
3
public void setInterpolator(Interpolator i) {
    mInterpolator = i;
}

10. Template(模板模式)

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

實現流程已經確定,實現細節由子類完成

生命週期對於我們都不陌生,它就是典型的Template模式,在具體流程確定的情況下,至於我們要複寫生命週期那些方法,實現那些功能由繼承activity的子類去具體實現。

關鍵在於必須有具體的執行流程,比如AsyncTask。

11. Proxy(代理模式)

定義:爲其他對象提供一種代理以控制對這個對象的訪問。
代理: 在出發點到目的地之間有一道中間層。

應用:Android跨進程通信方式 ,建議去了解一下Binder機制。

12. Interpreter(解釋器模式)

定義語言的文法,並且建立一個解釋器來解釋該語言中的句子。

比如Android中通過PackageManagerService來解析AndroidManifest.xml中定義的Activity、service等屬性。

13. State(狀態模式)

行爲是由狀態來決定的,不同狀態下有不同行爲。

注意:狀態模式的行爲是平行的、不可替換的,策略模式的行爲是彼此獨立可相互替換的。

體現:不同的狀態執行不同的行爲,當WIFI開啓時,自動掃描周圍的接入點,然後以列表的形式展示;當wifi關閉時則清空。

14. Command(命令模式)

我們有很多命令,把它們放在一個下拉菜單中,用戶通過先選擇菜單再選擇具體命令,這就是Command模式。

本來用戶(調用者)是直接調用這些命令的,在菜單上打開文檔,就直接指向打開文檔的代碼,使用Command模式,就是在這兩者之間增加一箇中間者,將這種直接關係拗斷,同時兩者之間都隔離,基本沒有關係了。

顯然這樣做的好處是符合封裝的特性,降低耦合度,有利於代碼的健壯性 可維護性 還有複用性。Command是將對行爲進行封裝的典型模式,Factory是將創建進行封裝的模式。

android底層邏輯對事件的轉發處理就用到了Command模式。

15. Iterator(迭代模式)

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

應用:

在Java中的Iterator類。

Android中的 Cursor。

1
cursor.moveToFirst();

16. Composite(組合模式)

將對象以樹形結構組織起來,以達成“部分-整體” 的層次結構,使得客戶端對單個對象和組合對象的使用具有一致性。

Android中View的結構是樹形結構,每個ViewGroup包含一系列的View,而ViewGroup本身又是View。這是Android中非常典型的組合模式。

17. Flyweight(共享模式/享元模式)

定義:避免大量擁有相同內容的小類的開銷(如耗費內存),使大家共享一個類(元類)。

面嚮對象語言的原則就是一切都是對象,但是如果真正使用起來,有時對象數可能顯得很龐大,比如,字處理軟件,如果以每個文字都作爲一個對象,幾千個字,對象數就是幾千,無疑耗費內存,那麼我們還是要”求同存異”,找出這些對象羣的共同點,設計一個元類,封裝可以被共享的類,另外,還有一些特性是取決於應用(context),是不可共享的,這也Flyweight中兩個重要概念內部狀態intrinsic和外部狀態extrinsic之分。

說白點,就是先捏一個的原始模型,然後隨着不同場合和環境,再產生各具特徵的具體模型,很顯然,在這裏需要產生不同的新對象,所以Flyweight模式中常出現Factory模式。Flyweight的內部狀態是用來共享的,Flyweight factory負責維護一個Flyweight pool(模式池)來存放內部狀態的對象。

Flyweight模式是一個提高程序效率和性能的模式,會大大加快程序的運行速度。應用場合很多:比如你要從一個數據庫中讀取一系列字符串,這些字符串中有許多是重複的,那麼我們可以將這些字符串儲存在Flyweight池(pool)中。

在Android線程通信中,每次獲取Message時調Message.obtain()其實就是從消息池中取出可重複使用的消息,避免產生大量的Message對象。

最後

那麼問題來了,什麼是設計模式?

這裏寫圖片描述

設計模式是前輩、大牛在實際編程中對遇到的問題解決方案的抽象。

請尊重個人勞動成果,轉載註明出處謝謝!
http://blog.csdn.net/amazing7/article/details/51719404

轉載請註明:Android開發中文站 » Android 源碼中的設計模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章