|
| 1 | +--- |
| 2 | +title: Abstract Factory |
| 3 | +shortTitle: Abstract Factory |
| 4 | +category: Structural |
| 5 | +language: zh-TW |
| 6 | +tag: |
| 7 | + - Extensibility |
| 8 | +--- |
| 9 | + |
| 10 | +## 別稱 |
| 11 | + |
| 12 | +* Kit (套件) |
| 13 | + |
| 14 | +## 抽象工廠設計模式的意圖 |
| 15 | + |
| 16 | +Java 中的抽象工廠模式 (Abstract Factory pattern) 提供一個介面,用於創建一系列相關或相依的物件家族,而無需指定其具體類別,從而增強了軟體設計的模組化與靈活性。 |
| 17 | + |
| 18 | +## 抽象工廠模式的詳細解釋與真實世界範例 |
| 19 | + |
| 20 | +真實世界範例 |
| 21 | + |
| 22 | +> 想像一家傢俱公司,它使用 Java 中的抽象工廠模式來生產多種風格的傢俱:現代風、維多利亞風和鄉村風。每種風格都包含椅子、桌子和沙發等產品。為了確保每種風格內部的一致性,該公司使用了抽象工廠模式。 |
| 23 | +> |
| 24 | +> 在這個情境中,抽象工廠是一個用於創建相關傢俱物件家族(椅子、桌子、沙發)的介面。每個具體工廠(ModernFurnitureFactory、VictorianFurnitureFactory、RusticFurnitureFactory)都實作了這個抽象工廠介面,並創建一組符合特定風格的產品。如此一來,客戶端就可以創建一整套現代或維多利亞風格的傢俱,而無需擔心其實例化的細節。這不僅保持了風格的一致性,也讓更換不同風格的傢俱變得輕而易舉。 |
| 25 | +
|
| 26 | +簡單來說 |
| 27 | + |
| 28 | +> 工廠中的工廠;一個將個別但相關/相依的工廠群組在一起,而無需指定其具體類別的工廠。 |
| 29 | +
|
| 30 | +維基百科說 |
| 31 | + |
| 32 | +> 抽象工廠模式提供了一種方法,可以封裝一組具有共通主題的獨立工廠,而無需指定它們的具體類別。 |
| 33 | +
|
| 34 | +類別圖 |
| 35 | + |
| 36 | + |
| 37 | + |
| 38 | +## Java 中的抽象工廠程式碼範例 |
| 39 | + |
| 40 | +為了使用抽象工廠模式來創建一個王國,我們需要具有共通主題的物件。精靈王國需要精靈國王、精靈城堡和精靈軍隊;而獸人王國則需要獸人國王、獸人城堡和獸人軍隊。王國中的這些物件之間存在著相依性。 |
| 41 | + |
| 42 | +將上述的王國範例轉換為程式碼。首先,我們為王國中的物件定義一些介面和實作。 |
| 43 | + |
| 44 | +```java |
| 45 | +public interface Castle { |
| 46 | + String getDescription(); |
| 47 | +} |
| 48 | + |
| 49 | +public interface King { |
| 50 | + String getDescription(); |
| 51 | +} |
| 52 | + |
| 53 | +public interface Army { |
| 54 | + String getDescription(); |
| 55 | +} |
| 56 | + |
| 57 | +// Elven implementations -> |
| 58 | +public class ElfCastle implements Castle { |
| 59 | + static final String DESCRIPTION = "This is the elven castle!"; |
| 60 | + |
| 61 | + @Override |
| 62 | + public String getDescription() { |
| 63 | + return DESCRIPTION; |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +public class ElfKing implements King { |
| 68 | + static final String DESCRIPTION = "This is the elven king!"; |
| 69 | + |
| 70 | + @Override |
| 71 | + public String getDescription() { |
| 72 | + return DESCRIPTION; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +public class ElfArmy implements Army { |
| 77 | + static final String DESCRIPTION = "This is the elven Army!"; |
| 78 | + |
| 79 | + @Override |
| 80 | + public String getDescription() { |
| 81 | + return DESCRIPTION; |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +// Orcish implementations similarly -> ... |
| 86 | +``` |
| 87 | + |
| 88 | +接著,我們為王國工廠定義抽象和實作。 |
| 89 | + |
| 90 | +```java |
| 91 | +public interface KingdomFactory { |
| 92 | + Castle createCastle(); |
| 93 | + |
| 94 | + King createKing(); |
| 95 | + |
| 96 | + Army createArmy(); |
| 97 | +} |
| 98 | + |
| 99 | +public class ElfKingdomFactory implements KingdomFactory { |
| 100 | + |
| 101 | + @Override |
| 102 | + public Castle createCastle() { |
| 103 | + return new ElfCastle(); |
| 104 | + } |
| 105 | + |
| 106 | + @Override |
| 107 | + public King createKing() { |
| 108 | + return new ElfKing(); |
| 109 | + } |
| 110 | + |
| 111 | + @Override |
| 112 | + public Army createArmy() { |
| 113 | + return new ElfArmy(); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +// 獸人 (Orcish) 的實作也類似 -> ... |
| 118 | +``` |
| 119 | +現在,我們可以為我們不同的王國工廠設計一個工廠。在這個範例中,我們創建了 `FactoryMaker`,它負責返回 `ElfKingdomFactory` 或 O`OrcKingdomFactory` 的實例。客戶端可以使用 `FactoryMaker` 來創建所需的具體工廠,而該工廠又會生產出不同的具體物件(繼承自 `Army`, `King`, `Castle`)。在這個範例中,我們還使用了一個 enum 來參數化客戶端將請求哪種類型的王國工廠。 |
| 120 | + |
| 121 | +```java |
| 122 | +public static class FactoryMaker { |
| 123 | + |
| 124 | + public enum KingdomType { |
| 125 | + ELF, ORC |
| 126 | + } |
| 127 | + |
| 128 | + public static KingdomFactory makeFactory(KingdomType type) { |
| 129 | + return switch (type) { |
| 130 | + case ELF -> new ElfKingdomFactory(); |
| 131 | + case ORC -> new OrcKingdomFactory(); |
| 132 | + }; |
| 133 | + } |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +這是我們範例應用程式的主函式: |
| 138 | + |
| 139 | +```java |
| 140 | +LOGGER.info("elf kingdom"); |
| 141 | +createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); |
| 142 | +LOGGER.info(kingdom.getArmy().getDescription()); |
| 143 | +LOGGER.info(kingdom.getCastle().getDescription()); |
| 144 | +LOGGER.info(kingdom.getKing().getDescription()); |
| 145 | + |
| 146 | +LOGGER.info("orc kingdom"); |
| 147 | +createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); |
| 148 | +LOGGER.info(kingdom.getArmy().getDescription()); |
| 149 | +LOGGER.info(kingdom.getCastle().getDescription()); |
| 150 | +LOGGER.info(kingdom.getKing().getDescription()); |
| 151 | +``` |
| 152 | + |
| 153 | +程式輸出: |
| 154 | + |
| 155 | +``` |
| 156 | +07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom |
| 157 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army! |
| 158 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle! |
| 159 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king! |
| 160 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom |
| 161 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army! |
| 162 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle! |
| 163 | +07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king! |
| 164 | +``` |
| 165 | + |
| 166 | +## 在 Java 中何時使用抽象工廠模式 |
| 167 | +在 Java 中,當您遇到以下情況時,請使用抽象工廠模式:: |
| 168 | + |
| 169 | +* 系統應與其產品的創建、組合和表示方式無關。 |
| 170 | + |
| 171 | +* 您需要用多個產品家族中的其中一個來配置系統。 |
| 172 | + |
| 173 | +* 必須同時使用一系列相關的產品物件,以強制保持一致性。 |
| 174 | + |
| 175 | +* 您希望提供一個產品的類別庫,只暴露它們的介面,而不是它們的實作。 |
| 176 | + |
| 177 | +* 相依物件的生命週期比消費者的生命週期短。 |
| 178 | + |
| 179 | +* 相依物件需要使用執行時期的值或參數來建構。 |
| 180 | + |
| 181 | +* 您需要在執行時期從一個家族中選擇要使用的產品。 |
| 182 | + |
| 183 | +* 新增新產品或家族時,不應要求修改現有程式碼。 |
| 184 | + |
| 185 | +## Java 抽象工廠模式教學 |
| 186 | + |
| 187 | +* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java) |
| 188 | +* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory) |
| 189 | + |
| 190 | +## 抽象工廠模式的優點與權衡 |
| 191 | + |
| 192 | +優點: |
| 193 | + |
| 194 | +* 靈活性:無需修改程式碼即可輕鬆切換產品家族。 |
| 195 | + |
| 196 | +* 解耦:客戶端程式碼只與抽象介面互動,提升了可移植性和可維護性。 |
| 197 | + |
| 198 | +* 可重用性:抽象工廠和產品有助於跨專案的元件重用。 |
| 199 | + |
| 200 | +* 可維護性:對個別產品家族的變更是局部的,簡化了更新過程。 |
| 201 | + |
| 202 | +權衡: |
| 203 | + |
| 204 | +* 複雜性:定義抽象介面和具體工廠會增加初期的開發開銷。 |
| 205 | + |
| 206 | +* 間接性:客戶端程式碼透過工廠間接與產品互動,可能會降低透明度。 |
| 207 | + |
| 208 | +## Java 中抽象工廠模式的實際應用 |
| 209 | + |
| 210 | +* Java Swing 的 `LookAndFeel` 類別,用於提供不同的外觀與感覺選項。 |
| 211 | + |
| 212 | +* Java Abstract Window Toolkit (AWT) 中的各種實作,用於創建不同的 GUI 元件。 |
| 213 | + |
| 214 | +* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) |
| 215 | +* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--) |
| 216 | +* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--) |
| 217 | + |
| 218 | +## 相關的 Java 設計模式 |
| 219 | + |
| 220 | +* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): 抽象工廠使用工廠方法來創建產品。 |
| 221 | +* [Singleton](https://java-design-patterns.com/patterns/singleton/): 抽象工廠類別通常被實作為單例 |
| 222 | +* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): 與抽象工廠類似,但更專注於以靈活的方式配置和管理一組相關物件。 |
| 223 | + |
| 224 | +## 參考資料與致謝 |
| 225 | + |
| 226 | +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) |
| 227 | +* [Design Patterns in Java](https://amzn.to/3Syw0vC) |
| 228 | +* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq) |
| 229 | +* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U) |
0 commit comments