設計模式之工廠模式!深入解析簡單工廠模式,工廠方法模式和抽象工廠模式
工廠模式
- 創建型模式:
- 對類的實例化過程進行抽象,能夠將對象的創建和對象的使用分離開來
- 為了使得軟體的結構更加清晰,外界對於這些對象使用只需要知道共同的介面,而不在意具體實現的細節,這樣使得整個系統更加符合單一職責的原則
- 創建型模式隱藏了類的實例的創建細節,通過隱藏對象創建和組合過程從而使得整個系統相互獨立的目的
- 創建型模式在創建什麼,由誰創建,何時創建更加靈活
- 工廠模式是一個重要的創建型模式,主要功能就是實例化對象
- 工廠模式: 負責將有共同介面的類實例化
- 主要解決介面選擇問題
- 在不同的條件下需要創建不同的實例時使用
- 工廠模式是一種創建型模式,提供了創建對象的最佳方式
- 使用工廠模式創建對象不會對客戶端暴露創建邏輯,並且使用一個共同的介面來指向新創建的對象
- 工廠模式在子類中實現工廠介面,創建過程在子類中執行
- 工廠模式的分類:
- 簡單工廠模式Simple Factory
- 工廠方法模式Factory Method
- 抽象工廠模式Abstract Factory
- 工廠模式優點:
- 可以使得程式碼結構清晰,有效地封裝變化
- 對調用者屏蔽具體的產品類
- 降低程式碼的耦合度
- 工廠模式的使用場景:
- 在任何需要生成複雜對象的地方,都可以使用工廠方法模式.只有複雜的對象才適用於工廠方法模式.對於簡單的只要通過new就可以完成創建的對象,無需使用工廠模式.如果簡單對象使用工廠模式,需要引入一個工廠類,增加系統的複雜度
- 工廠模式是一種典型的解耦模式,當類之間需要增加依賴關係時,可以使用工廠模式降低系統之間的耦合度
- 工廠模式是依靠抽象架構的,將實例化的任務交給子類實現,擴展性好.當系統需要較好的擴展性時,可以使用工廠模式,不同的產品使用不同的工廠來實現組裝
簡單工廠模式
- 簡單工廠模式Simple Factory Pattern:
- 定義一個類用於負責創建其餘類的實例,根據自變數的不同返回不同類的實例,被創建的實例通常都有一個共同的父類
- 簡單工廠模式中用於創建實例的方法時靜態static方法,因此又稱作是靜態工廠方法模式
- 簡單工廠模式的角色:
- 工廠類Factory : 簡單工廠模式核心類. 負責創建所有產品的內部邏輯,工廠類可以被外部調用,創建所需對象
- 抽象產品類Product : 工廠類創建的所有對象的父類,封裝產品的共有方法.提高系統的靈活性.使得工廠類只需要定義一個通用的工廠方法,因為所有創建的具體產品都是這個子類對象
- 具體產品類ConcorrectProduct: 所有被創建的對象都是這個類的具體實例,需要實現抽象產品中聲明的抽象方法

