設計模式系列之工廠模式三兄弟(Factory Pattern)

行無際發表於2020-06-14

說明:設計模式系列文章是讀劉偉所著《設計模式的藝術之道(軟體開發人員內功修煉之道)》一書的閱讀筆記。個人感覺這本書講的不錯,有興趣推薦讀一讀。詳細內容也可以看看此書作者的部落格https://blog.csdn.net/LoveLion/article/details/17517213

工廠模式是最常用的一類建立型設計模式,通常我們所說的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。簡單工廠模式是工廠方法模式的“小弟”,它不屬於GoF23種設計模式,但在軟體開發中應用也較為頻繁,通常將它作為學習其他工廠模式的入門。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。這三種工廠模式各具特色,難度也逐個加大,在軟體開發中它們都得到了廣泛的應用,成為物件導向軟體中常用的建立物件的工具。

簡單工廠模式

簡單工廠模式並不屬於GoF 23個經典設計模式,但通常將它作為學習其他工廠模式的基礎,它的設計思想很簡單。

模式定義

簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據引數的不同返回不同類的例項,被建立的例項通常都具有共同的父類。因為在簡單工廠模式中用於建立例項的方法是靜態(static)方法,因此簡單工廠模式又被稱為靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。

簡單工廠模式的要點在於:當你需要什麼,只需要傳入一個正確的引數,就可以獲取你所需要的物件,而無須知道其建立細節。

模式結構圖

簡單工廠模式結構圖如下所示:

簡單工廠模式結構圖

模式虛擬碼

在使用簡單工廠模式時,首先需要對產品類進行重構,不能設計一個包羅永珍的產品類,而需根據實際情況設計一個產品層次結構,將所有產品類公共的程式碼移至抽象產品類,並在抽象產品類中宣告一些抽象方法,以供不同的具體產品類來實現,典型的抽象產品類程式碼如下所示:

public abstract class Product {
    // 所有產品的公共屬性

    // 所有產品類的公共業務方法
    public void methodSame() {
        //公共方法的實現
    }

    // 宣告抽象業務方法
    public abstract void methodDiff();
}

具體產品類中實現了抽象產品類中宣告的抽象業務方法。

public class ConcreteProduct extends Product {
    @Override
    public void methodDiff() {
        // 具體產品業務方法的實現
    }
}

簡單工廠模式的核心是工廠類,在沒有工廠類之前,客戶端一般會使用new關鍵字來直接建立產品物件,而在引入工廠類之後,客戶端可以通過工廠類來建立產品,在簡單工廠模式中,工廠類提供了一個靜態工廠方法供客戶端使用,根據所傳入的引數不同可以建立不同的產品物件,典型的工廠類程式碼如下所示:

public class Factory {
    //靜態工廠方法
    public static Product getProduct(String arg) {
        Product product = null;
        if (arg.equalsIgnoreCase("A")) {
            product = new ConcreteProductA();
            //初始化設定product
        } else if (arg.equalsIgnoreCase("B")) {
            product = new ConcreteProductB();
            //初始化設定product
        }
        return product;
    }
}

客戶端程式碼中,我們通過呼叫工廠類的工廠方法即可得到產品物件,典型程式碼如下所示:

public class Client {
    public static void main(String[] args) {
        Product product;
        product = Factory.getProduct("A"); //通過工廠類建立產品物件
        product.methodSame();
        product.methodDiff();
    }
}

模式簡化

有時候,為了簡化簡單工廠模式,我們可以將抽象產品類工廠類合併,將靜態工廠方法移至抽象產品類中,如下圖所示。

簡化的簡單工廠模式

客戶端可以通過產品父類的靜態工廠方法,根據引數的不同建立不同型別的產品子類物件,這種做法在JDK等類庫和框架中也廣泛存在。
比如:java.nio.charset.Charset

public abstract class Charset {

    /**
     * Returns a charset object for the named charset.
     */
    public static Charset forName(String charsetName) {
        java.nio.charset.Charset cs = lookup(charsetName);
        if (cs != null)
            return cs;
        throw new UnsupportedCharsetException(charsetName);
    }
}

模式小結

簡單工廠模式提供了專門的工廠類用於建立物件,將物件的建立和物件的使用分離開,它作為一種最簡單的工廠模式在軟體開發中得到了較為廣泛的應用。

使用場景:

  1. 工廠類負責建立的物件比較少,由於建立的物件較少,不會造成工廠方法中的業務邏輯太過複雜。
  2. 客戶端只知道傳入工廠類的引數,對於如何建立物件並不關心。

工廠方法模式

簡單工廠模式雖然簡單,但存在一個很嚴重的問題。當系統中需要引入新產品時,由於靜態工廠方法通過所傳入引數的不同來建立不同的產品,這必定要修改工廠類的原始碼,將違背“開閉原則”,如何實現增加新產品而不影響已有程式碼?工廠方法模式應運而生。

模式定義

工廠方法模式中,我們不再提供一個統一的工廠類來建立所有的產品物件,而是針對不同的產品提供不同的工廠,系統提供一個與產品等級結構對應的工廠等級結構。工廠方法模式定義如下:

工廠方法模式(Factory Method Pattern):定義一個用於建立物件的介面,讓子類決定將哪一個類例項化。工廠方法模式讓一個類的例項化延遲到其子類。工廠方法模式又簡稱為工廠模式(Factory Pattern),又可稱作虛擬構造器模式(Virtual Constructor Pattern)或多型工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種類建立型模式。

模式結構圖

