從Android再來認識23種設計模式

概況來看本文章的內容

創建型:5個

單例模式
Builder
原型模式
工廠方法
抽象工廠

行為型: 11個

策略模式
狀態模式

觀察者模式
中介者模式
訪問者模式
迭代器模式
模板方法

備忘錄模式
命令模式
解釋器模式
職責鏈模式

結構型:7個

組合模式
代理模式
裝飾模式
外觀模式

享元模式
橋接模式
適配器模式

關于面向對象

面向對象的六大原則

談到設計模式,不得不說說面向對象的六大原則

1. 單一原則

單一原則通俗的講就是一個類只表達一個概念、一個方法只做一件事情。將一組相關性很高的函數、數據封裝到一個類中。換句話說,一個類應該有職責單一。

2. 開閉原則

開閉原則就是一個類對于擴展是開發的,但是對于修改是封閉的。這也是六大原則中最難的,通常開閉都是短暫的美好,但在業務升級與拓展的狀態下,原理的開閉是無法滿足。即使是這樣,也要盡可能的擴展而不是修改。

3. 里氏替換原則

所有引用基類的地方必須能透明地使用其子類對象。看著定義很是抽象,但是通俗的理解就是由子類實例化的父類引用,在使用這個引用時,感覺就像是使用了父類一樣。一個簡單的例子:

public class T{
  private class A{...}
  class B extends A{...}
  public static void main(String[] args){
    A a = new B();
    // 類型是A,但實際是B類型實例化的
    a.method();
  }
}

4. 依賴倒置原則

依賴倒置主要是為了解耦。具體來說:

  1. 高層模塊不應該依賴底層模塊,二者都應該依賴其抽象
  2. 抽象不應該依賴細節
  3. 細節應該依賴抽象

5. 接口隔離原則

類之間的依賴關系應該建立在最小的接口上其原則是將繁雜的功能接口拆分成更加單一職責的接口,防止接口內部的耦合。

6. 迪米特原則

一個對象應該對其他的對象有最少的了解 保留關鍵的共有方法,其他次要的都是私有方法,不應該被外部了解。

Android來表達面向對象的六大原則

1. 單例模式

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

單例模式的意義是顯而易見的,能極大的節省資源,提高代碼運行效率。缺點也是很明確的,就是不能發生狀態的變化,以提供統一的功能。

單例模式有懶漢式與餓漢式,實現方式有靜態成員變量法、枚舉法、容器法等。單例需要注意的就是懶漢模式下的多線程安全問題。這里給出懶漢模式比較優雅的實現代碼,其原理就是類加載器ClassLoader保證了并發的安全性。

public class Singleton{
  private Singleton(){}
  public static synchronized Singleton getInstance() {
    return SingletonHolder.instance;
  }
  private static class SingletonHolder{
    private static final Singleton instance = new Singleton();
  }
}

Android系統下的單例模式有

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

2. Builder模式

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

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
  .setTitle("title")
  .setMessage("message")
  .setPositiveButton("OK", null)
  // create之前的set方法操作的都是AlertDialog.Builder而已
  .create()
  // create之后就創建出一個真實的AlertDialog了,將復雜的創建過程都隱藏在create方法內部
  .show();

從上述代碼中可以非常直觀的看出Builder模式就是先設置好各種參數,然后再通過一個方法的調用構建出來一個復雜的對象。

3. 原型模式

原型模式很簡單,使用一個實例拷貝后的實例。
在java中拷貝存在深拷貝與淺拷貝,如果類中的成員變量都是基本數據類型,則系統自動實現這些基本數據類型的深拷貝。
那何時要用到原型模式呢?防止自己的實例在傳入別的模塊后,發生意外的修改。進行一次拷貝,再將該實例當做參數傳遞給別人,是非常明智的操作,尤其是這個實例是某個類的成員變量。

Uri uri = Uri.parse("smsto:10086");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);
// 這里舉例說明intent是提供了一次clone方法的
Intent intent = shareIntent.clone();
// 假設startActivity方法會修改intent
startActivity(intent);
// 然后接著使用shareIntent進行其他操作
// 如果不clone,鬼知道shareIntent會變成什么樣子
shareIntent.putExtra("key", "extra")
...

4. 工廠方法模式

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

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

public class ConcreteProductA extends Prodect{
    public void method(){
        System.out.println("我是產品A!");
    }
}
public class ConcreteProductB extends Prodect{
    public void method(){
        System.out.println("我是產品B!");
    }
}
// 類的實例化被推遲到了子類
public class MyFactory extends Factory{
    public Product createProduct(){
        return new ConcreteProductA();
    }
}

Android下的例子有BitmapFactory

