天天看點

Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式

文章目錄

  • Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式
    • 簡單工廠模式
      • 概述
      • 應用場景
      • 優缺點
      • 實作
    • 工廠方法模式
      • 概述
      • 應用場景
      • 優缺點
      • 實作
    • 抽象工廠模式
      • 概述
      • 應用場景
      • 優缺點
      • 實作
    • 總結

Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式

建立型模式的主要特點是“将對象的建立和使用分離”對類的執行個體化過程進行了抽象,能夠将軟體子產品中對象的建立和對象的使用分離,隐藏了類的執行個體的建立細節,通過隐藏對象如何被建立群組合在一起達到使整個系統獨立的目的。為了使軟體的結構更加清晰,外界對于這些對象隻需要知道它們共同的接口,而不清楚其具體的實作細節,使整個系統的設計更加符合單一職責原則。

當我們在開發中,存在需要生成複雜對象的時候,即類的構造較為複雜時,此時如果我們将直接在業務代碼中構造複雜對象,那麼就容易造成業務代碼中存在耦合,此時我們可以考慮使用工廠模式。工廠模式即定義一個專門用于構造對象的類,通過該類來構造複雜對象,進而“将對象的建立和使用分離”。

工廠模式可以分為簡單工廠模式、工廠方法模式和抽象工廠模式。其中簡單工廠模式不屬于23種GOF設計模式之中。

簡單工廠模式

概述

簡單工廠模式不屬于23種GOF設計模式,但是在開發過程中也屬于一種常用設計模式。簡單工廠模式又稱為靜态工廠方法模式,它屬于類建立型模式。在簡單工廠模式中,可以根據參數的不同傳回不同類的執行個體。簡單工廠模式專門定義一個類來負責建立其他類的執行個體,被建立的執行個體通常都具有共同的父類。

在工廠模式中,我們通常稱構造的複雜對象為”産品“,把建立複雜對象的類稱為”工廠“,在簡單工廠模式中,我們通常會建立一個具體的工廠類,然後建立一個靜态的方法用于建立”産品“,然後通過傳入的參數類型來傳回對應的”産品“對象。

應用場景

或許,我們可能會疑惑,根據簡單工廠模式的描述和實作,我們會發現,簡單工廠模式實際上就是将構造對象放入了一個專門的類中,在使用的時候我們還是需要根據不同的類型來擷取不同的對象,那麼這和我們直接通過建立一個對象有什麼差別呢?有使用這個設計模式的必要嗎?

答案是有這個必要的。因為一旦對象構造複雜,我們可能需要傳遞一系列參數,甚至有時我們在建立該對象時還需要先構造一系列的與業務代碼不相關的對象,這種代碼一旦多了之後就造成業務代碼臃腫,同時也違反了

單一職責原則

。有可能在不同情況下,我們可能需要使用到同一抽象類的不同具體實作類,此時我們隻需要知道所需類型即可,不關心不同對象的具體構造方式,将構造對象的代碼直接寫入業務代碼顯然是不合理的。這時,我們就可以考慮使用工廠模式了。

比如一個場景下,我們可能在需要在界面上使用一個圓形和方形的按鈕,且二者都繼承自一個按鈕的基類,在建立兩個對象之前,可能還有一些擷取構造參數的步驟,此時我們就可以使用簡單工廠模式。

對于産品種類相對較少的情況,考慮使用簡單工廠模式。使用簡單工廠模式的用戶端隻需要傳入工廠類的參數,不需要關心如何建立對象的邏輯,可以很友善地建立所需産品。

優缺點

優點:

  • 工廠類包含必要的邏輯判斷,可以決定在什麼時候建立哪一個産品的執行個體。用戶端可以免除直接建立産品對象的職責,很友善的建立出相應的産品。工廠和産品的職責區分明确。進而實作了”對象建立和使用分離“。
  • 用戶端無需知道所建立具體産品的類名,隻需知道參數即可。
  • 也可以引入配置檔案,在不修改用戶端代碼的情況下更換和添加新的具體産品類。

