談談23種設計模式在Android源碼及項目中的應用

談談23種設計模式在Android源碼及項目中的應用

本文首發于個人博客:Lam's Blog - 談談23種設計模式在Android源碼及項目中

的應用,文章由MarkDown語法編寫,可能不同平臺渲染效果不一,如果有存在排

版錯誤圖片無法顯示等問題,煩請移至個人博客,如果個人博客無法訪問可以留

言告訴我,轉載請聲明個人博客出處,謝謝。

本文將結合實際談談23種設計模式,每種設計模式涉及

定義:抽象化的定義與通俗的描述,盡量說明清楚其含義與應用場景

示例:如果項目中有使用過該模式,則會給出項目中的代碼,否則會給出盡

可能簡單好理解的java代碼

Android:該設計模式在Android源碼框架中哪些地方有使用到

重構:項目中是否存在可以用該模式進行重構的地方,如果有會給出重構前

與重構后的代碼或者思路

用這種方式進行介紹設計模式,旨在結合每天都在接觸的Android實際項目開發更

好地理解設計模式,拉近與設計模式的距離,同時在實際開發與重構中,思考可

以應用的重構手段與設計模式,既能保證寫出復用性與可靠性更高的代碼,也是

對如何利用重構與設計模式這兩大支柱進行優雅編程的最佳實踐與總結。

同時一次性以這種方式介紹23種設計模式,也是出于既然要使用一個模式,那么

就應該要先知道這么一個模式的想法,四人幫的《設計模式》也是對經驗的總

結,但是有巨人托著你上去,又何必自己再摸黑造梯子。

重構不是本章的重點,因為這也是一個非常大的話題,這邊只討論實際項目中是

否有存在一些能用設計模式進行改善的地方。

關于重構,這邊也有寫了一篇博文*** 重構:改善既有代碼的設計 ***,基本列

舉了《重構:改善既有代碼的設計》中的各項要點,后續還會繼續將《重構》中

的手法與設計模式應用到實際項目中,有所總結之后會再寫幾篇實際應用的博

文。

設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目

的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他

人理解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多贏的,

設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊

塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在

現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重復

發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。

單一職責原則

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

說,一個類應該有職責單一。

開閉原則

開閉原則理解起來也不復雜,就是一個類應該對于擴展是開放的,但是對于修改

是封閉的。在一開始編寫代碼時,就應該注意盡量通過擴展的方式實現新的功

能,而不是通過修改已有的代碼實現,否則容易破壞原有的系統,也可能帶來新

的問題,如果發現沒辦法通過擴展來實現,應該考慮是否是代碼結構上的問題,

通過重構等方式進行解決。

里氏替換原則

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第1頁 共38頁 2020/11/3 下午9:00

所有引用基類的地方必須能透明地使用其子類對象。本質上就是說要好好利用繼

承和多態,從而以父類的形式來聲明變量(或形參),為變量(或形參)賦值任

何繼承于這個父類的子類。

依賴倒置原則

依賴倒置主要是實現解耦,使得高層次的模塊不依賴于低層次模塊的具體實現細

節。怎么去理解它呢,我們需要知道幾個關鍵點:

高層模塊不應該依賴底層模塊(具體實現),二者都應該依賴其抽象(抽象

類或接口)

抽象不應該依賴細節

細節應該依賴于抽象

在我們用的Java語言中,抽象就是指接口或者抽象類,二者都是不能直接被實例

化;細節就是實現類,實現接口或者繼承抽象類而產生的類,就是細節。使用

Java語言描述就是:各個模塊之間相互傳遞的參數聲明為抽象類型,而不是聲明

為具體的實現類;

接口隔離原則

類之間的依賴關系應該建立在最小的接口上。其原則是將非常龐大的、臃腫的接

口拆分成更小的更具體的接口。

迪米特原則

一個對象應該對其他的對象有最少的了解.

假設類A實現了某個功能,類B需要調用類A的去執行這個功能,那么類A應該只暴

露一個函數給類B,這個函數表示是實現這個功能的函數,而不是讓類A把實現這

個功能的所有細分的函數暴露給B。

單例模式

定義

確保單例類只有一個實例,并且這個單例類提供一個函數接口讓其他類獲取到這

個唯一的實例。

如果某個類,創建時需要消耗很多資源,即new出這個類的代價很大;或者是這個

類占用很多內存,如果創建太多這個類實例會導致內存占用太多。上述情況下就

應該使用單例模式

實際應用

<pre><code>

// 單例對象

private static AdvertPresenter mInstance;

/**

* 私有化構造函數

/

private AdvertPresenter(){

}

/*

* 獲取AdvertPresenter實例

* @return

*/

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第2頁 共38頁 2020/11/3 下午9:00

public static AdvertPresenter getInstance() {

if (mInstance == null) {

synchronized (AdvertPresenter.class) {

if (mInstance == null) {

mInstance = new AdvertPresenter();

}

}

}

return mInstance;

}

<code></pre>

Android

<pre><code>

//獲取WindowManager服務引用

WindowManager wm =

(WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

</code></pre>

其內部就是通過單例的方式持有一個WindowManager并返回這個對象

重構

項目中存在多次使用Random與Gson的操作,可以將Random與Gson對象封裝成單例

進行使用

建造者模式

定義

將一個復雜對象的構造與它的表示分離,使得同樣的構造過程可以創建不同的表

示。

主要是在創建某個對象時,需要設定很多的參數(通過setter方法),但是這些

參數必須按照某個順序設定,或者是設置步驟不同會得到不同結果。

示例

各類自定義Dialog

Android

<pre><code>

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();

</code></pre>

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第3頁 共38頁 2020/11/3 下午9:00

重構

暫無

原型模式

定義

用原型實例指定創建對象的種類,并通過拷貝這些原型創建新的對象。

可以在類的屬性特別多,但是又要經常對類進行拷貝的時候可以用原型模式,這

樣代碼比較簡潔,而且比較方便。

拷貝時要注意淺拷貝與深拷貝

示例

<pre><code>

private HashMap<String, PointBean> getClonePointMap(Map<String,

PointBean> map) {

HashMap<String, PointBean> clone = new HashMap<>();

if (map != null) {

Iterator iterator = map.entrySet().iterator();

while (iterator.hasNext()) {

Map.Entry entry = (Map.Entry) iterator.next();

String key = (String) entry.getKey();

PointBean pointBean = (PointBean) entry.getValue();

if (pointBean != null) {

//遍歷map并將克隆對象放到新的map中

clone.put(key, pointBean.clone());

} else {

clone.put(key, null);

}

}

}

return clone;

}

</code></pre>

Android

<pre><code>

Intent intent = new Intent(Intent.ACTION_SENDTO, uri);

//克隆副本

Intent copyIntent=(Intetn)shareIntent.clone();

</code></pre>

重構

如果存在逐一去除某個對象的各項參數值,轉而賦值給另一個對象身上時,便可

使用原型模式

工廠模式

簡單工廠模式

定義

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第4頁 共38頁 2020/11/3 下午9:00

建立一個工廠(一個函數或一個類方法)來制造新的對象。

示例

<pre><code>

public static Operation createOperate(string operate)

{

Operation oper = null;

switch (operate)

{

case "+":

{

oper = new OperationAdd();

break;

}

case "-":

{

oper = new OperationSub();

break;

}

case "*":

{

oper = new OperationMul();

break;

}

case "/":

{

oper = new OperationDiv();

break;

}

}

return oper;

}

}

</code></pre>

Android

<pre><code>

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);

}

</code></pre>

在getSystemService方法中就是用到了簡單工廠模式,根據傳入的參數決定創建