public class BitmapFactory {
    public static Bitmap decodeFile(String pathName, Options opts) {
        Bitmap bm = null;
        ......
        return bm;
    }
}

5. 抽象工廠模式

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

public abstract class AbstractProductA{
    public abstract void method();
}
public abstract class AbstractProdectB{
    public abstract void method();
}
// 這個工廠一旦創建,就天生必須擁有兩條生產線,具體生產什么,將由子類決定
public abstract class AbstractFactory{
    public abstract AbstractProductA createProductA();
    public abstract AbstractProductB createProductB();
}

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

抽象工廠與工廠方法的區別,表面上看就是生產線是多條,還是有一條。但其設計模式的思想是表達了抽象工廠表示一組關聯的生產線,而不僅僅是表示同一類型的生產線。

0.0 簡單工廠

這并不是一個真正的模式,但是和抽象工廠和工廠方法模式一樣,它經常被用于封裝創建對象的代碼

// 根據傳入的參數決定給出哪個Service
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);
  }

6. 策略模式

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

public abstract class AbstractSortAlgorithm {
  public abstract sort(List list);
}
public class CustomList extends List {
  private AbstractSortAlgorithm algorithm;
  public void setSortAlgorithm(AbstractSortAlgorithm algorithm){this.algorithm = algorithm;}
  public void sortList() {algorithm.sort(this);}
}

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

7. 狀態模式

允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。

public abstract class Actor {
    public abstract void act();
}

class GirlActor extends Actor{
    public void act(){System.out.println("GirlActor");}
}
class BoyActor extends Actor{
    public void act(){System.out.println("BoyActor");}
}

class Stage{
    private Actor actor;
    public void firstFrame(Actor actor){this.actor = new GirlActor();}
    public void secondFrame(Actor actor){this.actor = new BoyActor();}
    // 在不同的狀態下會有不同的輸出
    public void performPlay(){
        actor.act();
    }
}

狀態模式在Android下使用的還是很多的,比如WIFI在開啟時,自動掃描周圍的接入點,然后以列表展示,當WIFI關閉時,清空列表。

8. 責任鏈模式

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

最好的一個例子就是View的測量,定位,繪制,事件傳遞,事件分發等。這些例子都是從父View開始依次向子View進行,是一個特別典型的責任鏈模式。特別要提到的是,savedInstance的處理也是責任鏈模式。

9. 解釋器模式

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

通俗的來講,就是自定義一個格式文件,然后解析它。

AndroidManifest.xml是典型的一個自定義xml文件語言,layout目錄下的xml文件,都是具有特殊語法的文件。他們都會被XmlPullParser這個解析器進行解釋。

10. 命令模式

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

在Android底層的事件機制中,底層邏輯對事件的轉發處理。每次的按鍵事件會被封裝成NotifyKeyArgs對象,通過InputDispatcher封裝具體的事件操作。

11. 觀察者模式

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

觀察者模式比較典型的場景就是發布消息通知訂閱者。沒錯Android下的廣播就是觀察者模式,在FrameWork層,狀態發生變化后(比如WIFI狀態),會遍歷全部的register。

12. 備忘錄模式

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

通俗的講就是一個提前備份,一旦出現問題,能盡可能的恢復到最接近之前的狀態。

Android下的onSaveInstanceState就在時不時的進行著備忘,在onRestoreInstanceState時,就能取出最后的記錄。

13. 迭代器模式

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

這個迭代器再好理解不過了,我們天天用的Iterator正是這種迭代器模式。

在Android中SQLiteDatabase的query也是迭代器模式

cursor = database.query(...);
while(cursor.moveToNext()){
    cursor.get...;
}

14. 模板方法模式

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

直接看代碼

public class CustomActivity extends Activity {
    // Activity留給用戶自定義某些特定步驟
    protected void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      // ... ...
    }
}

可以看出模板方法就是留出適當的方法等待子類來實現。不管如何重寫父類,父類的主邏輯將不會被改變。

15. 訪問者模式

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

Android中很少使用訪問者模式,ButterKnife中到是使用了。
這里只給出一個案例:
場景:一群人到你家,一起喂食寵物

  1. 抽象寵物角色(被訪問者):動物抽象類
  2. 抽象訪問者角色(訪問者):給寵物喂食的人
  3. 具體被訪問者:狗,貓
  4. 結構對象角色:主人家
  5. 具體訪問者:主人、其他人

下面就具體來看實現:

  1. 創建抽象節點--寵物
interface Animal {
    void accept(Person person);
}
  1. 創建抽象訪問者接口
interface Person {
    void feed(Cat cat);
    void feed(Dog dog);
}
  1. 創建Animal接口的具體節點