缺點:

  • 簡單工廠模式的工廠類單一,負責所有産品的建立,職責過重,一旦異常,整個系統将受影響。且工廠類代碼會非常臃腫,違背高聚合原則。
  • 使用簡單工廠模式會增加系統中類的個數(引入新的工廠類),增加系統的複雜度和了解難度。
  • 簡單工廠模式使用了 static 工廠方法,造成工廠角色無法形成基于繼承的等級結構.
  • 系統擴充困難,一旦添加新産品就不得不修改工廠邏輯,在産品類型較多時,有可能造成工廠邏輯過于複雜,不利于系統的擴充和維護。違背了

    開閉原則

實作

簡單工廠模式實作方式較為簡單,在簡單工廠模式中,其結構主要分為以下三部分:

  • 工廠類:負責建立負責對象,通常會提供一個靜态方法,然後根據請求類型傳回具體對象。
  • 抽象産品類:産品類的抽象基類。
  • 具體産品類:工廠模式建構的具體産品對象。通常都繼承自一個基類。

就拿上述的建立兩個不同按鈕為例,首先我們建構抽象産品類Button,其存在一個draw方法用于繪制按鈕,并建立兩個具體産品類,具體如下:

/**
 * 抽象産品類
 */
abstract class Button{

    init {
        draw()
    }

    abstract fun draw();

}

/**
 * 具體産品類
 */
class CircleButton : Button() {

    override fun draw() {
        println("draw a circle")
    }

}

/**
 * 具體産品類
 */
class RectButton : Button() {

    override fun draw() {
        println("draw a rect")
    }

}
           

然後,我們建立一個工廠類,用于構造Button對象,具體如下:

/**
 * 簡單工廠模式
 */
class SimpleFactory {

    companion object{
        const val BUTTON_CIRCLE = "button_circle"
        const val BUTTON_RECT = "button_rect"

        /**
         * 構造具體産品對象的靜态方法
         * @param type 産品類型
         * @return Button抽象類型
         */
        public fun createButton(type : String) : Button{
            return when(type){
                BUTTON_CIRCLE -> {
                    // TODO 可能某些用于構造對象的操作
                    CircleButton()
                }
                BUTTON_RECT -> {
                    // TODO 可能某些用于構造對象的操作
                    RectButton()
                }
                else -> throw Exception("錯誤的類型")
            }
        }
    }

}
           

如此一來,我們就完成了一個簡單工廠模式的應用,當我們需要建立Button對象時,我們隻需要傳入對應的類型就可以擷取到對應的Button對象,外部對如何建立Button對象是完全不關心的。如下:

val circle = SimpleFactory.createButton(SimpleFactory.BUTTON_CIRCLE)
val rect = SimpleFactory.createButton(SimpleFactory.BUTTON_RECT)
           

工廠方法模式

概述

從上文可知,對于簡單工廠模式,如果我們需要新增一個産品類型,比如,我們又添加了一個圓角矩形的按鈕,我們隻需要在工廠類中添加一個對應的類型并在createButton方法中建立對應對象即可。但是,如果類型很多呢?那麼我們就需要在createButton方法中添加大量代碼,将所有Button建立都放于工廠類中,而且也違反了

開閉原則

,需要不斷修改原方法。

而工廠方法模式就是對于簡單工廠模式的進一步抽象,相比于簡單工廠模式,工廠方法模式将工廠也進行抽象,然後對于每個具體産品類,我們都建立一個具體的工廠類,專門用于該産品的構造對象。這種抽象化的結果使這種結構可以在不修改具體工廠類的情況下引進新的産品,如果出現新的按鈕類型,隻需要為這種新類型的按鈕建立一個具體的工廠類就可以獲得該新按鈕的執行個體,這一特點無疑使得工廠方法模式具有超越簡單工廠模式的優越性,更加符合“開閉原則”。

應用場景

  • 當外部建立對象的類無法預知對象确切類别及其依賴關系時,可使用工廠方法。工廠方法模式将建立産品的代碼與實際使用産品的代碼分離, 進而能在不影響其他代碼的情況下擴充産品建立部分代碼。
  • 當我們希望使用者可擴充内部元件時,可使用工廠方法。我們可以建立對應的抽象工廠類和産品類,當使用者需要對元件進行擴充時,隻需要繼承抽象産品類實作具體産品,然後再構造其對應的工廠類即可。
  • 當我們建立對象時,希望可以複用之前的對象,也可以使用工廠方法模式。在處理大型資源密集型對象 (比如資料庫連接配接、 檔案系統和網絡資源) 時,我們通常需要進行對象複用,但是如果我們将複用的代碼寫在業務邏輯代碼中,那麼顯然是不合理的,是以我們可以使用工廠方法模式,将構造和複用對象的代碼全部封裝到工廠類中。