哪個對象,由于這些對象以單例模式提前創建好了,所以此處不用new了,直接把

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第5頁 共38頁 2020/11/3 下午9:00

單例返回就好。

重構

<pre><code>

//重構前

public class AdvertPresenter {

...

private void initAdvertManager() {

String[] platforms = mAdConfig.getAllPlatforms();

if (platforms != null && platforms.length > 0) {

int platformSize = platforms.length;

for (int i = 0; i < platformSize; i++) {

String platform = platforms[i];

if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {

FacebookAdvertManager fbAdManager = new FacebookAdvertManager();

mAdvertManager.put(AdvertConstant.AD_PLATFORM_FACEBOOK, fbAdManager);

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB))

{

AdMobAdvertManager adMobAdvertManager = new AdMobAdvertManager();

mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADMOB,

adMobAdvertManager);

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB))

{

MopubAdvertManager mopubAdvertManager = new MopubAdvertManager();

mAdvertManager.put(AdvertConstant.AD_PLATFORM_MOPUB,

mopubAdvertManager);

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {

AdxAdvertManager mopubAdvertManager = new AdxAdvertManager();

mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADX, mopubAdvertManager);

}

}

}

}

...

}

//重構后

public class BaseAdvertManager {

...

public static BaseAdvertManager create(String platform) {

if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {

return new FacebookAdvertManager();

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB))

{

return new MopubAdvertManager();

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {

return new AdxAdvertManager();

} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB))

{

return new AdMobAdvertManager();

} else {

return new NullAdvertManager();//引入NULL對象

}

}

...

}

public class AdvertPresenter {

...

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第6頁 共38頁 2020/11/3 下午9:00

private void initAdvertManager() {

String[] platforms = mAdConfig.getAllPlatforms();

if (platforms != null && platforms.length > 0) {

int platformSize = platforms.length;

for (int i = 0; i < platformSize; i++) {

String platform = platforms[i];

mAdvertManager.put(platform, BaseAdvertManager.create(platform));

}

}

}

...

}

</code></pre>

工廠方法模式

定義

是定義一個創建產品對象的工廠接口,讓其子類決定實例化哪一個類,將實際創

建工作推遲到子類當中。

示例

<pre><code>

public abstract class Product {

public abstract void method();

}

public class ConcreteProduct extends Prodect {

public void method(){

System.out.println("我是具體產品!");

}

}

public abstract class Factory{

public abstract Product createProduct();

}

public class ConcreteFactory extends Factory{

public Product createProduct(){

return new ConcreteProductA();

}

}

</code></pre>

Android

我們在開發中會用到很多數據結構,比如ArrayList,HashMap等。我們先來看下

Java中Collection部分的類集框架的簡要UML圖。

<center>{% qnimg aalasdknazx.png %}</center>

我們知道Iterator是迭代器,用來遍歷一個集合中的元素。而不同的數據結構遍

歷的方式是不一樣的,所以迭代器的實現也是不同的。使用工廠方法模式將迭代

器的具體類型延遲到具體容器類中,比較靈活,容易擴展。

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第7頁 共38頁 2020/11/3 下午9:00

<pre><code>

public interface Iterable<T> {

/**

* Returns an iterator over elements of type {@code T}.

*

* @return an Iterator.

*/

Iterator<T> iterator();

}

</code></pre>

List和Set繼承自Collection接口,Collection接口繼承于Iterable接口。所以

List和Set接口也需要繼承并實現Iterable中的iterator()方法。然后我們常用的

兩個間接實現類ArrayList和HashSet中的iterator方法就給我們具體構造并返回

了一個迭代器對象。

我們找到ArrayList類,查看iterator方法的實現。

<pre><code>

@Override

public Iterator<E> iterator() {

return new ArrayListIterator();

}

</code></pre>

ArrayListIterator類型定義如下:

<pre><code>

private class ArrayListIterator implements Iterator<E> {

/** Number of elements remaining in this iteration */

private int remaining = size;

private int removalIndex = -1;

private int expectedModCount = modCount;

public boolean hasNext() {

return remaining != 0;

}

@SuppressWarnings("unchecked") public E next() {

ArrayList<E> ourList = ArrayList.this;

int rem = remaining;

if (ourList.modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

if (rem == 0) {

throw new NoSuchElementException();

}

remaining = rem - 1;

return (E) ourList.array[removalIndex = ourList.size - rem];

}

public void remove() {

Object[] a = array;

int removalIdx = removalIndex;

if (modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

if (removalIdx < 0) {

throw new IllegalStateException();

}

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第8頁 共38頁 2020/11/3 下午9:00

System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);

a[--size] = null;

removalIndex = -1;

expectedModCount = ++modCount;

}

}

</code></pre>

我們看到這個類實現了Iterator接口,接口的定義如下:

<pre><code>

public interface Iterator<E> {

boolean hasNext();

E next();

default void remove() {

throw new UnsupportedOperationException("remove");

}

default void forEachRemaining(Consumer<? super E> action) {

Objects.requireNonNull(action);

while (hasNext())

action.accept(next());

}

}

</code></pre>

基本的結構也分析完了,接下來對號入座,看一看具體是如何實現工廠方法模式

的。

Iterator————>Product ArrayListIteratorr————>ConcreteProduct

Iterable/List————>Factory ArrayList————>ConcreteFactory

工廠方法使一個類的實例化延遲到子類,對應著將迭代器Iterator的創建從List

延遲到了ArrayList。這就是工廠方法模式。

重構

暫無

抽象工廠模式

定義

為創建一組相關或者是相互依賴的對象提供一個接口,而不需要制定他們的具體

抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可

以向客戶端提供一個接口,使客戶端在不必指定產品的具體情況下,創建多個產

品族中的產品對象。

示例

<pre><code>

public abstract class AbstractProductA{

public abstract void method();

}

public abstract class AbstractProdectB{

public abstract void method();

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第9頁 共38頁 2020/11/3 下午9:00

}

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();

}

}

</code></pre>

Android

由于該模式存在的局限性,Android中很少有用到這個模式的地方,

com.android.internal.policy包下的IPolicy有使用到這個模式,它是關于

Android窗口,窗口管理,布局加載,以及事件回退Handler這一系列窗口相關產

品的抽象工廠,但是其在源碼中其實也只有一個具體的工廠實現。因為這部分結

構較為復雜,代碼量大,有興趣的同學可以自己去查看相關資料或者閱讀源碼。

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第10頁 共38頁 2020/11/3 下午9:00

與工廠方法模式對比

使用

不依賴于產品類實例如何被創建,組合和表達的細節;

產品有多于一個的產品族,而系統只消費其中某一族的產品;

同屬于同一個產品族是在一起使用的;

提供一個產品類的庫,所有產品以同樣的接口出現,從而使使用者不依賴于

實現;

區別

抽象工廠是面向一個工廠方法的升級;

抽象方法提供的是一個產品族,即多個產品等級結構,而工廠方法則是針對

一個產品等級結構;

抽象方法提供的產品是衍生自多個抽象或者接口,而工廠方法則衍生自同一

個抽象或者接口;

優點

抽象工廠模式隔離了具體類的生產,使得客戶并不需要知道什么被創建。

當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使

用同一個產品族中的對象。

增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原

則”。

缺點

增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類,對

“開閉原則”的支持呈現傾斜性。

(可以把示例中的AB當做等級,12當做族群,A1B1屬于同一族群不同等級,

當添加同一等級下新的產品時很方便,但是要添加不同等級的產品就會破壞

“開閉原則”)

由于抽象工廠不易于拓展新的產品族,所以這種設計模式,在提供對外部人員訪

問時,很少使用,也有人說抽象工廠方法模式是一種很“惡心”的設計模式,運

