1. 程式人生 > 實用技巧 >設計模式學習筆記(五):工廠方法模式

設計模式學習筆記(五):工廠方法模式

1 前言

儘管簡單工廠模式實現了物件的建立和使用分離,但是仍然存在以下兩個問題:

  • 工廠類過於龐大,包含了大量的判斷程式碼,導致維護和測試難度增大
  • 系統擴充套件不靈活,如果增加了新的產品型別,必須修改靜態工廠方法的業務邏輯,違反了開閉原則
  • 具體產品與工廠類之間的耦合度高,嚴重影響了系統的靈活性和擴充套件性

一個更好的辦法是使用工廠方法模式。

2 工廠方法模式

工廠方法模式:定義一個用於建立物件的介面,讓子類決定將哪一個類例項化。
工廠方法又簡稱工廠模式或虛擬構造器模式或多型工廠模式,讓一個類的例項化延遲到其子類,是一種類建立型模式。
結構圖如下:

工廠方法模式包含以下四個角色:

  • Product(抽象產品):定義產品的介面,是工廠方法模式所建立的超型別,也就是產品物件的公共父類
  • ConcreteProduct(具體產品):實現了抽象產品介面,某種型別的具體產品由專門的具體工廠建立,具體工廠與具體產品一一對應
  • Factory(抽象工廠):在抽象工廠類中,聲明瞭工廠方法,用於返回一個產品。抽象工廠是工廠方法模式的核心,所有建立物件的工廠類都必須實現該介面
  • ConcreteFactory(具體工廠):它是抽象工廠類的子類,實現了抽象工廠中定義的工廠方法,並可由客戶端呼叫,返回一個具體產品類的例項

3 例項

日誌記錄器的設計:該記錄器可以通過多種途徑儲存系統的執行日誌,例如檔案記錄或者資料庫記錄。

程式碼如下:

public class Test
{
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();        
        Logger logger = factory.createLogger();
        logger.log();
    }
}

//抽象產品
interface Logger
{
    void log();
}

//具體產品:DatabaseLogger
class DatabaseLogger implements Logger
{
    public void log()
    {
        System.out.println("資料庫日誌記錄");
    }
}

//具體產品:FileLogger
class FileLogger implements Logger
{
    public void log()
    {
        System.out.println("檔案日誌記錄");
    }
}

//抽象工廠
interface LoggerFactory
{
    Logger createLogger();
}

//具體工廠:DatabaseLoggerFactory
class DatabaseLoggerFactory implements LoggerFactory
{
    public Logger createLogger()
    {
        return new DatabaseLogger();
    }
}

//具體工廠:FileLoggerFactory
class FileLoggerFactory implements LoggerFactory
{
    public Logger createLogger()
    {
        return new FileLogger();
    }
}

4 隱藏

可以把抽象工廠設定為抽象類,工廠方法直接可以對客戶端隱藏,也就是說可以直接通過抽象工廠呼叫具體產品類的業務方法,客戶端無需建立具體產品,直接通過工廠類呼叫即可,程式碼修改如下(抽象產品以及具體產品類不用修改):

//抽象工廠
abstract class LoggerFactory
{
    public void log()
    {
        this.createLogger().log();
    }
    public abstract Logger createLogger();
}

//具體工廠:DatabaseLoggerFactory
class DatabaseLoggerFactory extends LoggerFactory
{
    public Logger createLogger()
    {
        return new DatabaseLogger();
    }
}

//具體工廠:FileLoggerFactory
class FileLoggerFactory extends LoggerFactory
{
    public Logger createLogger()
    {
        return new FileLogger();
    }
}

public class Test
{
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();
        factory.log();
    }
}

5 主要優點

  • 封裝細節:工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節,使用者只需關心所需產品對應的工廠,無需關心建立細節,甚至無須知道具體產品類的類名
  • 多型:工廠方法的多型效能夠讓工廠可以自主確定建立何種產品物件,而如何建立物件的細節則完全封裝在具體工廠內部
  • 擴充套件性好:加入新產品時無須修改抽象工廠,抽象產品的介面,也無須修改客戶端與其他的具體產品和具體工廠,只需要增加一個具體工廠以及具體產品,系統擴充套件性很好,完全符合開閉原則

6 主要缺點

  • 類數量多:在新增新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,一定程度上增加了系統的複雜度,有更多的類需要編譯和執行,給系統帶來額外開銷
  • 增加理解難度:基於系統的擴充套件性需要引入抽象層,在客戶端中均使用了抽象層的定義,增加了系統的抽象性以及理解難度

7 適用場景

  • 客戶端不知道其所需要的物件的類:在工廠方法模式中,客戶端不需要知道具體的產品類名,只需要知道所對應的工廠即可
  • 抽象工廠類通過子類來指定建立哪個物件:工廠方法模式中,抽象工廠類只需要提供一個建立產品的介面,而有其子類來確定具體要建立的物件,利用面向物件的多型性和里氏代換原則,在程式執行時,子類物件將覆蓋父類物件,從而使得系統更加容易擴充套件

8 總結