--- 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 | 具體實現產品 | > ![](https://i.imgur.com/CisjBGg.png) ### 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 * **符合依賴倒置** (依賴高層模組,而不是低層模組) ::: **--實做--** > ![](https://i.imgur.com/G6oemoD.png) ### 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()); } } ``` **--實做--** > ![](https://i.imgur.com/Z27poiu.png) * 使用反射的話,使用者就必須知道他自己目標要產生的類,**這就不符合迪米特原則(最少知識原則)**,讓我們來看看 UML 圖 > ![](https://i.imgur.com/B1JAxzw.png) ### 靜態工廠 * 靜態工廠又稱為簡單工廠,它 **把原本抽象的 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}") } ``` > ![](https://i.imgur.com/sYTVpIN.png) :::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 方法 } ``` > ![](https://i.imgur.com/NJfzjcS.png) ### [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 > ![](https://i.imgur.com/QgiUgUJ.png) ### [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 > ![](https://i.imgur.com/gTm96SV.png) ## Android source 在 Activity#onCreate 時我們會用 setContent 方法來繪製布局,給予不同 xml 檔案就會設置不同布局,這就是一個工廠模式的實做 (**就像是反射工廠,給予不同 class 最終返回不同類**) > ![](https://i.imgur.com/e2PVGBY.png) ### 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 設計模式` `基礎進階`標題