用最為典范的一個是該模式最初的目的,也就是為了適應Unit和Windows兩個操作

系統下的視圖而構建視圖族,視圖族有各自不同的實現;另一個就是Java連接數

據庫的操作中,對不同的數據庫的操作而形成的的對象操作族,但是當再次更換

數據時,所需要造成的接口的修改也十分麻煩,所以擴展性不好

重構

暫無

策略模式

定義

有一系列的算法,將每個算法封裝起來(每個算法可以封裝到不同的類中),各

個算法之間可以替換,策略模式讓算法獨立于使用它的客戶而獨立變化。

示例

<pre><code>

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第11頁 共38頁 2020/11/3 下午9:00

public abstract class BaseAdvertManager {

protected abstract void doLoadAdvert();

}

public class FacebookAdvertManager extends BaseAdvertManager {

@Override

protected void doLoadAdvert() {

Log.v(TAG, "加載Facebook廣告");

}

}

public class AdmobAdvertManager extends BaseAdvertManager {

@Override

protected void doLoadAdvert() {

Log.v(TAG, "加載Admob廣告");

}

}

</code></pre>

Android

Android在屬性動畫中使用時間插值器的時候就用到了策略模式。在使用動畫時,

你可以選擇線性插值器LinearInterpolator、加速減速插值器

AccelerateDecelerateInterpolator、減速插值器DecelerateInterpolator以及

自定義的插值器。這些插值器都是實現根據時間流逝的百分比來計算出當前屬性

值改變的百分比。通過根據需要選擇不同的插值器,實現不同的動畫效果。

重構

暫無

狀態模式

定義

狀態模式中,行為是由狀態來決定的,不同狀態下有不同行為。狀態模式和策略

模式的結構幾乎是一模一樣的,主要是他們表達的目的和本質是不同。

示例

<pre><code>

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{

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第12頁 共38頁 2020/11/3 下午9:00

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();

}

}

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第13頁 共38頁 2020/11/3 下午9:00

</code></pre>

Android

Android源碼中很多地方都有用到狀態模式,舉一個例子,就是Android的WIFI管

理模塊。當WIFI開啟時,自動掃描周圍的接入點,然后以列表的形式展示;當

wifi關閉時則清空。這里wifi管理模塊就是根據不同的狀態執行不同的行為。

與策略模式的區別

狀態模式的行為是平行的、不可替換的,策略模式是屬于對象的行為模式,其行

為是彼此獨立可相互替換的。

重構

項目中有需要功能如瘦臉等存在開關,現在是通過配置文件進行判斷,可以通過

狀態模式進行重構,進而在具體處理圖片時可以利用多態的特性直接使用對象進

行處理

責任鏈模式

定義

使多個對象都有機會處理請求,從而避免請求的發送者和接受者直接的耦合關

系,將這些對象連成一條鏈,并沿這條鏈傳遞該請求,直到有對象處理它為止。

示例

<pre><code>

/**

抽象處理者

*/

public abstract class Handler {

/**

持有后繼的責任對象

/

protected Handler successor;

/*

示意處理請求的方法,雖然這個示意方法是沒有傳入參數的

但實際是可以傳入參數的,根據具體需要來選擇是否傳遞參數

/

public abstract void handleRequest();

/*

取值方法

/

public Handler getSuccessor() {

return successor;

}

/*

賦值方法,設置后繼的責任對象

*/

public void setSuccessor(Handler successor) {

this.successor = successor;

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第14頁 共38頁 2020/11/3 下午9:00

}

}

/**

具體處理者

/

public class ConcreteHandler extends Handler {

/*

處理方法,調用此方法處理請求

/

@Override

public void handleRequest() {

/*

判斷是否有后繼的責任對象

如果有,就轉發請求給后繼的責任對象

如果沒有,則處理請求

*/

if(getSuccessor() != null)

{

System.out.println("放過請求");

getSuccessor().handleRequest();

}else

{

System.out.println("處理請求");

}

}

}

/**

發起請求的客戶類

*/

public class Client {

public static void main(String[] args) {

//組裝責任鏈

Handler handler1 = new ConcreteHandler();

Handler handler2 = new ConcreteHandler();

handler1.setSuccessor(handler2);

//提交請求

handler1.handleRequest();

}

}

</code></pre>

Android

在Android處理點擊事件時,父View先接收到點擊事件,如果父View不處理則交給

子View,把責任依次往下傳遞;還有Java的異常捕獲機制也是責任鏈模式的一種

體現

重構

暫無

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第15頁 共38頁 2020/11/3 下午9:00

解釋器模式

定義

給定一個語言,定義它的語法,并定義一個解釋器,這個解釋器用于解析語言。

示例

如編寫各種功能模塊的配置文件,然后按照項目定義的配置文件編寫規則在運行

過程中將配置文件加載為配置對象,這個模式在日常項目中應該或多或少都會使

用到,就不貼出代碼了。

Android

這個用到的地方也不少,其一就是Android的四大組件需要在

AndroidManifest.xml中定義,其實AndroidManifest.xml就定義了<Activity>,

<Service>等標簽(語句)的屬性以及其子標簽,規定了具體的使用(語法),通

過PackageManagerService(解釋器)進行解析。

重構

暫無

命令模式

定義

命令模式將每個請求封裝成一個對象,從而讓用戶使用不同的請求把客戶端參數

化;將請求進行排隊或者記錄請求日志,以及支持可撤銷操作。

舉個例子來理解:當我們點擊“關機”命令,系統會執行一系列操作,比如暫停

事件處理、保存系統配置、結束程序進程、調用內核命令關閉計算機等等,這些

命令封裝從不同的對象,然后放入到隊列中一個個去執行,還可以提供撤銷操

作。

示例

<pre><code>

public void method() {

Handler.post(new Runnable() {

@Override

public void run() {

clearCache();

statics();

finish();

}

});

}

</code></pre>

Android

在Android事件機制中,底層邏輯對事件的轉發處理。每次的按鍵事件會被封裝成

NotifyKeyArgs對象,通過InputDispatcher封裝具體的事件操作。還有一個例子

就是我們使用的Runnable,我們可以使用它來封裝自己想做的操作,然后交給

Handler按順序處理,或者在處理前remove取消掉

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第16頁 共38頁 2020/11/3 下午9:00

重構

現在的廣告模塊業務邏輯可以進行這種模式的重構,將廣告拉取、廣告展示等請

求都封裝成一個個的對象,放入到一個管理容器中按照一定規則進行調用,這樣

不僅可以避免在調用了展示廣告接口的時候正在拉取廣告,進而導致展示次數丟

失的情況,也可以在出現網絡錯誤等異常情況時可以對一些步驟進行取消。

觀察者模式

定義

有時被稱作發布/訂閱模式,其定義了一種一對多的依賴關系,讓多個觀察者對象

同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者

對象,使它們能夠自動更新自己。

示例

Java的Observable類和Observer接口就是實現了觀察者模式。一個Observer對象

監視著一個Observable對象的變化,當Observable對象發生變化時,Observer得

到通知,就可以進行相應的工作。

感興趣的可以直接去查看兩個類的源碼,這個模式比較常用,就不在這里再貼一

次了。

Android

ListView的適配器有個notifyDataSetChange()函數,就是通知ListView的每個

Item,數據源發生了變化,各個子Item需要重新刷新一下。

重構

比如現在相機選完濾鏡拍照后進入自拍確認頁,這時候如果用戶更換了濾鏡,那

么回來的時候應該把相機界面的濾鏡替換為最新選擇的濾鏡。目前是在相機界面

的onResume函數里做這個邏輯,但是其實可以把濾鏡抽成一個完整的模塊,然后

