抽象產品 `Car`,具體產品 `BenzCar`、`BMWCar`、`LandRoverCar`。司機(客戶端)需要靈活切換車型。
BenzCar car = new BenzCar(); car.drive(); // 換車必須改代碼、重編譯
XMLConfiguration config = new XMLConfiguration("car.xml");
String name = config.getString("driver2.name");
Car car;
switch (name) {
case "Land Rover": car = new LandRoverCar(); break;
case "BMW": car = new BMWCar(); break;
case "Benz": car = new BenzCar(); break;
}
car.drive();
問題:新增車型仍需修改客戶端 Switch 邏輯,違反 OCP。
public class CarFactory1 {
public static Car newCar() {
String name = loadConfig("factory1.name");
switch (name) { ... } // 同版本 2
return car;
}
}
// 客戶端
Car car = CarFactory1.newCar(); car.drive();
進步:創建邏輯集中到工廠類,客戶端零感知。但工廠內部 Switch 仍需修改。
public class CarFactory2 {
public static Car newCar() {
String className = loadConfig("factory2.class"); // 完整類名
try {
return (Car) Class.forName(className).newInstance();
} catch (Exception e) { ... }
}
}
配置文件寫 `com.jasongj.product.LandRoverCar`,新增車型只需添加類並修改配置,**工廠代碼零修改**,滿足 OCP。Spring IOC 核心即基於此原理。
// 1. 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Vehicle {
String type(); // 別名,如 "benz", "bmw"
}
// 2. 產品類標註
@Vehicle(type = "benz") public class BenzCar extends Car { ... }
@Vehicle(type = "bmw") public class BMWCar extends Car { ... }
// 3. 工廠掃描構建映射表
public class CarFactory3 {
private static final Map<String, Class<?>> allCars = new ConcurrentHashMap<>();
static {
Reflections reflections = new Reflections("com.jasongj.product");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Vehicle.class);
for (Class<?> clazz : annotated) {
Vehicle v = clazz.getAnnotation(Vehicle.class);
allCars.put(v.type(), clazz);
}
}
public static Car newCar() {
String type = loadConfig("factory3.type"); // 讀別名
Class<?> clazz = allCars.get(type);
return clazz != null ? (Car) clazz.newInstance() : null;
}
}
優勢:配置用業務友好別名(`benz` vs 全限定類名),掃描機制自動發現新類型(約定優於配置),類似 Apache NiFi Processor 工廠、Spring `@Component` 掃描。

| 優點 | 缺點 |
|---|---|
| 創建邏輯集中,客戶端解耦 | 工廠類集中所有創建邏輯,違反 SRP,一旦故障全系統受影響 |
| 配置/反射/註解實現 OCP | 產品族擴展時(新接口),工廠需判斷創建哪類產品,邏輯膨脹 |
| 屏蔽具體類名,降低記憶負擔 | 靜態方法無法繼承覆寫,難以構建工廠等級結構 |
Class.forName("org.apache.hive.jdbc.HiveDriver"); // 觸發 Driver 靜態塊註冊
Connection conn = DriverManager.getConnection("jdbc:hive2://localhost:10000/default");
// DriverManager 內部遍歷 RegisteredDrivers,調用 driver.connect(url, props)
// 應用層零感知具體驅動類,完美體現簡單工廠
其他例子:`Calendar.getInstance()`、`NumberFormat.getInstance()`、`Charset.forName()`。
本文代碼完整版可見 GitHub:github.com/jasongj/design-patterns/simple-factory