class Dog implements Animal {
    @Override public void accept(Person person){
        person.feed(this);
        System.out.println("旺旺");
    }
}
class Cat implements Animal {
    @Override public void accept(Person person){
        person.feed(this);
        System.out.println("喵喵");
    }
}
  1. 創建具體訪問者角色
class Owner implements Person {
    @Override public void feed(Cat cat) {System.out.println("主人喂食貓");}
    @Override public void feed(Dog dog) {System.out.println("主人喂食狗");}
}
class Someone implements Person {
    @Override public void feed(Cat cat) {System.out.println("客人喂食貓");}
    @Override public void feed(Dog dog) {System.out.println("客人喂食狗");}
}
  1. 具體的結構對象,訪問者與被訪問者的交互空間,準確地說是Person訪問Animal
class Home {
    private List<Animal> nodeList = new ArrayList<>();
    void add(Animal animal) {nodeList.add(animal);}

    void action(Person person) {
        for (Animal node : nodeList) {node.accept(person);}
    }
}
  1. 最后進行測試
public class Test {
    public static void main(String[] args) {
        Home home = new Home();
        home.add(new Dog());
        home.add(new Cat());

        Owner owner = new Owner();
        home.action(owner);

        Someone someone = new Someone();
        home.action(someone);
    }
}

16. 中介者模式

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

中介者類似于電腦主板,主板接著顯示器,硬盤鍵盤等等設備,設備之間的通信全部經過主板協調。

在Android中的Binder驅動也是一個中介者,所有的Service在通信之前都會向ServiceManger查詢出目標Service,也就是說,進程直接不是直接跨進程,而是由ServiceManager來管理的。所以說Binder啟動就是一個中介者模式。

17. 代理模式

定義:為其他類提供一種代理以控制這個對象的訪問。

AIDL文件會生成一個代理類,在跨進程通信傳遞數據時,Parcelable對象的序列化與transct寫入對方進程的內存地址,這一系列的操作都被代理類進行了隱藏。所以代理模式能很好的隱藏一些復雜的操作。

18. 組合模式

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

Android中的ViewGroup便是樹形結構,每個ViewGroup包含一系列View,而ViewGroup本身就是View。這便是組合模式。

19. 適配器模式

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

適配器使用的頻率特別高,每個ListView和RecycleView都需要適配器來給出每個ItemView。至于如何給出ItemView,可能不盡相同,但是Adapter給出了一個統一的接口規范,只需要進行一次轉換即可。

20.裝飾模式

定義:動態的給一個對象添加額外的指責。就增加功能來說,裝飾模式比子類集成的方式更靈活。

Android中的ContextWrapper(ContextThemeWrapper的父類(Acrtivity的父類))。是一個地地道道的裝飾者

public class ContextWrapper extends Context {
    Context mBase;
    // 由構造函數傳入一個content
    public ContextWrapper(Context base) {
        mBase = base;
    }
}

21. 享元模式

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

享元可以說一直都在被使用,比如Java的常量池,線程池等。主要是為了重用對象。

在Android中,Handler機制中有個postMessage方法,可以通過Message.obtain()來從消息池中取出可復用Message,避免產生大量Message對象。

22. 外觀模式

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

外觀模式核心在于子系統與外部通信,比如當你sendBroadcast、startActivity、bindService等等,系統內部的實現非常復雜,但是一個統一的對象context把子系統全部隱藏起來。

23. 橋接模式

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

一個類在多個維度上要進行變化,比如列表View在列表如何展示與數據來源上的兩個維度的變化。具體來說就是AbsListView的兩個維度的變化,AbsListView的子類有ListView、GridView、CustomListView等等,而ListAdapter也是一個變化的維度。

24. MVC、MVP、MVVM等組合設計模式

MVC

全稱是Model-View-Controller,如下圖:
MVC模式

在Android下,layout.xml就是View層。數據層可以由數據庫與網絡數據構成Model,但Model層要完全表達數據的輸入與輸出。Activity就是Controller層,在比較復雜的Activity中,往往不能很好的完成表達成Controller層,往往混合了View層代碼與Model層代碼,這也是Activity的尷尬之處。

MVP

Model-View-Presenter,如下圖:


mvp模式

這個模式主要是為了解決Activity在MVC模式下的尷尬處境,把Activity完全表示為一個View層。這樣Activity就可以很單純的處理View的問題了,處理View顯示問題也是Activity的特長。

MVVM

Model-View-ViewModel,如下圖:
MVVM模式

這個模式優化的顯示層的問題,View單向的訪問ViewModel,ViewModel與Model之間進行交互。在RecycleView中的ViewHolder正是這種模式。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370