工廠方法模式提供一個抽象工廠介面來宣告抽象工廠方法,而由其子類來具體實現工廠方法,建立具體的產品物件。工廠方法模式結構如圖所示:

工廠方法模式結構圖

在工廠方法模式結構圖中包含如下幾個角色:

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

模式虛擬碼

與簡單工廠模式相比,工廠方法模式最重要的區別是引入了抽象工廠角色,抽象工廠可以是介面,也可以是抽象類或者具體類,其典型程式碼如下所示:

public interface Factory {
    Product factoryMethod();
}

在抽象工廠中宣告瞭工廠方法但並未實現工廠方法,具體產品物件的建立由其子類負責,客戶端針對抽象工廠程式設計,可在執行時再指定具體工廠類,具體工廠類實現了工廠方法,不同的具體工廠可以建立不同的具體產品,其典型程式碼如下所示:

public class ConcreteFactory implements Factory {
    @Override
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

在客戶端程式碼中,只需關心工廠類即可,不同的具體工廠可以建立不同的產品,典型的客戶端類程式碼片段如下所示:

public class Client {
    public static void main(String[] args) {
        // 確定是哪個工廠可得到產品
        Factory factory = new ConcreteFactory();
        // 獲取產品
        Product product = factory.factoryMethod();
    }
}

模式簡化

有時候,為了進一步簡化客戶端的使用,還可以對客戶端隱藏工廠方法,此時,在工廠類中將直接呼叫產品類的業務方法,客戶端無須呼叫工廠方法建立產品,直接通過工廠即可使用所建立的物件中的業務方法。

// 改為抽象類
public class AbstractFactory {
    // 在工廠類中直接呼叫產品類的業務方法
    public void productMethod() {
        Product product = this.createProduct();
        product.method();
    }

    public abstract Product createProduct();
}

通過將業務方法的呼叫移入工廠類,可以直接使用工廠物件來呼叫產品物件的業務方法,客戶端無須直接呼叫工廠方法,在客戶端並不關心Product細節的情況下使用這種設計方案會更加方便。

模式小結

工廠方法模式能夠讓工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部,使用者只需要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名。基於工廠角色產品角色的多型性設計是工廠方法模式的關鍵。

抽象工廠模式

工廠方法模式通過引入工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,但由於工廠方法模式中的每個工廠只生產一類產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷。此時,我們可以考慮將一些相關的產品組成一個產品族,由同一個工廠來統一生產,這就是我們本文將要學習的抽象工廠模式的基本思想。

這裡我斗膽舉個例子來說明一下吧,如果不恰當歡迎指出。

眾所周知,國內知名的電器廠有海爾、海信(姑且就認為是2個),電器廠會生產電視機、電冰箱、空調(姑且就認為是3種產品)。

  • 使用工廠方法模式:工廠方法模式中每個工廠只生產一類產品,那麼就必須要有海爾電視機廠海爾電冰箱廠海爾空調廠海信電視機廠海信電冰箱廠海信空調廠
  • 使用抽象工廠模式:抽象工廠中每個工廠生產由多種產品組成的"產品族",那麼就只需要有海爾工廠海信工廠就夠了,每個工廠可生產自家的電視機、電冰箱、空調。

由此看出使用抽象工廠模式極大地減少了系統中類的個數。

模式定義

抽象工廠模式為建立一組物件提供了一種解決方案。與工廠方法模式相比,抽象工廠模式中的具體工廠不只是建立一種產品,它負責建立一族產品。抽象工廠模式定義如下:

抽象工廠模式(Abstract Factory Pattern):提供一個建立一系列相關或相互依賴物件的介面,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,它是一種物件建立型模式。

模式結構圖

在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用於產生多種不同型別的產品,這些產品構成了一個產品族,抽象工廠模式結構如圖所示:

抽象工廠模式結構圖

在抽象工廠模式結構圖中包含如下幾個角色:

  • AbstractFactory(抽象工廠):它宣告瞭一組用於建立一族產品的方法,每一個方法對應一種產品。
  • ConcreteFactory(具體工廠):它實現了在抽象工廠中宣告的建立產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中。
  • AbstractProduct(抽象產品):它為每種產品宣告介面,在抽象產品中宣告瞭產品所具有的業務方法。
  • ConcreteProduct(具體產品):它定義具體工廠生產的具體產品物件,實現抽象產品介面中宣告的業務方法。

模式虛擬碼

在抽象工廠中宣告瞭多個工廠方法,用於建立不同型別的產品,抽象工廠可以是介面,也可以是抽象類或者具體類,其典型程式碼如下所示:

public abstract class AbstractFactory {

    public abstract AbstractProductA createProductA();

    public abstract AbstractProductB createProductB();

    public abstract AbstractProductC createProductC();
}

具體工廠實現了抽象工廠,每一個具體的工廠方法可以返回一個特定的產品物件,而同一個具體工廠所建立的產品物件構成了一個產品族。對於每一個具體工廠類,其典型程式碼如下所示:

public class ConcreteFactory1 extends AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }

    @Override
    public AbstractProductC createProductC() {
        return new ConcreteProductC1();
    }
}

模式小結

如果一開始就學習抽象工廠模式估計很難理解為什麼這樣設計,按次序學習分析簡單工廠模式工廠方法模式抽象工廠模式基本就順理成章了。實際開發中,可能並不是照搬照套工廠模式三兄弟的虛擬碼,大多會簡化其中的部分實現。本來學習設計模式就是重思想,學習如何用抽象類、介面、拆分、組合等將軟體解耦合,並增強系統可擴充套件性,這才是最關鍵的。

相關文章