[創建型模式] 工廠方法模式、簡單工廠模式、抽象工廠模式(Factory Pattern)

什麼是工廠模式

富士康生產電子設備,它是個工廠,電子設備是產品。

一汽集團生產汽車,它是個工廠,汽車是產品。

大連造船廠生產航母,它是個工廠,航母是產品。

我翻閱了很多書籍,也瀏覽了很多博客,總結一下,工廠模式大致可以細分爲三種:

  1. 工廠方法模式
  2. 簡單工廠模式
  3. 抽象工廠模式

這麼多年,一直有個問題縈繞在我心裏,從未得到過能讓我信服的答案:

簡單工廠模式、抽象工廠模式,這兩者到底有什麼區別?

最近,我又系統的複習了一遍設計模式,沒有侷限於設計模式的經典之作《設計模式之禪》,我彷彿開悟了一般,看到了自己多年來想要的答案。

我覺的很多書籍、很多博客,閱讀量多的,點贊量高的,對這個問題的認識和解答普遍都是錯誤的,自己沒整明白,也誤導了很多後來人。

我把自己的理解寫下來,與大家分享探討,希望能夠幫助那些和我有着同樣問題的人,找到答案,讓你們自己信服。

工廠方法模式

設計模式是解決具體問題的通用思路,它的思想是面向對象,無關於任何具體的編程語言。

工廠方法模式,就是把一個靜態方法當作創建對象的工廠,用Java代碼表示:

public class Product {}

public class Factory {
    // 工廠方法
    public static Product createProduct() {
        return new Product();
    }
}

工廠方法模式的特點:不需要創建工廠實例,就有工廠的能力。

上面的工廠方法只支持創建一類對象,如果要創建多類不同的對象,有兩種變通方案:

  1. 工廠方法帶參數,用參數區分要創建的不同類對象
  2. 每類對象分配一個工廠方法

如果是強類型的面嚮對象語言,選擇方案1就會有個約束,不同類型的對象需要有個共同的基類,因爲強類型語言的方法返回值類型是唯一的。

兩種方案的代碼實現:

public class Product {} // 對象基類
public class Product1 extends Product {} // 對象1
public class Product2 extends Product {} // 對象2

public class Factory {
    // 方案1:用工廠方法參數區分不同類型對象
    public static Product createProduct(int type) {
        if (type == 1) return new Product1();
        else if (type == 2) return new Product2();
        else return null;
    }
    // 方案2:一類對象一個工廠方法
    public static Product1 createProduct1() {
        return new Product1();
    }
    public static Product2 createProduct2() {
        return new Product2();
    }
}

網上很多人誤以爲方案2就是簡單工廠模式,這種理解是不對的。

簡單工廠模式

簡單工廠模式是工廠方法模式的升級版。

它倆的不同點:簡單工廠模式需要有工廠實例對象。

它倆的共同點:工廠返回的都是具體的產品對象。

設計模式有六大基本原則,其中有個“接口隔離原則”,在這個原則的約束下,通常還會爲工廠類和產品類定義統一的接口。

下面用Java代碼來展示分析三種業務場景,應用簡單工廠模式,最後一種場景就是很多人以爲的抽象工廠模式,但其實不是!

場景一:一個工廠,生產一種產品

// 產品類
public class Product {}

// 工廠接口
public interface IFactory{
    Product createProduct();
}

// 簡單工廠模式
public class Factory implements IFactory{
    @Override
    public Product createProduct() {
        return new Product();
    }
}

這段代碼看着規範,但也冗餘,可以考慮用上面說的工廠方法模式簡化它。

場景二:多個工廠,生產同種類型不同型號的產品

// 產品
public interface Product {}
public class Product1 implements Product {}
public class Product2 implements Product {}

// 工廠
public interface IFactory {
    Product createProduct();
}
public class Factory1 implements IFactory {
    @Override
    public Product createProduct() {
        return new Product1();
    }
}
public class Factory2 implements IFactory {
    @Override
    public Product createProduct() {
        return new Product2();
    }
}

這樣的代碼範式,沒有異議,所有人都認可它是簡單工廠模式。

場景三:多個工廠,生產不同類型不同型號的產品

這個場景下,產品就拿手機、電腦舉例,兩個工廠分別是華爲工廠、小米工廠,分別生產:華爲手機、華爲電腦、小米手機、小米電腦。

// 產品接口
public interface Mobile {}
public interface Computer {}

// 手機產品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}

// 電腦產品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}

// 工廠接口
public interface IFactory {
    Mobile createMobile();
    Computer createComputer();
}
// 華爲工廠
public class HuaWeiFactory implements IFactory {
    @Override
    public Mobile createMobile() { return new HuaWeiMobile(); }
    @Override
    public Computer createComputer() { return new HuaWeiComputer(); }
}

// 小米工廠
public class XiaoMiFactory implements IFactory {
    @Override
    public Mobile createMobile() { return new XiaoMiMobile(); }
    @Override
    public Computer createComputer() { return new XiaoMiComputer(); }
}

