工廠模式屬於創建型模式,核心思想是將物件實例化邏輯封裝在工廠中,客戶端僅依賴抽象介面(介面或抽象類),不依賴具體實現類,實現「開閉原則」——對擴展開放,對修改關閉。
簡單工廠(靜態工廠方法)
│
├─ 問題:違反開閉原則,新增產品需修改工廠類
│
▼
工廠方法(多態工廠)
│
├─ 解決:每個產品對應一個具體工廠,新增產品只需新增工廠類
├─ 問題:類別爆炸,單一產品族場景下過度設計
│
▼
抽象工廠(產品族工廠)
│
└─ 解決:一個工廠創建多個相關/依賴的產品族,約束產品間一致性
Fruit)Apple、Grape)FruitFactory)// 抽象產品
public interface Fruit {
void fruitType();
}
// 具體產品
public class Apple implements Fruit {
public void fruitType() { System.out.println("這是蘋果"); }
}
public class Grape implements Fruit {
public void fruitType() { System.out.println("這是葡萄"); }
}
// 簡單工廠
public class FruitFactory {
public static Fruit createFruit(String type) {
if ("apple".equalsIgnoreCase(type)) return new Apple();
if ("grape".equalsIgnoreCase(type)) return new Grape();
throw new IllegalArgumentException("未知水果類型: " + type);
}
}
// 客戶端
Fruit fruit = FruitFactory.createFruit("apple");
fruit.fruitType();
| 優點 | 缺點 |
|---|---|
| 客戶端與具體類解耦 | 工廠類集中所有創建邏輯,違反單一職責 |
| 簡單易懂、易實作 | 新增產品必須修改工廠原始碼(違反開閉原則) |
| 靜態方法調用方便 | 難以擴展、測試困難(靜態方法難 Mock) |
Factory 介面)AppleFactory、GrapeFactory)// 工廠介面
public interface Factory {
Fruit createFruit();
}
// 具體工廠:一個工廠對應一個產品
public class AppleFactory implements Factory {
public Fruit createFruit() { return new Apple(); }
}
public class GrapeFactory implements Factory {
public Fruit createFruit() { return new Grape(); }
}
// 客戶端:依賴抽象工廠
public class FruitStore {
private final Factory factory;
public FruitStore(Factory factory) { this.factory = factory; }
public Fruit sellFruit() { return factory.createFruit(); }
}
// 使用
FruitStore appleStore = new FruitStore(new AppleFactory());
Fruit apple = appleStore.sellFruit();
| 優點 | 缺點 |
|---|---|
| 符合開閉原則,新增產品只需新增工廠類 | 類別數量成倍增加(每個產品一個工廠) |
| 支援多態,便於測試與替換 | 單一產品族場景下過度設計 |
| 客戶端完全面向介面程式設計 | 若產品層級固定,不如簡單工廠實用 |
當系統有多個產品等級結構(如:水果種類蘋果/葡萄 × 水果等級普通/優質/有機),且需要約束特定組合(如:有機蘋果只能搭配有機包裝),抽象工廠提供統一介面創建一族相關產品。
Factory1)AppleFactory1 創建 Apple1、Apple2)Fruit、Package)// 多個抽象產品
public interface Fruit { void fruitType(); }
public interface Package { void pack(); }
// 抽象工廠:定義創建一族產品的方法
public interface AbstractFactory {
Fruit createFruit();
Package createPackage();
}
// 具體工廠:有機水果族
public class OrganicFactory implements AbstractFactory {
public Fruit createFruit() { return new OrganicApple(); }
public Package createPackage() { return new OrganicPackage(); }
}
// 具體工廠:普通水果族
public class NormalFactory implements AbstractFactory {
public Fruit createFruit() { return new NormalApple(); }
public Package createPackage() { return new NormalPackage(); }
}
// 客戶端
public class FruitShop {
private final AbstractFactory factory;
public FruitShop(AbstractFactory factory) { this.factory = factory; }
public void sellSet() {
Fruit fruit = factory.createFruit();
Package pkg = factory.createPackage();
fruit.fruitType();
pkg.pack();
}
}
| 優點 | 缺點 |
|---|---|
| 約束產品族內部一致性(有機配有機) | 新增產品等級(如加入 Juice)需修改所有工廠介面與實作 |
| 客戶端與具體產品完全解耦 | 複雜度高,簡單場景殺雞用牛刀 |
| 易於切換整個產品族(季節換裝) | 類別爆炸嚴重 |
場景分析:
├─ 單一產品等級、產品數量固定/極少變動 → 簡單工廠(或直接 new)
├─ 單一產品等級、頻繁新增產品、需多態替換 → 工廠方法
├─ 多個產品等級、存在產品族約束、需整體切換 → 抽象工廠
└─ 極簡場景、無擴展需求 → 直接 new / 依賴注入框架管理
現代框架下,工廠多由容器管理,@Bean 方法即為工廠方法,@Configuration 類即為抽象工廠。
// 靈活參數化,減少工廠類數量
public interface FruitFactory {
Fruit createFruit(String variety, Grade grade);
}
// 執行時動態註冊,支援外掛擴展
public class FruitRegistry {
private static final Map> REGISTRY = new ConcurrentHashMap<>();
public static void register(String key, Supplier supplier) {
REGISTRY.put(key, supplier);
}
public static Fruit create(String key) {
Supplier s = REGISTRY.get(key);
if (s == null) throw new IllegalArgumentException("Unknown: " + key);
return s.get();
}
}
// Java 8+ 使用 Supplier/Function 替代工廠介面
Supplier appleSupplier = Apple::new;
Function fruitFactory = type -> "apple".equals(type) ? new Apple() : new Grape();
三種工廠模式本質相同——封裝變化、解耦依賴。實務中不必拘泥於學術定義,根據業務變化率、產品結構複雜度、團隊熟悉度靈活選擇或組合。核心原則:**客戶端依賴抽象,具體創建邏輯集中管理,新增功能不修改既有代碼**。