在模塊內部自己實現觀察者模式,這樣一方面把濾鏡的邏輯封裝起來,與外界解

耦,也符合Java多用組合的理念

EventBus

EventBus的好處很明顯,可以很方便簡單地實現觀察者模式,但是壞處也很明顯

大量的濫用,將導致邏輯的分散,出現問題后很難定位。

沒辦法實現強類型,在編譯的時候就發現問題,(Otto實現了這個,但性能

有問題)。在實現上通過一個很弱的協議,比如onEvent{XXX}, {XXX}表示

ThreadModel,來實現線程的切換。

代碼可讀性存在問題,IDE無法識別這些協議,對IDE不友好。

所以如果出現了需要使用觀察者模式的情況,在各方面條件允許的情況下,建議

還是在這個模塊中自己實現觀察者模式,如果發現這個功能在其他模塊也需要,

那么就要考慮是不是應該把這系列功能抽成一個更加獨立的模塊從而進行復用,

而不是貪圖方便地直接使用EventBus

備忘錄模式

定義

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第17頁 共38頁 2020/11/3 下午9:00

在不破壞封閉的前提下,捕獲一個對象的內部狀態,并在對象之外保存這個狀

態,這樣,以后就可將對象恢復到原先保存的狀態中。

示例

序列化對象到本地并在必要時將對象反序列化恢復回來

<pre><code>