很多人會認爲這種實現就是抽象工廠模式,理由大概都是從兩個方面論證:

  1. 不同的產品族概念:華爲產品族、小米產品族
  2. 不同的維度看產品:從功能角度分手機、電腦,從商家角度分華爲、小米

其實,這兩方面的理由表達的是同一個意思,無論哪個,都不成立。因爲這都是從人的主觀意識出發,站在不同視角看產品分類罷了。這樣的理由根本就沒有涉及到設計模式的核心要點。

試想一下:難道大名鼎鼎的簡單工廠模式,就是工廠少一點,產品類型少一點這麼簡單的嗎?這就是我多年的疑惑。

場景一到場景三的演變,工廠多了,產品多了,代碼複雜了,但工廠模式的核心是沒有變化的:

  1. 工廠本身有實例化的對象
  2. 工廠創建的都是具體產品

具體產品指的是MobileComputer的子類實例化對象,被工廠用一個個的new關鍵字生產出來。

抽象產品指的是MobileComputer本身的產品概念,它是抽象工廠模式的核心要點。

抽象工廠模式

抽象工廠模式的核心要點是:

  1. 工廠具有實例化對象
  2. 工廠生產抽象產品

怎麼理解“工廠生產抽象產品”這句話呢?

還拿上面的場景三舉例,意思就是:一個工廠生產出來的手機可能是華爲的,也可能是小米的;同理,生產出來的電腦可能是華爲的,也可能是小米的。

它和簡單工廠模式的區別是:簡單工廠生產的產品具有確定性,抽象工廠生產的產品具有不確定性。

這個不確定性就是抽象工廠的含義!工廠生產出來的產品,我不確定它是華爲手機還是小米手機,但我知道它是個手機。

分析到這裏,代碼實現就是多種多樣的,總體上,應該類似於這樣:

public interface Product {}

// 產品接口
public interface Mobile extends Product{}
public interface Computer extends Product{}

// 手機產品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}

// 電腦產品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}

// 工廠接口
public interface IFactory {
    Mobile createMobile();
    Computer createComputer();
}

// 沒有具體的華爲工廠、小米工廠,只有一個生產手機、電腦的工廠
public class Factory implements IFactory {
    private Class<? extends Product> mobileClass;
    private Class<? extends Product> computerClass;
    
    public Factory(Class<? extends Product> mobileClass, Class<? extends Product> computerClass) {
        this.mobileClass = mobileClass;
        this.computerClass = computerClass;
    }
    @Override
    public Mobile createMobile() {
        try {
            return (Mobile) mobileClass.newInstance();
        } catch (Exception e) {
            return null;
        }
    }
    @Override
    public Computer createComputer() {
        try {
            return (Computer) computerClass.newInstance();
        } catch (Exception e) {
            return null;
        }
    }
}

其實,這個例子和這段代碼還不足以完全表現出抽象工廠模式的價值。

抽象工廠的重點是對工廠進行高度抽象化,避免自身成爲一個具體的工廠。

抽象化不應該涉及具體,但它應該能夠表達邏輯!

所以,抽象工廠解決的問題應該是具體產品的生產邏輯,也就是說,抽象工廠負責實現一類產品的生產邏輯,但是不負責具體產品的生產。

手機、電腦,都是由很多的零部件組裝出來的,可以設計一個抽象工廠,負責組裝再加工的流程,具體的零部件去其它地方“採購”,把所有的零部件扔到抽象工廠裏面,工廠就能給我們生產出來手機、電腦。

抽象工廠不屬於華爲,也不屬於小米,但它能生產手機、電腦。至於手機、電腦到底是華爲的,還是小米的,取決於工廠裏的零部件從誰家“採購”。

只要組裝再加工的生產流程不變,抽象工廠就可以壟斷所有手機、電腦的生產市場,而不用關心具體是誰家的品牌。

假如現在新增需求,我要生產蘋果的手機、電腦,怎麼辦呢?好辦,抽象工廠不變,把零部件的“採購”渠道換成蘋果的,就能生產出蘋果的手機、電腦。

看到沒,這纔是抽象工廠模式真正的威力所在!

總結

總體而言,工廠模式就兩個核心要點:

  1. 有沒有具體的工廠實例
  2. 生產的產品是具體的,還是抽象的

有工廠實例,生產抽象產品,就是抽象工廠模式

有工廠實例,生產具體產品,就是簡單工廠模式

沒有工廠實例,生產具體產品,就是工廠方法模式

沒有工廠實例,生產抽象產品,也是工廠方法模式

如果按這樣的劃分規則,工廠模式是不是就應該有四種了呢?

再來看看最開始的問題:簡單工廠模式、抽象工廠模式,這兩者到底有什麼區別?

我想答案應該是這樣的:簡單工廠生產具體產品,新增一種產品,就需要新增一個工廠;抽象工廠生產抽象產品,新增一種產品,不需要新增工廠。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章