優缺點

優點:

  • 滿足

    單一職責原則

    。 你可以将産品建立代碼放在其對應的工廠類中, 進而使得代碼更容易維護。
  • 滿足

    開閉原則

    。 當需要擴充新的産品類型時,無需更改現有用戶端代碼, 隻需要繼承抽象産品類和工廠類建立對應的産品類和工廠類即可。
  • 在工廠方法模式中,工廠方法用來建立客戶所需要的産品,同時還向客戶隐藏了哪種具體産品類将被執行個體化這一細節,使用者隻需要關心所需産品對應的工廠,無須關心建立細節。

缺點:

  • 應用工廠方法模式需要引入許多新的子類, 每個産品對需要建立對應的産品類和工廠類,代碼可能會是以變得更複雜。 最好的情況是将該模式引入建立者類的現有層次結構中。
  • 由于考慮到系統的可擴充性,需要引入抽象層,在用戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和了解難度,且在實作時可能需要用到DOM、反射等技術,增加了系統的實作難度。

實作

與簡單工廠模式相比,工廠方法模式抽象化程度更高,其結構相比而言也更加複雜一點,具體如下:

  • 抽象工廠(建立者)類:聲明傳回産品對象的工廠方法。 該方法的傳回對象類型必須與産品接口相比對。
  • 具體産品工廠(建立者)類:将會重寫基礎工廠方法, 使其傳回不同類型的産品。
  • 抽象産品類:定義了産品的規範,描述了産品的主要特性和功能。
  • 具體産品類:實作了抽象産品角色所定義的接口,由具體工廠來建立,它同具體工廠之間一一對應。

如下圖所示:

Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式

我們還是以之前的Button為例,其中産品類和具體産品類與簡單工廠方法相同,我們所需要修改的就是工廠類,首先建立抽象工廠類,如下:

/**
 * 抽象工廠類
 */
abstract class ButtonFactory {

    /**
     * 抽象工廠方法,用于建立産品對象,傳回類型為Button
     * 也可以不将其設為抽象方法,而是提供一個預設實作,子類去重寫
     */
    abstract fun createButton():Button
    
}
           

抽象工廠提供了一個工廠方法,用于建立對應的産品對象,我們可以将其設為抽象方法

abstract

,也可以根據需求提供一個預設實作。然後對于每個具體的産品類,我們都需要去建立一個對應的工廠類,如下:

/**
 * 具體産品工廠類
 */
class CircleButtonFactory : ButtonFactory(){

    override fun createButton(): Button {
        //TODO 建構具體産品對象的邏輯
        return CircleButton()
    }

}

/**
 * 具體産品工廠類
 */
class RectButtonFactory : ButtonFactory(){

    override fun createButton(): Button {
        //TODO 建構具體産品對象的邏輯
        return RectButton()
    }

}
           

至此,工廠方法模式就實作完畢了,在之後的業務邏輯中,我們隻需要調用對應的工廠類即可。值得注意的是,在具體的工廠類中,我們的工廠方法也應該傳回Button類型,而不是具體的産品類型,因為這樣在是使用工廠建立對象的位置就不會與産品類型耦合。

當然,這隻是最簡單的工廠方法模式的實作,實際上我們可以根據實際開發需求對其進行修改。比如工廠方法可以提供多個重載方法,根據不同的請求參數進而建立不同的産品對象,同時我們也可以通過工廠模式實作對産品對象的複用,在工廠類中持有一個對象池,當對象已存在時直接複用等。

一般來說,工廠對象應當有一個抽象的父類型,如果工廠等級結構中隻有一個具體工廠類的話,抽象工廠就可以省略,也将發生了退化。當隻有一個具體工廠,在具體工廠中可以建立所有的産品對象,并且工廠方法設計為靜态方法時,工廠方法模式就退化成簡單工廠模式。