- 簡單工廠模式程式碼實現
- 簡單工廠模式優點:
- 簡單工廠模式提供了專門的類用於創建對象,實現了對責任的分割. 工廠類Factory中含有必要的判斷邏輯,決定創建具體產品類ConcreteProduct的實例,客戶端只需要消費產品
- 客戶端不需要知道需要創建的具體產品類ConcreteProduct的類名,只需要知道具體產品類ConcreteProduct對應的參數即可
- 通過引入配置文件,可以在不修改客戶端的情況下修改和增加新的產品類ConcreteProduct, 提高了系統的靈活性
- 簡單工廠模式缺點:
- 工廠類Factory集中了所有產品的創建邏輯,如果發生異常,整個系統都會發生故障
- 簡單工廠模式中增加了系統中類的個數,增加了系統的複雜度和理解難度
- 簡單工廠模式中如果需要添加新的產品需要修改工廠邏輯,違背了開閉原則,不利於系統的擴展和維護
- 簡單工廠模式使用了靜態方法,無法形成基於繼承的等級結構
- 簡單工廠模式的使用場景:
- 工廠類中負責創建的對象比較少時
- 客戶端只需要知道傳入工廠類的參數,不關心創建對象的參數
簡單工廠類的實現方式
直接傳入判斷參數key
public class Factory {
public static Product produce(String concreteProductType) {
switch (concreteProductType) {
case "A" :
return new ConcreteProductA();
break;
case "B" :
return new ConcreteProductB();
break;
default :
throw new Exception("沒有對應的產品類型");
break;
}
}
}
- 問題:
- 如果新增產品類,需要在工廠類中新增case
- 違背了開閉原則,這種方法是不建議使用的
利用反射
public Class Factory {
public static Product produce(String concreteProductClassPathName) throw Exception {
try {
Product product = (Product)Class.forName(concreteProductClassPathName).newInstance();
return product;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
throw new Exception("沒有對應的產品");
}
}
- 問題:
- 如果新增產品類,需要傳入具體產品類的類路徑名稱
- 可以通過配置文件進行優化,將具體產品類的類路徑名稱配置在properties文件中,通過載入配置文件將類路徑名稱傳入工廠類的方法中
- 這樣新增產品類,只需要修改配置文件即可
反射和配置文件結合
A=com.oxford.factory.simple.ConcreteProductA
B= com.oxford.factory.simple.ConcreteProductB
- PropertyReader: 增加一個配置文件讀取類,將配置文件的資訊讀取到Map中
public Class PropertyReader {
public static Map<String, String> property = new HashMap<>();
public Map<String, String> readProperty(String fileName) {
Properties properties = new Properties();
InputStream input = getClass.getResourceAsStream(fileName);
try {
pro.load(input);
Iterator<String> iterator = pro.StringPropertyNames().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = properties.getProperty(key);
map.put(key, value);
}
input.close();
} catch (IOException e) {
e.printStacTrace();
}
return map;
}
}
public Class Factory {
public static Product produce(String concreteProductType) throws Exception {
PropertyReader reader = new PropertyReder();
Map<String, String> property = reader.readProperty("property.properties");
try {
Product product = (Product)Class.forName(property.get(concreteProductType)).newInstance();
return product;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
throw new Exception("沒有對應的產品");
}
}
- 問題:
- 每次調用的方法時,都要解析配置文件,增加系統開銷
- 可以在文件讀取類在程式啟動時就載入,就可以不用在每次調用時解析配置文件了
簡單工廠模式總結
- 工廠類是整個簡單工廠模式的關鍵:
- 工廠類中包含必要的判斷邏輯,根據給定的參數來決定創建哪一個具體的產品類
- 通過使用工廠類,客戶端只需要消費具體產品即可,而不用關注具體產品對象的創建過程
- 通過使用簡單工廠模式明確了各個類的職責,有利於整個軟體體系結構的優化
- 工廠類中集中了所有具體對象的創建邏輯,違背了高內聚的責任分配原則.這樣工廠類中創建的類只能是事先考慮到的,如果需要添加新的類,則需要修改工廠類的邏輯,這違背了開閉原則
- 當系統中的具體產品類不斷增多時,就會出現要求工廠類根據不同的條件創建不同的實例的需求.這種對條件的判斷和對具體產品類型的判斷交錯在一起,不利於對系統的擴展和維護.這樣的問題可以通過使用工廠方法模式進行優化
工廠方法模式
- 工廠方法模式Factory Method Pattern:
- 定義一個創建對象的介面,通過實現這個介面的類來決定實例化具體的類
- 工廠方法模式讓具體的類的實例化延遲到子類中進行
- 工廠方法模式的角色:
- 工廠類Factory:
- 工廠方法介面,通常返回一個抽象產品類型Product的實例對象
- 這個類是工廠方法模式的核心,與客戶端程式無關. 任何在模式中創建的具體產品都需要實現這個介面
- 工廠實現類ConcreteFactory:
- 工廠類介面實現,覆寫工廠類Factory定義的工廠方法,返回具體產品類ConcreteProduct的抽象產品類型Product類型的實例
- 工廠實現類ConcreteFactory中包含與客戶端密切相關的邏輯,並且被客戶端調用來創建具體的產品實例
- 抽象產品類Product:
- 工廠方法模式創建的具體產品類的父類,定義類具體產品中共有的方法
- 具體產品類ConcreteProduct:
- 具體產品實現類,實現了抽象產品類Product中的方法
- 工廠模式創建的每一個對象都是具體產品類ConcreteProduct的一個實例

- 工廠方法模式程式碼實現
- 工廠方法模式優點:
- 在工廠方法模式中,通過工廠方法來創建客戶端需要的產品ConcreteProduct, 用戶只需要關心需要具體產品ConcreteProduct對應的工廠實現ConcreteFactory. 不需要關心具體產品ConcreteProduct的創建細節和具體產品類ConcreteProduct的名稱
- 基於工廠類Factory和抽象產品類Product的多態性設計是工廠方法模式的關鍵. 這樣工廠類Factory可以自主確定需要創建何種產品ConcreteProduct的對象,並且創建具體產品ConcreteProduct對象的具體實現封裝在具體工廠ConcreteFactory的內部. 具體工廠類ConcreteFactory都具有同一父類介面Factory, 因此工廠方法模式又稱為多態工廠模式
- 工廠方法模式完全符合開閉原則,有利於系統的擴展和維護. 工廠方法模式在系統中添加新產品時,只需要添加一個具體工廠類ConcreteFactory和具體產品類ConcreteProduct即可
- 工廠方法模式缺點:
- 工廠模式在系統中添加新產品時,需要添加具體產品類ConcreteProduct和具體工廠類ConcreteFactory, 系統中類的個數成對增加,一定程度上增加了系統複雜度以及系統編譯運行的開銷
- 工廠方法模式的使用場景:
- 一個類不需要知道所需要的對象的類: 工廠方法模式中,客戶端不知道具體產品類的類名,只知道具體的產品對象由哪一個具體工廠實現來創建. 這時,客戶端需要知道創建具體產品的具體工廠實現類
- 一個類通過子類來指定創建哪一個對象: 工廠方法模式中,工廠類中只需要一個創建產品的的介面,由子類來確定具體要創建的對象,通過利用多態和里氏代換原則,可以在程式運行時,通過子類對象覆蓋父類對象,從而使得系統得以擴展
- 通過將創建具體產品的任務交由工廠類的具體工廠實現來完成,客戶端不需要關心具體產品類的創建, 需要的時候動態指定產品的具體工廠實現即可. 可以將具體工廠類的類名存儲在配置文件或者資料庫中
- 工廠方法模式的使用場景示例:
- 日誌記錄器: 日誌可以記錄到本地磁碟,系統事件,遠程伺服器等等,用戶可以選擇日誌記錄的位置
- 資料庫訪問: 當用戶不知道最後系統採用哪一類資料庫時,以及資料庫可能會發生變化時
- 伺服器框架設計: 設計一個連接伺服器的框架時,可能會用到三個協議POP3, IMAP, HTTP時,可以將三個協議看作是具體產品類,使用工廠方法模式實現
工廠方法模式總結
- 工廠方法模式是簡單工廠模式的抽象和拓展,通過多態,工廠方法模式保持了簡單工廠模式的優點,改善了簡單工廠模式的缺點
- 工廠方法模式中,核心的工廠類僅僅給出具體工廠實現必須實現的介面,不再負責具體產品的創建,具體產品的創建交由具體的工廠實現完成.這樣使得系統可以在不修改核心的工廠類時進行具體產品實現的擴展
- 優點:
- 客戶端想要創建對象,只需要知道具體工廠實現即可
- 系統的擴展性高,如果新增產品,只需要一個具體工廠實現類和具體產品類即可,符合開閉原則
- 對客戶端隱藏了具體實現,客戶端只需要關心具體的工廠實現即可
- 缺點:
- 每次增加一個產品,都需要增加一個具體工廠實現類和具體產品類,這樣使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,也增加了系統具體類的依賴,同時類的增加也增加了編譯和運行時的系統開銷
抽象工廠模式
- 抽象工廠模式Abstract Factory Pattern:
- 提供介面或者抽象類用於創建一組相關或者相互依賴的具體產品對象,不需要指定具體的類
- 抽象工廠模式的基本思想:
- 工廠方法模式通過引入工廠等級結構,解決了簡單工廠模式中工廠類的職責過大的問題.但是由於工廠方法模式中每個工廠只生產一類產品,這樣可能會導致存在大量的工廠類的問題,這樣會增加系統的開銷
- 可以將一些相關的產品組成一個產品族,由同一個工廠來統一生產
- 產品族: 位於不同產品等級結構中功能相關聯的產品組成的家族
- 抽象工廠模式與工廠方法模式區別:
- 抽象工廠模式:
- 抽象工廠模式是針對多個產品的等級結構
- 抽象工廠模式的具體產品實現或者繼承於不同的介面或者抽象類
- 工廠方法模式:
- 工廠方法模式是針對一個產品的等級結構
- 工廠方法模式的具體產品實現或者繼承於同一個介面或者抽象類
- 抽象工廠模式的角色:
- 抽象工廠類AbstractFactory: 抽象工廠模式的核心,與應用的業務邏輯無關. 通常使用介面或者抽象類實現,所有具體工廠類ConcreteFactory必須實現介面或者抽象類
- 具體工廠類ConcreteFactory: 實現工廠定義的方法,包含創建具體產品實例的業務邏輯
- 抽象產品AbstractProduct: 定義一類產品對象的介面或者抽象類,這個類是工廠方法模式創建的對象的父類
- 具體產品ConcreteProduct: 實現業務邏輯的具體的產品,抽象工廠中創建的每一個產品對象都是一個具體產品的實例

- 抽象工廠模式程式碼實現
- 抽象工廠模式優點:
- 抽象工廠模式分隔了具體類的生成,客戶端不需要知道具體創建的類
- 當一個產品族中的對象設計成一起工作時,能夠保證客戶端只使用同一個產品族的對象
- 抽象工廠模式缺點:
- 如果添加新的產品對象時,難以對產品等級結構進行擴展
- 抽象工廠模式的使用場景:
- 一個系統中不依賴於產品類的具體實例的創建,組合以及表達的細節
- 系統中有多個產品族,並且每次只使用其中一種產品族
- 同一個產品族的產品會在一起使用
- 系統中提供一個產品類的庫,客戶端不依賴具體產品的實現,所有產品以同樣的介面出現
- 系統結構穩定,不會頻繁增加產品族
- 抽象工廠模式問題: 開閉原則的傾斜性
- 抽象工廠模式中開閉原則的傾斜性是指在抽象工廠模式中,增加新的產品方便,但是增加新的產品族很麻煩
- 開閉原則要求系統對修改關閉,對擴展開放.對於多個產品族和多個產品等級結構的系統的功能擴展增強包括:
- 增加產品: 對於增加新的產品,只需要增加一個對應的具體工廠即可,不需要修改已有的程式碼
- 增加產品族: 對於增加新的產品族的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品族產品的方法,這是違背了開閉原則的
- 因為抽象工廠模式存在開閉原則的傾斜性,因此要求在系統設計之初就要考慮整個系統的所有產品族,不會在設計完成之後再增加新的產品族,也不會刪除已有的產品族.否則會導致系統有大量的修改,難以維護
抽象工廠模式總結
- 抽象工廠模式是工廠方法模式的進一步拓展,提供了更為強大的工廠類用於系統的擴展
- 抽象工廠模式分隔了具體類的生成,客戶端無需了解產品的創建過程,這樣使得很容易切換具體工廠.因為所有的具體工廠都實現了抽象工廠中定義的公共介面,因此只需要改變具體工廠的實例,就可以改變整個系統的行為
- 當一個產品族中的多個產品對象一起工作時,可以保證客戶端始終只使用同一個產品族中的對象
- 增加新的產品很方便,無需修改已有的系統,符合開閉原則
- 但是增加系統新的產品族的產品等級結構很麻煩,需要對原有的系統大量修改,甚至需要修改抽象層程式碼,違背了開閉原則