---
title: 'Factory 工廠方法模式'
disqus: kyleAlien
---
Factory 工廠方法模式
===
## Overview of Content
如有引用參考請詳註出處,感謝 :smile:
> **定義一個用於建立物件的介面,讓子類別決定實例化哪個類別**,讓子類返回實例類別讓使用者使用
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
:::
[TOC]
## Factory 使用場景
需要特別處的初始化物件的地方 **(複雜物件),都可以使用**,用 new 就可以產生物件的地方則不需要特別使用
> 把生產過程掩飾在工廠中,**使用者不需要知道詳細過程 (隱藏細節)**
1. 替代 `new` 關鍵字(雖說該模式可以替代 new 關鍵字,但在設計時仍請注意複雜度,否則會造成設計過度,失去應有的可維護性)
2. 靈活、可拓展性的框架
3. 鬆耦合:工廠模式配合介面導向編程,可以達到讓使用者依賴工廠產生的物件,而不必自己創建物件
> 在目標物件、使用者之間創建一個工廠,由工廠來創建、監控物件的生產(隱藏細節)
4. 單元測試:解耦不同類之間的相互依賴關係(由於測試框架的發展,這方面使用已經弱化)
:::success
* 通常可以用來封裝物件建立的細節 並管理、**控制實體(instance)的數量**
> 也就是說有類似 Pool 的功能
:::
:::info
* **工廠設計 vs. 單例設計**
* 工廠設計優點:
可以產出多個產品並進行控管,並且可以依照需要創建不同產品名,**有較高的函數可讀性**
> 單例設計 大多會依照 `getInstance` 取名,可讀性較差
* 相似之處:
它們都是為了創建物件,所以工廠模式中可以用到與單例設計類似的技法
> 像是 lazy singleton, enum object... 等等
:::
### Factory - UML & 定義
* **Factory 定義**:
一個用於創建對象的 interface(Factory),讓子類(ConcreateFactory)來決定要實現哪個類(ConcreateProduct)
> 工廠方法使一個類的實例化時機轉移到子類
:::warning
這時要注意,父類、子類實例化的時機點 (虛擬機會先建構父類,再建構子類),若父類調用子類的時機點不對則會發生 Crash
:::
* **Factory UML 關係**
| 描述 | 類 | 負責範圍 |
| - | -------- | -------- |
| interface、abstract | Factory | 定義要產生的目標,但抽象又依賴於抽象 (**依賴倒置**) |
| - | ConcreateFactory | 具體實現工廠 |
| interface、abstract | Product | 定義抽象產品,**是 Factory 要產生的目標** |
| - | ConcreateProduct | 具體實現產品 |
> 
### Factory 特點
* 工廠模式是 **高內聚** 的一種表現
:::info
* 隱藏了創建對象的細節;**使用者不用負擔細節,過多的細節增加了使用的成本**
:::
* 同時由於工廠並非直接依賴實體的產品類(`Product`),而是依賴抽象產品(典型的解耦框架),所以有優秀的拓展性
* **工廠模式是一個很好的模式,但是再 **多出別的類別時要引入抽象層(抽象化 `Factory`, `Product`)**,會導致程式變複雜,如果是一個簡單的情況就要考量是否使用了**
> 在有可能拓展不同創建類別的情況下再使用會比較好
## Factory 實現
### Factory 標準
1. Product:ITravelInfo 定義了一個抽象產品,它會與 Factory 產生關係
```java=
public interface ITravelInfo {
String getCityName();
String getTravelInfo();
}
```
2. ConcreateProduct:定義產品實現的細節,透過 Factory 不同的組裝,設定不同的數值
```java=
public class TravelInfoImpl implements ITravelInfo {
public String name;
public boolean chineseLang;
public List<String> places = new ArrayList<>();
public TravelInfoImpl(String name, boolean chineseLang) {
this.name = name;
this.chineseLang = chineseLang;
}
@Override
public String getCityName() {
return name;
}
@Override
public String getTravelInfo() {
return places.toString() + ", Chinese lang: " + (chineseLang ? "Yes" : "No");
}
}
```
3. Factory:ICity 定義工廠的核心,它依賴於 ITravelInfo 接口,也就是 Factory 要產生的不同的 ITravelInfo 類
```java=
public interface ICity {
ITravelInfo getTaiwan();
ITravelInfo getJapan();
}
```
4. ConcreateFacotry:實做 ICity 工廠細節,**在這裡加入(隱藏)一些細節,避免讓使用者知道**,當然也可以減輕使用者使用的負擔
```java=
public class CityFactory implements ICity {
@Override
public ITravelInfo getTaiwan() {
TravelInfoImpl travelInfo = new TravelInfoImpl("Taiwan", true);
travelInfo.places.add("taipei");
return travelInfo;
}
@Override
public ITravelInfo getJapan() {
TravelInfoImpl travelInfo = new TravelInfoImpl("Japan", false);
travelInfo.places.add("tokyo");
return travelInfo;
}
}
```
* 使用 Factory 類:
```java=
public class FactoryMain {
public static void main(String[] args) {
ICity iCity = new CityFactory();
ITravelInfo taiwan = iCity.getTaiwan();
print(taiwan);
ITravelInfo japan = iCity.getJapan();
print(japan);
}
public static void print(ITravelInfo travelInfo) {
System.out.println("City: " + travelInfo.getCityName() +
", place: " + travelInfo.getTravelInfo());
}
}
```
:::success
* **符合依賴倒置** (依賴高層模組,而不是低層模組)
:::
**--實做--**
> 
### Factory 變形 - 反射
* 從上面的簡單範例可以看出,決定產出了類別由工廠決定,所以每次都要修改 **工廠(ConcreateFactory)內容才能產出不同類的實例**,使用起來很不方便
> 你可能要新增一個 City 就需要修改 ICity 接口,這樣不符合開閉原則
1. IFactory 修改:ICity 的抽象定義,讓多接收一個 Class 參數(並用泛型限制參數)
```java=
public interface ICity {
ITravelInfo getTravel(Class<? extends ITravelInfo> clz);
}
```
2. ConcreateProduct 修改:為每個 Product 類創建單獨的實做類,並將細節放在實做類上
```java=
public class JapanTravelInfo implements ITravelInfo {
public String name;
public boolean chineseLang;
public List<String> places = new ArrayList<>();
public JapanTravelInfo() {
this.name = "Japan";
this.chineseLang = false;
places.add("Tokyo");
}
@Override
public String getCityName() {
return name;
}
@Override
public String getTravelInfo() {
return places.toString() + ", Chinese lang: " + (chineseLang ? "Yes" : "No");
}
}
public class TaiwanTravelInfo implements ITravelInfo {
public String name;
public boolean chineseLang;
public List<String> places = new ArrayList<>();
public TaiwanTravelInfo() {
this.name = "Taiwan";
this.chineseLang = true;
places.add("Taipei");
}
@Override
public String getCityName() {
return name;
}
@Override
public String getTravelInfo() {
return places.toString() + ", Chinese lang: " + (chineseLang ? "Yes" : "No");
}
}
```
3. ConcreateFactory 修改:利用 class 反射動態創建不同的類
```java=
public class CityFactory implements ICity {
@Override
public ITravelInfo getTravel(Class<? extends ITravelInfo> clz, String name) {
try {
Constructor<? extends ITravelInfo> constructor = clz.getConstructor();
return constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
```
4. 最後修改一下 User 使用的方式即可
```java=
public class FactoryMain {
public static void main(String[] args) {
ICity iCity = new CityFactory();
ITravelInfo taiwan = iCity.getTravel(TaiwanTravelInfo.class);
print(taiwan);
ITravelInfo japan = iCity.getTravel(JapanTravelInfo.class);
print(japan);
}
public static void print(ITravelInfo travelInfo) {
System.out.println("City: " + travelInfo.getCityName() +
", place: " + travelInfo.getTravelInfo());
}
}
```
**--實做--**
> 
* 使用反射的話,使用者就必須知道他自己目標要產生的類,**這就不符合迪米特原則(最少知識原則)**,讓我們來看看 UML 圖
> 
### 靜態工廠
* 靜態工廠又稱為簡單工廠,它 **把原本抽象的 Factory 簡化為實體類**,如下
1. **私有化建構函數**
2. 建立可被靜態呼叫並創建物件的函數
```kotlin=
class CityFactory private constructor() {
companion object {
fun getTravel(clz: Class<out ITravelInfo>): ITravelInfo {
return clz.getConstructor().newInstance()
}
}
}
```
* 使用:高層模組在呼叫時就改成使用靜態類
```kotlin=
fun main() {
var info = CityFactory.getTravel(TaiwanTravelInfo::class.java)
println("${info.cityName} - ${info.travelInfo}")
info = CityFactory.getTravel(JapanTravelInfo::class.java)
println("${info.cityName} - ${info.travelInfo}")
}
```
> 
:::warning
靜態工廠十分好用,但它的缺點也很明顯,就是 Factory 無法拓展
:::
### 多工廠 - 多產品
* 有時候我們在創建(實例化)一個產品時所需要耗費的工相當的多(或是說攏長),把產品的實例化全部寫在一個類中實現實,就會顯的相當難看,這時就可以做出比較針對性的區分
1. **Factory**:**抽象工廠只有一個方法**,讓 Concrete Factory 透過參數,或是其他方式自己決定要生產的實體
```kotlin=
interface ICity {
// 假設之前有很多個方法,現在全部歸納為一個方法
fun getTravel() : ITravelInfo
}
```
2. **ConcreteFactory**:Concrete Factory 與 Concrete Product 對應創建一個實體類,**有多少 Product 就有多少 Factory**
```kotlin=
class TaiwanFactory : ICity {
override fun getTravel(): ITravelInfo {
return TaiwanTravelInfo()
}
}
class JapanFactory : ICity {
override fun getTravel(): ITravelInfo {
return JapanTravelInfo()
}
}
```
:::info
* 從這裡也可以很清楚的看到一個問題,就是類的拓增導致維護出現難度,並要同時考慮 Factory、Product 的關係
> 解決方法:可以透過一個協調類來讓 Concrete Factory 與 Concrete Product 不要直接調用到對方,降低其耦合
:::
### 反射 - 單例工廠
* 可以統一使用反射的方式來取得私有建構函數並創建唯一物件,並將這個唯一物件透過工廠包裝(工廠可以不用是單例),如下範例
1. 創建一個私有類,它代表了一個將要被單例化的對象
```kotlin=
class Travel private constructor() {
fun todo() {
// do something
}
}
```
2. 產品抽象、具體產品:單例產品
```kotlin=
interface ITravel {
fun place(): String
}
// 私有化建構函數
class Travel private constructor(): ITravel {
override fun place(): String {
return "Taiwan"
}
}
```
3. 抽象、具體工廠類:透過 **反射(創建對象)** + **靜態(單例)** 的方式達成與單例相同的效果
> 這裡必須要使用靜態儲存產品才能達到單例的效果
```kotlin=
interface ICityFactory {
val travel : ITravel
}
class SingleFactory: ICityFactory {
companion object {
// 靜態 產品
private val _travel: ITravel
get() {
return Travel::class.java.getConstructor().newInstance()
}
}
override val travel: ITravel
get() = _travel
}
```
:::danger
* 這 **必須要團隊成員共同遵守這個規範**,否則仍可能會創建多個對象(別人也可以用反射自己稿一個新對象出來)
:::
### 覆用工廠
* 在需要時才加載產品,並且加載後將產品緩存,在需要時才取用;以下創建一個類似將產品單例緩存、並懶加載的覆用工廠
> 之所以是單例,是因為 Cache 是靜態的 Field
```kotlin=
class ProductFactory {
companion object {
private val cacheMap = mutableMapOf<Class<*>, ITravel>()
fun createProduct(productClz : Class<*>) : ITravel {
cacheMap[productClz].let {
if (it != null) {
return it
}
val travel = if (productClz == TaiwanTravel::class.java) {
TaiwanTravel()
} else {
JapanTravel()
}
cacheMap[productClz] = travel
}
return cacheMap.getValue(productClz)
}
}
}
```
## Java
在 Java 包裝的數據結構類中,就有包含了工廠模式應用
以 [**List**](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/List.java)、[**Set**](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/Set.java) 來說都繼承於 Collection 接口,而 Collection 繼承 [**Iterable**](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/lang/Iterable.java) 接口,**Iterable 讓使用者一定要返回一個迭代類**
| 代表 | 對象 |
| -------- | -------- |
| Factory | Iterable |
| Product | [**Iterator**](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/Iterator.java),它代表了 Product |
```java=
// Iterable.java
public interface Iterable<T> { // Iterable 接相當於一個抽象工廠
// 必須實做一個迭代類
Iterator<T> iterator();
... 省略 default 方法
}
// --------------------------------------------------------------
// Iterator.java
public interface Iterator<E> { // Iterator 是一個工廠生產的抽象產品 (IProduct)
boolean hasNext();
E next();
... 省略 default 方法
}
```
> 
### [ArrayList](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/ArrayList.java)
* **Factory**:ArrayList 是我們常使用到的數據結構實現類,它內部就有實現 `iterator` 方法,它必須實做了工廠 Iterable 接口
```java=
// ArrayList.java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
... 省略其他方法
public Iterator<E> iterator() {
return new Itr(); // 工廠類,創建一個 Itr 類
}
}
```
* **Product**:Itr 類負責產生迭代 ArrayList 的對象
```java=
// ArrayList.java
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor < limit;
}
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
... 省略部份方法
}
```
* ArrayList UML
> 
### [HashSet](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/HashSet.java)
> Set 接口實現的子類特點是,**禁止內部成員的重複**
* **Factory**:HashSet 內部就有實現 `iterator` 方法,它必須實做了工廠 Iterable 接口
```java=
// HashSet.java
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
// 題外 transient 是禁止反序列化的標誌
private transient java.util.HashMap<E, java.lang.Object> map;
public Iterator<E> iterator() {
return map.keySet().iterator();
}
}
```
* **Product**:HashSet#iterator 迭代是返回 [**HashMap**](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/util/HashMap.java)#keySet#iterator 方法,從這裡可以看出來 iterator 針對不同的結構,由實做類返回不同的處理對象
> 所以每一個數據結構都會實現各自的 `iterator` (抽象產品)
```java=
// HashMap.java
public class HashMap<K, V> extends java.util.AbstractMap<K, V>
implements java.util.Map<K, V>, java.lang.Cloneable, java.io.Serializable {
final class KeySet extends AbstractSet<K> {
public final Iterator<K> iterator() { return new KeyIterator(); }
... 省略部份方法
}
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
... 省略部份
}
}
```
* HashSet UML
> 
## Android source
在 Activity#onCreate 時我們會用 setContent 方法來繪製布局,給予不同 xml 檔案就會設置不同布局,這就是一個工廠模式的實做 (**就像是反射工廠,給予不同 class 最終返回不同類**)
> 
### ActivityThread 呼叫 onCreate
* 請看 [**ActivityThread 分析**](https://hackmd.io/k63B4rCgT0-mzMMtV_iYvQ?view) 篇章
### 加載 XML 布局
* 請看 [**LayoutInflator 分析**](https://hackmd.io/EPfgb14TS-Kcd-Pv7tDZ-w?view) 篇章
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)
* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)
* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::
### 創建模式 - Creation Patterns
* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)
* **創建模式 - `Creation Patterns`**:
創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
:::success
* [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
* [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
* [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
* [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
* [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)
* [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)
* [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
:::
### 行為模式 - Behavioral Patterns
* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
* **行為模式 - `Behavioral Patterns`**:
行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
:::warning
* [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)
* [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
* [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)
* [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)
* [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)
* [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
* [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)
* [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)
* [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)
* [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)
* [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::
### 結構模式 - Structural Patterns
* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)
* **結構模式 - `Structural Patterns`**:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
:::danger
* [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)
* [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)
* [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)
* [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)
* [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)
* [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
:::
## Appendix & FAQ
:::info
:::
###### tags: `Java 設計模式` `基礎進階`標題