抽象工廠模式

概述

在工廠方法模式中具體工廠負責生産具體的産品,每一個具體工廠對應一種具體産品,工廠方法也具有唯一性,一般情況下,一個具體工廠中隻有一個工廠方法或者一組重載的工廠方法。但是有時候我們需要一個工廠可以提供多個産品對象,而不是單一的産品對象。此時,我們又應該如何實作呢?這時,我們就可以使用抽象工廠模式了。

為了更好地了解抽象工廠模式,首先,我們需要了解兩個概念:

  • **産品等級結構:**産品等級結構即産品的繼承結構,如:手機是一個抽象類,但不同品牌地手機不同,是以其存在具體的實作類華為手機、小米手機、蘋果手機等。這就構成了一個産品等級結構,其中手機為父類,具體不同品牌的手機為子類。
  • **産品族:**所謂産品族就是同一系列的不同産品,同一個工廠生産的,位于不同産品等級結構中的一組産品。比如:華為所生産的不僅是手機,還有電腦,平闆等其它産品,這就是華為的産品族。

了解了産品族和産品等級結構後,我們就會發現,使用工廠方法模式,我們隻能構造同一産品等級結構的産品對象,如果我們在開發中所需要建立的複雜對象,不僅是一個産品等級結構,而可能存在多個産品族,此時使用工廠方法模式是很難實作的。此時就需要使用抽象工廠模式了。

抽象工廠模式是一種為通路類提供一個建立一組相關或互相依賴對象的接口,且通路類無須指定所要産品的具體類就能得到同族的不同等級的産品的模式結構。

抽象工廠模式與工廠方法模式最大的差別在于,工廠方法模式針對的是一個産品等級結構,而抽象工廠模式則需要面對多個産品等級結構,一個工廠等級結構可以負責多個不同産品等級結構中的産品對象的建立 。當一個工廠等級結構可以建立出分屬于不同産品等級結構的一個産品族中的所有對象時,抽象工廠模式比工廠方法模式更為簡單、有效率。

應用場景

  • 如果代碼需要與多個不同系列的相關産品互動,但是由于無法提前擷取相關資訊,或者出于對未來擴充性的考慮,你不希望代碼基于産品的具體類進行建構,在這種情況下,可以使用抽象工廠模式。抽象工廠為你提供了一個接口, 可用于建立每個系列産品的對象。 隻要代碼通過該接口建立對象, 那麼你就不會生成與應用程式已生成的産品類型不一緻的産品。
  • 如果一個類與多種類型産品互動, 就可以考慮将工廠方法抽取到獨立的工廠類或具備完整功能的抽象工廠類中。

優缺點

優點:

  • 抽象工廠模式隔離了具體類的生成,使得客戶并不需要知道什麼被建立。由于這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實作了抽象工廠中定義的那些公共接口,是以隻需改變具體工廠的執行個體,就可以在某種程度上改變整個軟體系統的行為。
  • 當一個産品族中的多個對象被設計成一起工作時,它能夠保證用戶端始終隻使用同一個産品族中的對象。
  • 增加一個産品族隻需要增加一個工廠類即可,滿足

    開閉原則

缺點:

  • 開閉原則

    的傾斜。增加一個産品族隻需要增加一個具體工廠類即可,但增加一個産品等級結構困難,需要改變抽象類,不滿足

    開閉原則

  • 由于采用該模式需要向應用中引入衆多接口和類, 代碼可能會比之前更加複雜。

實作

抽象工廠模式,其主要結構分為四個部分:

  • 抽象工廠:聲明了一組建立各種抽象産品的方法,聲明了一個産品族。
  • 具體工廠:繼承了抽象工廠,代表了一個産品族,實作了該産品族各個産品對象的建立。
  • 抽象産品:為每種産品聲明接口,在抽象産品中定義了産品的抽象業務方法。
  • 具體産品:定義具體工廠生産的具體産品對象,實作抽象産品接。口中定義的業務方法

其結構圖如下:

Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式Android開發學習筆記之設計模式——工廠方法模式&抽象工廠模式