public PhotoData readPhotoData(String path) {

PhotoData photoData = null;

ObjectInputStream objInput = null;

try {

objInput = new ObjectInputStream(new FileInputStream(path));

photoData = (PhotoData) objInput.readObject();

} catch (IOException | ClassNotFoundException e) {

e.printStackTrace();

} finally {

try {

if (objInput != null) {

objInput.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

return photoData;

}

public void writePhotoData(PhotoData data, String path) {

ObjectOutputStream objOutput = null;

try {

objOutput = new ObjectOutputStream(new FileOutputStream(path));

objOutput.writeObject(data);

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (objOutput != null) {

objOutput.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

</code></pre>

Android

Activity的onSaveInstanceState和onRestoreInstanceState就是用到了備忘錄模

式,分別用于保存和恢復。

重構

暫無

迭代器模式

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第18頁 共38頁 2020/11/3 下午9:00

定義

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

表示。

迭代器模式是與集合共生共死的,一般來說,我們只要實現一個集合,就需要同

時提供這個集合的迭代器,就像java中的Collection,List、Set、Map等,這些

集合都有自己的迭代器。假如我們要實現一個這樣的新的容器,當然也需要引入

迭代器模式,給我們的容器實現一個迭代器。

示例

Java的Iterator就是一個抽象迭代器,通過實現這個接口各個集合可以提供自己

定制的具體迭代器,感興趣的可以直接查看源碼

雖然我們使用集合的場景非常多,但是實際使用到迭代器的卻比較少,對于比較

簡單的遍歷(像數組或者有序列表),使用迭代器方式遍歷較為繁瑣,比如

ArrayList,我們更傾向于使用for循環來遍歷,但是針對hash表之類的集合來

說,引入迭代器就反而簡單多了。同時我們也可以通過自定義迭代器來對有序列

表提供正序遍歷或者倒序遍歷,用戶只需要得到迭代器就可以遍歷了,而不需要

關心具體遍歷算法。

Android

Android源碼中,最典型的就是Cursor用到了迭代器模式,當我們使用

SQLiteDatabase的query方法時,返回的就是Cursor對象,之后再通過Cursor去遍

歷數據

重構

暫無

模板方法模式

定義

定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類可以不改變

一個算法的結構即可重定義該算法的某些特定的步驟。

示例

<pre><code>

public abstract class BaseAdvertManager {

public void loadAdvert(Context context, int type) {

if (NetUtils.checkNetConnection(context) != NetUtils.OK) {

return;

}

mStateArray.put(type, AdvertConstant.AD_STATE_LOADING);

doLoadAdvert(context, type);

}

protected abstract void doLoadAdvert(Context context, int type);

}

</code></pre>

廣告管理器抽象類定義了加載廣告的通用模板,但是又把模板中具體加載廣告的

邏輯聲明為抽象方法為各個子類自我實現

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第19頁 共38頁 2020/11/3 下午9:00

Android

啟動一個Activity過程非常復雜,有很多地方需要開發者定制,也就是說,整體

算法框架是相同的,但是將一些步驟延遲到子類中,比如Activity的onCreate、

onStart等等。這樣子類不用改變整體啟動Activity過程即可重定義某些具體的操

作了。

重構

大部分代碼相同部分代碼不同的方法都可以嘗試使用模板方法重構,要么是在同

一個類里面進行方法的重構,要么通過塑造模板函數的重構手段對子類與父類進

行重構

項目中自拍確認頁保存照片并分享與保存照片并后退目前是兩個獨立的方法,但

是方法內部大多數代碼都是一樣的,就需要用該模式進行重構,由于代碼量大而

且該重構手段比較簡單,就不貼出代碼

訪問者模式

定義

封裝一些作用于某種數據結構中各元素的操作,它可以在不改變這個數據結構的

前提下定義作用于這些元素的新的操作。

假如一個對象中存在著一些與本對象不相干(或者關系較弱)的操作,為了避免

這些操作污染這個對象,則可以使用訪問者模式來把這些操作封裝到訪問者中

去。

假如一組對象中,存在著相似的操作,為了避免出現大量重復的代碼,也可以將

這些重復的操作封裝到訪問者中去。

訪問者模式的目的是封裝一些施加于某種數據結構元素之上的操作,一旦這些操

作需要修改的話,接受這個操作的數據結構則可以保持不變。

訪問者模式是23種設計模式中最復雜最難理解的一個,但他的使用率并不高,大

部分情況下,我們不需要使用訪問者模式,少數特定的場景才需要。

大多數情況下,你并需要使用訪問者模式,但是當你一旦需要使用它時,那你就

是真的需要它了。——GOF《設計模式:可復用面向對象軟件的基礎 》

示例

<pre><code>

interface Service {

public void accept(Visitor visitor);

}

class Draw implements Service {

public void accept(Visitor visitor) {

visitor.process(this);

}

}

class Fund implements Service {

public void accept(Visitor visitor) {

visitor.process(this);

}

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第20頁 共38頁 2020/11/3 下午9:00

}

class Saving implements Service {

public void accept(Visitor visitor) {

visitor.process(this);

}

}

class Visitor {

public void process(Service service) {

System.out.println("基?業務");

}

public void process(Saving service) {

System.out.println("??");

}

public void process(Draw service) {

System.out.println("提?");

}

public void process(Fund service) {

System.out.println("基?");

}

}

public class Client {

public static void main(String[] args){

Service saving = new Saving();

Service fund = new Fund();

Service draw = new Draw();

Visitor visitor = new Visitor();

saving.accept(visitor);

fund.accept(visitor);

draw.accept(visitor);

}

}

</code></pre>

采用Visitor的好處如上面所示,當需要改變其中一項業務的處理時,不需要每個

地方都進行修改,而只需要改動Visitor類中相應的處理函數就可以了。也就是說

它適合于業務處理時常發生變動的情況。

當然,Visitor也有它自身的限制。它不適合于業務數量的經常變化,因為一旦新

增或刪除一些Service時,需要對Visitor進行相應的增刪。也就是說具體Service

與Visitor是耦合的。

Android

Android中運用訪問者模式,其實主要是在編譯期注解中,編譯期注解核心原理依

賴APT(Annotation Processing Tools),著名的開源庫比如ButterKnife、

Dagger、Retrofit都是基于APT。

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第21頁 共38頁 2020/11/3 下午9:00

重構

如果是一些經常需要變動邏輯的業務則非常適合使用訪問者模式,如果是需要頻

繁增加新的業務的,則不適合,所以Android的UI展示部分其實理論上來說是適合

使用訪問者模式的,因為UI常常一個版本一個變化,如果當UI的變化不只是局限

在XML中修修改改的話,而是已經體現在了代碼中,那么可以考慮是否可以使用訪

問者模式進行修改。

目前實際項目中暫無這種情況,廣告模塊后續的UI渲染由于是根據不同的廣告平

臺所下發的廣告對象來進行對應的渲染,目前每個廣告平臺渲染視圖的接口所需

參數不太一樣,但是可以考慮進行一下抽離封裝,做成一個簡單的Visitor試試

看。

中介者模式

定義

中介者模式包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯調

用,從而使他們可以輕松耦合。當某些對象之間的作用發生改變時,不會立即影

響其他的一些對象之間的作用保證這些作用可以彼此獨立的變化,中介者模式將

多對多的相互作用轉為一對多的相互作用。

其實,中介者對象是將系統從網狀結構轉為以調停者為中心的星型結構。

舉個簡單的例子,一臺電腦包括:CPU、內存、顯卡、IO設備。其實,要啟動一臺

計算機,有了CPU和內存就夠了。當然,如果你需要連接顯示器顯示畫面,那就得

加顯卡,如果你需要存儲數據,那就要IO設備,但是這并不是最重要的,它們只

是分割開來的普通零件而已,我們需要一樣東西把這些零件整合起來,變成一個

完整體,這個東西就是主板。主板就是起到中介者的作用,任何兩個模塊之間的

通信都會經過主板協調。

示例

<pre><code>

public abstract class Person {

protected String name;

protected Mediator mediator;

Person(String name,Mediator mediator){

this.name = name;

this.mediator = mediator;

}

}

public class HouseOwner extends Person{

HouseOwner(String name, Mediator mediator) {

super(name, mediator);

}

public void constact(String message){

mediator.constact(message, this);

}

public void getMessage(String message){

System.out.println("?主:" + name +",獲得???" + message);

}

}

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第22頁 共38頁 2020/11/3 下午9:00

public class Tenant extends Person{

Tenant(String name, Mediator mediator) {

super(name, mediator);

}

public void constact(String message){

mediator.constact(message, this);

}

public void getMessage(String message){

System.out.println("租?者:" + name +",獲得???" + message);

}

}

public abstract class Mediator {

//申明一個聯絡方法

public abstract void constact(String message,Person person);

}

public class MediatorStructure extends Mediator{

//首先中介結構必須知道所有房主和租房者的信息

private HouseOwner houseOwner;

private Tenant tenant;

public HouseOwner getHouseOwner() {

return houseOwner;

}

public void setHouseOwner(HouseOwner houseOwner) {

this.houseOwner = houseOwner;

}

public Tenant getTenant() {

return tenant;

}

public void setTenant(Tenant tenant) {

this.tenant = tenant;

}

public void constact(String message, Person person) {

if(person == houseOwner){

tenant.getMessage(message);

}

else{

houseOwner.getMessage(message);

}

}

}

public class Client {

public static void main(String[] args) {

//一個房主、一個租房者、一個中介機構

MediatorStructure mediator = new MediatorStructure();

HouseOwner houseOwner = new HouseOwner("張三", mediator);

Tenant tenant = new Tenant("??", mediator);

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第23頁 共38頁 2020/11/3 下午9:00

mediator.setHouseOwner(houseOwner);

mediator.setTenant(tenant);

tenant.constact("?說你那?有三?的?主?租.....");

houseOwner.constact("是的!請問你需?租嗎?");

}

}

</code></pre>

<blockquote>

房主:張三,獲得信息:聽說你那里有三室的房主出租…..

租房者:李四,獲得信息:是的!請問你需要租嗎?

</blockquote>

Android

在Binder機制中,就用到了中介者模式。我們知道系統啟動時,各種系統服務會

向ServiceManager提交注冊,即ServiceManager持有各種系統服務的引用 ,當我

們需要獲取系統的Service時,比如ActivityManager、WindowManager等(它們都

是Binder),首先是向ServiceManager查詢指定標示符對應的Binder,再由

ServiceManager返回Binder的引用。并且客戶端和服務端之間的通信是通過

Binder驅動來實現,這里的ServiceManager和Binder驅動就是中介者。

重構

從年初開始就有在項目里面做MVP的重構,MVP架構里面P層其實就是一個中介者,

負責協調V和M

外觀模式/門面模式

定義

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

舉個例子,我們在啟動計算機時,只需按一下開關鍵,無需關系里面的磁盤、內

存、cpu、電源等等這些如何工作,我們只關心他們幫我啟動好了就行。實際上,

由于里面的線路太復雜,我們也沒辦法去具體了解內部電路如何工作。主機提供

唯一一個接口“開關鍵”給用戶就好。

示例

<pre><code>

/**

cpu子系統類

*/

public class CPU

{

public static final Logger LOGGER = Logger.getLogger(CPU.class);

public void start()

{

LOGGER.info("cpu is start...");

}

public void shutDown()

{

LOGGER.info("CPU is shutDown...");

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第24頁 共38頁 2020/11/3 下午9:00

}

}

/**

Disk子系統類

*/

public class Disk

{

public static final Logger LOGGER = Logger.getLogger(Disk.class);

public void start()

{

LOGGER.info("Disk is start...");

}

public void shutDown()

{

LOGGER.info("Disk is shutDown...");

}

}

/**

Memory子系統類

*/

public class Memory

{

public static final Logger LOGGER =

Logger.getLogger(Memory.class);

public void start()

{

LOGGER.info("Memory is start...");

}

public void shutDown()

{

LOGGER.info("Memory is shutDown...");

}

}

/**

門面類(核心)

*/

public class Computer

{

public static final Logger LOGGER =

Logger.getLogger(Computer.class);

private CPU cpu;

private Memory memory;

private Disk disk;

public Computer()

{

cpu = new CPU();

memory = new Memory();

disk = new Disk();

}

public void start()

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第25頁 共38頁 2020/11/3 下午9:00

{

LOGGER.info("Computer start begin");

cpu.start();

disk.start();

memory.start();

LOGGER.info("Computer start end");

}

public void shutDown()

{

LOGGER.info("Computer shutDown begin");

cpu.shutDown();

disk.shutDown();

memory.shutDown();

LOGGER.info("Computer shutDown end...");

}

}

/**

客戶端類

*/

public class Cilent {

public static final Logger LOGGER =

Logger.getLogger(Cilent.class);

public static void main(String[] args)

{

Computer computer = new Computer();

computer.start();

LOGGER.info("=================");

computer.shutDown();

}

}

</code></pre>

從上面的實例來看,有了這個Facade類,也就是Computer類,用戶就不用親自去

調用子系統中的Disk,Memory、CPU類了,不需要知道系統內部的實現細節,甚至

都不用知道系統內部的構成。客戶端只需要跟Facade交互就可以了。

Android

那么Android哪里使用到了外觀模式呢?依然回到Context,Android內部有很多復

雜的功能比如startActivty、sendBroadcast、bindService等等,這些功能內部

的實現非常復雜,如果你看了源碼你就能感受得到,但是我們無需關心它內部實

現了什么,我們只關心它幫我們啟動Activity,幫我們發送了一條廣播,綁定了

Activity等等就夠了。

重構

暫無

代理模式

定義

給某一個對象提供一個代理,并由代理對象控制對原對象的引用。

代理模式有幾種,虛擬代理,計數代理,遠程代理,動態代理。主要分為兩類,

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第26頁 共38頁 2020/11/3 下午9:00

靜態代理和動態代理。

靜態代理

定義

靜態代理比較簡單,是由程序員編寫的代理類,并在程序運行前就編譯好的,而

不是由程序動態產生代理類,這就是所謂的靜態。可以通過聚合和繼承兩種方式

實現,繼承方式不夠靈活,所以只介紹聚合的方式

示例

<pre><code>

nterface Subject {

void request();

}

class RealSubject implements Subject {

public void request(){

System.out.println("RealSubject");

}

}

class Proxy implements Subject {

private Subject subject;

public Proxy(Subject subject){

this.subject = subject;

}

public void request(){

System.out.println("begin");

subject.request();

System.out.println("end");

}

}

public class ProxyTest {

public static void main(String args[]) {

RealSubject subject = new RealSubject();

Proxy p = new Proxy(subject);

p.request();

}

}

</code></pre>

重構

目前項目中有需要加載圖片的業務需求,加載圖片的框架可以有ImageLoader、

Glide等等,可以通過適配器模式讓這些第三方或者自己內部的工具類整合在一

起,然后通過靜態代理的方式提供給外部使用圖片處理的相關功能。

動態代理

定義

動態代理中,代理類并不是在Java代碼中實現,而是在運行時期生成,相比靜態

代理,動態代理可以很方便的對委托類的方法進行統一處理,如添加方法調用次

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第27頁 共38頁 2020/11/3 下午9:00

數、添加日志功能等等,動態代理分為jdk動態代理和cglib動態代理,下面通過

一個例子看看如何實現jdk動態代理。

JDK動態代理示例

<pre><code>

//定義業務邏輯

public interface Service {

//目標方法

public abstract void add();

}

public class UserServiceImpl implements Service {

public void add() {

System.out.println("This is add service");

}

}

//利用java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接

口定義代理類的實現。

class MyInvocatioHandler implements InvocationHandler {

private Object target;

public MyInvocatioHandler(Object target) {

this.target = target;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("-----before-----");

Object result = method.invoke(target, args);

System.out.println("-----end-----");

return result;

}

public Object getProxy() {

ClassLoader loader = Thread.currentThread().getContextClassLoader();

Class<?>[] interfaces = target.getClass().getInterfaces();

return Proxy.newProxyInstance(loader, interfaces, this);

}

}

//使用動態代理

public class ProxyTest {

public static void main(String[] args) {

Service service = new UserServiceImpl();

MyInvocatioHandler handler = new MyInvocatioHandler(service);

Service serviceProxy = (Service)handler.getProxy();

serviceProxy.add();

}

}

執行結果:

-----before-----

This is add service

-----end-----

</code></pre>

cglib動態代理示例

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第28頁 共38頁 2020/11/3 下午9:00

前面分析到,因為Java只允許單繼承,而JDK生成的代理類本身就繼承了Proxy

類,因此,使用JDK實現的動態代理不能完成繼承式的動態代理,但是我們可以使

用cglib來實現繼承式的動態代理。

大名鼎鼎的spring中就含有cglib動態代理,在此也以Spring中自帶的cglib完成

動態代理的實現:

<pre><code>

//1.具體主題

public class Train{

public void move(){

System.out.println("火車行駛中…");

}

}

//2.生成代理

public class CGLibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();

public Object getProxy(Class<?> clazz){

enhancer.setSuperclass(clazz);

enhancer.setCallback(this);

return enhancer.create();

}

/**

* 攔截所有目標類方法的調用

* 參數:

* obj目標實例對象

*method 目標方法的反射對象

* args方法的參數

* proxy代理類的實例

*/

public Object intercept(Object obj, Method method, Object[] args,

MethodProxy proxy) throws Throwable {

//代理類調用父類的方法

System.out.println("日志開始");

proxy.invokeSuper(obj, args);

System.out.println("日志結束");

return null;

}

}

//3.測試

public class Test {

public static void main(String[] args) {

CGLibProxy proxy = new CGLibProxy();

Train t = (Train) proxy.getProxy(Train.class);

t.move();

}

}

</code></pre>

重構

在項目中很多地方需要統計,這部分功能放在哪一層都感覺不合適,可以通過動

態代理將簡單的統計抽離出來,動態代理其實就是AOP編程思想的一種具體實現

小結

動態代理與靜態代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調

用處理器一個集中的方法中處理。在接口方法數量比較多的時候,我們可以進行

靈活處理,而不需要像靜態代理那樣對每一個方法或方法組合進行處理。Proxy

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第29頁 共38頁 2020/11/3 下午9:00

很美很強大,但是僅支持 interface 代理。Java 的單繼承機制注定了這些動態

代理類們無法實現對 class 的動態代理。好在有cglib為Proxy提供了彌補。

class與interface的區別本來就模糊,在java8中更是增加了一些新特性,使得

interface越來越接近class,當有一日,java突破了單繼承的限制,動態代理將

會更加強大。

Android

AIDL會根據當前的線程判斷是否要跨進程訪問,如果不需要跨進程就直接返回實

例,如果需要跨進程則返回一個代理。而在跨進程通信時,需要把參數寫入到

Parcelable對象,然后再執行transact函數,AIDL通過生成一個代理類,這個代

理類會自動幫我們寫好這些操作。

而要實現Android的插件化開發,動態代理更是必不可少的。

中介者、代理、外觀模式三者的區別

中介者模式:A,B之間的對話通過C來傳達。A,B可以互相不認識(減少了A

和B對象間的耦合)

代理模式:A要送B禮物,A,B互相不認識,那么A可以找C來幫它實現送禮物

的愿望(封裝了A對象)

外觀模式:A和B都要實現送花,送巧克力的方法,那么我可以通過一個抽象

類C實現送花送巧克力的方法(A和B都繼承C)。(封裝了A,B子類)

代理模式和外觀者模式這兩種模式主要不同就是代理模式針對的是單個對象,而

外觀模式針對的是所有子類。

裝飾模式

定義

動態的給一個對象添加額外的職責,就增加功能來說,裝飾模式比子類繼承的方

式更靈活。

我們通常可以使用繼承來實現功能的拓展,如果這些需要拓展的功能的種類很繁

多,那么勢必生成很多子類,增加系統的復雜性,同時,使用繼承實現功能拓

展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的。

使用Decorator的理由是:這些功能需要由用戶動態決定加入的方式和時機。

Decorator提供了"即插即用"的方法,在運行期間決定何時增加何種功能。

示例

<pre><code>

public abstract class Component{

public abstract void operate();

}

public class ConcreteComponent extends Component{

public void operate(){

//具體的實現

}

}

public class Decorator extends Component {

private Component component;

public Decorator(Component component){

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第30頁 共38頁 2020/11/3 下午9:00

this.component = component;

}

public void operate(){

operateA();

component.operate();

operateB();

}

public void operateA(){

}

public void operateB(){

}

}

public static void main(String[] args) {

// 使用普通功能類

Component concreteComponent = new ConcreteComponent();

Component decorator = new Decorator(concreteComponent);

decorator.operate();

}

}

</code></pre>

如果你細心,會發現,上面調用類似我們讀取文件時的調用:

<pre><code>

FileReader fr = new FileReader(filename);

BufferedReader br = new BufferedReader(fr);

</code></pre>

實際上Java 的I/O API就是使用Decorator實現的,I/O變種很多,如果都采取繼

承方法,將會產生很多子類,顯然相當繁瑣。

Android

那么在Android哪里出現了裝飾模式呢?我們平時經常用到Context類,但是其實

Context類只是個抽象類,具體實現是ContextImpl,那么誰是ContextImpl的裝飾

類呢?我們知道Activity是個Context,但是Activity 并不是繼承于Context,而是

繼承于ContextThremeWrapper.而ContextThremeWrapper繼承于

ContextWrapper,ContextWrapper繼承Context.說了這么多,跟裝飾模式有啥關

系?主要是引入ContextWrapper這個類。ContextWrapper內部有個Context引用

mContext,并且ContextWrapper中對Context的每個方法都有實現,在實現中調用

的就是mContext相同的方法。

重構

子類過多的時候可以考慮使用

裝飾模式與代理模式的區別

裝飾模式:以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方

案;

代理模式:給一個對象提供一個代理對象,并有代理對象來控制對原有對象的引

用;

裝飾模式應該為所裝飾的對象增強功能;代理模式對代理的對象施加控制,并不

提供對象本身的增強功能

你在一個地方寫裝飾,大家就知道這是在增加功能,你寫代理,大家就知道是在

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第31頁 共38頁 2020/11/3 下午9:00

限制,

組合模式

定義

將對象組成成樹形結構,以表示“部分-整體”的層次結構,使得用戶對單個對象

和組合對象的使用具有一致性。

示例

<pre><code>

public class Employee {

private String name;

private String dept;

private int salary;

private List<Employee> subordinates;

//構造函數

public Employee(String name,String dept, int sal) {

this.name = name;

this.dept = dept;

this.salary = sal;

subordinates = new ArrayList<Employee>();

}

public void add(Employee e) {

subordinates.add(e);

}

public void remove(Employee e) {

subordinates.remove(e);

}

public List<Employee> getSubordinates(){

return subordinates;

}

public String toString(){

return ("Employee :[ Name : "+ name

+", dept : "+ dept + ", salary :"

+ salary+" ]");

}

}

public class CompositePatternDemo {

public static void main(String[] args) {

Employee CEO = new Employee("John","CEO", 30000);

Employee headSales = new Employee("Robert","Head Sales", 20000);

Employee headMarketing = new Employee("Michel","Head Marketing", 20000);

Employee clerk1 = new Employee("Laura","Marketing", 10000);

Employee clerk2 = new Employee("Bob","Marketing", 10000);

Employee salesExecutive1 = new Employee("Richard","Sales", 10000);

Employee salesExecutive2 = new Employee("Rob","Sales", 10000);

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第32頁 共38頁 2020/11/3 下午9:00

CEO.add(headSales);

CEO.add(headMarketing);

headSales.add(salesExecutive1);

headSales.add(salesExecutive2);

headMarketing.add(clerk1);

headMarketing.add(clerk2);

System.out.println(CEO);

for (Employee headEmployee : CEO.getSubordinates()) {

System.out.println(headEmployee);

for (Employee employee : headEmployee.getSubordinates()) {

System.out.println(employee);

}

}

}

}

</code></pre>

Android

Android中View的結構是樹形結構,每個ViewGroup包含一系列的View,而

ViewGroup本身又是View。這是Android中非常典型的組合模式。

重構

暫無

適配器模式

定義

把一個類的接口變換成客戶端所期待的另一個接口,從而使原本因接口不匹配而

無法在一起工作的兩個類能夠在一起工作。

示例

<pre><code>

// 目標接口,或稱為標準接口,即客戶所期待的接口

interface Target {

public void request();

}

// 具體目標類,只提供普通功能

class ConcreteTarget implements Target {

public void request() {

System.out.println("普通類 具有 普通功能...");

}

}

// 已存在的、具有特殊功能、但不符合我們既有的標準接口的類,也是需要被適

配的類

class Adaptee {

public void specificRequest() {

System.out.println("被適配類具有特殊功能...");

}

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第33頁 共38頁 2020/11/3 下午9:00

}

// 適配器類,繼承了被適配類,同時實現標準接口

class Adapter extends Adaptee implements Target{

public void request() {

super.specificRequest();

}

}

// 測試類public class Client {

public static void main(String[] args) {

// 使用普通功能類

Target concreteTarget = new ConcreteTarget();

concreteTarget.request();

Target adapter = new Adapter();

adapter.request();

}

}

</code></pre>

Android

比較典型的有ListView和RecyclerView。為什么ListView需要使用適配器

呢?ListView用于顯示列表數據,但列表數據形式多種多樣,為了處理和顯示不

同的數據,我們需要對應的適配器作為橋梁。這樣ListView就可以只關心它的每

個ItemView,而不用關心這個ItemView具體顯示的是什么。而我們的數據源存放

的是要顯示的內容,它保存了每一個ItemView要顯示的內容。ListView和數據源

之間沒有任何關系,這時候,需要通過適配器,適配器提供getView方法給

ListView使用,每次ListView只需提供位置信息給getView函數,然后getView函

數根據位置信息向數據源獲取對應的數據,根據數據返回不同的View。

重構

當想使用一個既有類的接口,但是這個既有類與目前的代碼結構不相兼容的時候

可以考慮使用適配器模式。

享元模式

定義

享元模式的英文是Flyweight,在拳擊比賽中指最輕量級,即“蠅量級”或“雨量

級”,這里選擇使用“享元模式”的意譯,是因為這樣更能反映模式的用意。享

元模式是對象的結構模式。享元模式以共享的方式高效地支持大量的細粒度對

象。

享元模式還分單純享元模式和復合享元模式

示例

在JAVA語言中,String類型就是使用了享元模式。String對象是final類型,對象

一旦創建就不可改變。在JAVA中字符串常量都是存在常量池中的,JAVA會確保一

個字符串常量在常量池中只有一個拷貝。String a="abc",其中"abc"就是一個字

符串常量。

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第34頁 共38頁 2020/11/3 下午9:00

Android

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

重用對象。

在Android哪里用到了享元模式呢?線程通信中的Message,每次我們獲取Message

時調用Message.obtain()其實就是從消息池中取出可重復使用的消息,避免產生

大量的Message對象。

重構

暫無

橋接模式

定義

將抽象部分與實現部分分離,使他們獨立地進行變化。

其實就是,一個類存在兩個維度的變化,且這兩個維度都需要進行擴展。

拿汽車在路上行駛的來說。既有小汽車又有公共汽車,它們都不但能在市區中的

公路上行駛,也能在高速公路上行駛。這你會發現,對于交通工具(汽車)有不

同的類型,它們所行駛的環境(路)也有不同類型,在軟件系統中就要適應兩個

方面(不同車型,不同道路)的變化,怎樣實現才能應對這種變化呢?

在軟件系統中,某些類型由于自身的邏輯,它具有兩個或多個維度的變化,那么

如何應對這種“多維度的變化”?如何利用面向對象的技術來使得該類型能夠輕

松的沿著多個方向進行變化,而又不引入額外的復雜度?這就要使用Bridge模

式。Bridge模式是一個非常有用的模式,也非常復雜,它很好的符合了開放-封閉

原則和優先使用對象,而不是繼承這兩個面向對象原則。

示例

首先看一下不應用橋接模式時的上述汽車例子代碼:

<pre><code>

//基類 路

class Road {

void run() {

System.out.println("路");

}

}

//市區街道

class Street extends Road {

void run() {

System.out.println("市區街道");

}

}

//高速公路

class SpeedWay extends Road {

void run() {

System.out.println("高速公路");

}

}

//小汽車在市區街道行駛

class CarOnStreet extends Street {

void run() {

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第35頁 共38頁 2020/11/3 下午9:00

System.out.println("小汽車在市區街道行駛");

}

}

//小汽車在高速公路行駛

class CarOnSpeedWay extends SpeedWay {

void run() {

System.out.println("小汽車在高速公路行駛");

}

}

//公交車在市區街道行駛

class BusOnStreet extends Street {

void run() {

System.out.println("公交車在市區街道行駛");

}

}

//公交車在高速公路行駛

class BusOnSpeedWay extends SpeedWay {

void run() {

System.out.println("公交車在高速公路行駛");

}

}

//測試

public static void main(String[] args) {

CarOnSpeedWay carOnSpeedWay = new CarOnSpeedWay();

carOnSpeedWay.run();

BusOnStreet busOnStreet = new BusOnStreet();

busOnStreet.run();

}

</code></pre>

但是我們說這樣的設計是脆弱的,仔細分析就可以發現,它還是存在很多問題,

首先它在遵循開放-封閉原則的同時,違背了類的單一職責原則,即一個類只有一

個引起它變化的原因,而這里引起變化的原因卻有兩個,即路類型的變化和汽車

類型的變化;其次是重復代碼會很多,不同的汽車在不同的路上行駛也會有一部

分的代碼是相同的;

再次是類的結構過于復雜,繼承關系太多,難于維護,最后最致命的一點是擴展

性太差。如果變化沿著汽車的類型和不同的道路兩個方向變化,我們會看到這個

類的結構會迅速的變龐大。

接下來我們再看一下應用了橋接模式之后的代碼:

<pre><code>

abstract class AbstractRoad{

AbstractCar aCar;

void run(){};

}

abstract class AbstractCar{

void run(){};

}

class Street extends AbstractRoad{

@Override

void run() {

// TODO Auto-generated method stub

super.run();

aCar.run();

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第36頁 共38頁 2020/11/3 下午9:00

System.out.println("在市區街道行駛");

}

}

class SpeedWay extends AbstractRoad{

@Override

void run() {

// TODO Auto-generated method stub

super.run();

aCar.run();

System.out.println("在高速公路行駛");

}

}

class Car extends AbstractCar{

@Override

void run() {

// TODO Auto-generated method stub

super.run();

System.out.print("小汽車");

}

}

class Bus extends AbstractCar{

@Override

void run() {

// TODO Auto-generated method stub

super.run();

System.out.print("公交車");

}

}

public static void main(String[] args){

AbstractRoad speedWay = new SpeedWay();

speedWay.aCar = new Car();

speedWay.run();

AbstractRoad street = new Street();

street.aCar = new Bus();

street.run();

}

</code></pre>

可以看到,通過對象組合的方式,Bridge 模式把兩個角色之間的繼承關系改為了

耦合的關系,從而使這兩者可以從容自若的各自獨立的變化,這也是Bridge模式

的本意。

這樣增加了客戶程序與路與汽車的耦合。其實這樣的擔心是沒有必要的,因為這

種耦合性是由于對象的創建所帶來的,完全可以用創建型模式去解決。在應用時

結合創建型設計模式來處理具體的問題。

Android

在Android中橋接模式用的很多,舉個例子,對于一個View來說,它有兩個維度的

變化,一個是它的描述比如Button、TextView等等他們是View的描述維度上的變

化,另一個維度就是將View真正繪制到屏幕上,這跟Display、HardwareLayer和

Canvas有關。這兩個維度可以看成是橋接模式的應用。

重構

暫無

談談23種設計模式在Android源碼及項目中的應用 - 簡書 http://www.lxweimin.com/p/b2d62447c9ea

第37頁 共38頁 2020/11/3 下午9:00

本文是在看完《重構:改善既有代碼之道》,開始看四人幫的《設計模式:可復

用面向對象軟件的基礎》之前,結合多篇博文、實際項目與個人理解寫下的。

推薦一定要在有所積累甚至遇到一些你已經沒必要用以往經驗解決的問題(這樣

的解決是指最佳方案,而不是只求解決就好)的時候去看這兩個思想,共鳴真的

很重要。

而對于這種體系龐雜的技術知識的學習,我一向不建議只看幾篇博文就了事,這

樣大多只會留于表面,除了拒絕紙上談兵多實踐之外,很多最重要的內容往往在

書里才能說得詳盡,就像各個模式之間怎么協調,各個模式之間有什么區別,多

種多樣的使用場景,在一個模式就能寫一篇論文的情況下,幾篇博文怎么可能學

得盡。

最早看設計模式是在兩年半前,那時候還沒畢業,看的是《大話設計模式》,那

時候沒有多少積累,對架構最說得上的就一個MVC,所以即使看的是以通俗著稱的

《大話》還是云里霧里,看沒兩章就放棄了。

但是在代碼量不斷的積累和對問題不斷的發現之后,再重新去看《重構》與《設

計模式》,這時候的感覺是完全不一樣的,特別在我之前做了一段時間項目的重

構之后,不夸張地說,在看《重構》這本書時,每一章都有那種讓我相見恨晚的

感覺。

技術方面的成長是多方面的,一方面在于經驗的積累,但是經驗的積累受限于很

多現實因素,另一方面的成長則是思想上的進化,當然這也可以通過實際做項目

來提升,但是同樣的,通過項目來學習總有一天會到瓶頸,這時候就代表你該去

收獲那些巨人們留下的思想了。

之前有在一些地方看過重構無用、設計模式無用之類的理論,其實并不是重構與

設計模式無用,而是自己還沒有到能覺得它們有用的境界,設計模式絕不是偶爾

掛在嘴邊聊聊兩句定義就好的談資,作為java程序員必讀的三本書之一的《設計

模式》,能夠這么長時間傳承下來自有其底蘊所在。

最后吐槽一下,《設計模式》這本書真的是神書,可是真的也晦澀難懂,很大一

部分原因在于四人幫這四位大神對設計模式的深度理解而造成寫書時極其簡練抽

象的表述,所以在堅持完前面兩章后再次宣告放棄,轉身投向《Head First:設

計模式》的懷抱,前者200多頁,后者700多頁,但是哪本好讀些大家都懂。

不過重構與設計模式本身就都不是能一蹴即就的,這兩者都是需要不斷地應用、

重新認識、再應用,這樣一直重復之后才能完全融會貫通,所以也不必糾結于今

天看完就要全部掌握,每隔一段時間再看一下,遇到問題的時候回過頭來向其尋

找解決方案,這才是學習這兩大思想的正途。

把《重構》《設計模式》這類更偏向于思想層面的知識放到年初就學也是為了之

后進一步學習《Effective Java》、《Java編程思想》與Android源碼做鋪墊,閱

讀源碼最佳的方法不是從第一行開始往下讀,要么是通過問題出發,以點及面;

要么就是你先試圖從編碼者的思想層面出發,有時候當你發現了這個模塊的設計

思路與模式之后,很多東西都是一點就通的,你甚至知識看到一個模塊的類結構

就知道整個模塊的大體內容,然后就像是你自己寫的這些代碼一樣,找任何東西

都輕而易舉。就像如果你不懂泛型,即使寫過四五個大項目,看到一堆用上泛型

的第三方庫的源碼一樣懵逼。

參考

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

Android 源碼中的工廠方法模式

Android設計模式(十二)--抽象工廠模式

Java設計模式二:抽象工廠模式(Abstract Factory)/工廠方法(Factory Method)

Android源碼中的命令模式

Java設計模式系列之迭代器模式

《JAVA與模式》之訪問者模式

設計模式讀書筆記—–中介者模式

java設計模式之外觀模式(門面模式)

組合模式

JAVA設計模式初探之適配器模式

Java設計模式(7)裝飾模式(Decorator模式)

《JAVA與模式》之享元模式

JAVA設計模式初探之橋接模式

說說Java代理模式

Java 代理模式和裝飾者模式的區別

Java設計模式——代理模式實現及原理

作者:格子林ll

鏈接:http://www.lxweimin.com/p/b2d62447c9ea

來源:簡書

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。