我們可以以上述的智能産品為例,存在華為和小米兩個品牌,二者都生産手機和電腦。首先,我們建立各個抽象産品類Phone和Computer,以及其對應的具體産品,如下:

/**
 * 手機的抽象類
 */
abstract class Phone {

    abstract fun call()

}

/**
 * 華為手機,具體産品類
 */
class HuaWeiPhone : Phone(){

    override fun call() {
        //TODO 具體業務代碼
        println("華為手機")
    }

}

/**
 * 小米手機,具體産品類
 */
class XiaoMiPhone : Phone(){

    override fun call() {
        //TODO 具體業務代碼
        println("小米手機")
    }

}
           
/**
 * 電腦抽象産品類
 */
abstract class Computer {
    
    abstract fun play()
    
}

/**
 * 華為電腦,具體産品類
 */
class HuaWeiComputer : Computer() {
    
    override fun play() {
        //TODO 業務代碼
        println("華為電腦")
    }

}

/**
 * 小米電腦,具體産品類
 */
class XiaoMiComputer : Computer() {

    override fun play() {
        //TODO 業務代碼
        println("小米電腦")
    }

}
           

然後,建立抽象工廠,在其中聲明手機和電腦的工廠方法,如下:

/**
 * 抽象工廠,聲明建立産品的工廠方法
 */
abstract class ElectronicsFactory {

    /**
     * 建立手機的抽象工廠方法
     */
    abstract fun createPhone(): Phone

    /**
     * 建立電腦的抽象工廠方法
     */
    abstract fun createComputer(): Computer

}
           

最後,我們隻需要繼承抽象工廠,建立具體工廠類,實作工廠方法即可實作一個産品族,如下:

/**
 * 華為工廠,具體工廠
 */
class HuaWeiFactory : ElectronicsFactory() {

    /**
     * 華為手機的具體工廠方法
     */
    override fun createPhone(): Phone {
        //TODO 可能存在的邏輯
        return HuaWeiPhone()
    }

    /**
     * 華為電腦的具體工廠方法
     */
    override fun createComputer(): Computer {
        //TODO 可能存在的邏輯
        return HuaWeiComputer()
    }

}

/**
 * 小米工廠,具體工廠
 */
class XiaoMiFactory : ElectronicsFactory() {

    /**
     * 小米手機的具體工廠方法
     */
    override fun createPhone(): Phone {
        //TODO 可能存在的邏輯
        return XiaoMiPhone()
    }

    /**
     * 小米的具體工廠方法
     */
    override fun createComputer(): Computer {
        //TODO 可能存在的邏輯
        return XiaoMiComputer()
    }

}
           

至此,抽象工廠模式的簡單應用就完成了,我們在需要建立産品對象時,我們隻需要使用對應的工廠類,然後調用對應産品的工廠方法即可,而不必關心内部建立對象的邏輯。

總結

盡管從簡單工廠模式、到工廠方法模式,再到抽象工廠模式,盡管三者是抽象程度越來越高,其中抽象工廠模式最為抽象和最具一般性的一種形态。,但是,我們在實際開發過程中,不是就一昧的使用抽象工廠模式,而是要根據實際情況來選擇。因為抽象化程度越高,其代碼量就會越多,類就越複雜,如果使用場景簡單的話,我們可以直接使用簡單工廠模式和工廠方法模式。

當抽象工廠模式中每一個具體工廠類隻建立一個産品對象,也就是隻存在一個産品等級結構時,抽象工廠模式退化成工廠方法模式;當工廠方法模式中抽象工廠與具體工廠合并,提供一個統一的工廠來建立産品對象,并将建立對象的工廠方法設計為靜态方法時,工廠方法模式退化成簡單工廠模式。是以,在隻存在一個産品結構時,我們是完全可以使用簡單工廠模式和工廠方法模式來替代的,即使使用抽象工廠模式也隻會提高代碼量,不能帶來更多的優化效果。

設計模式,學習起來很簡單,關鍵是需要在之後的開發中能夠靈活運用,而且不同模式也能夠互相配合,比如在工廠模式中,其實我們可以結合之前學習的單例模式,這樣我們就不需要建立工廠對象了。