首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 設計模式(一):簡單工廠模式進化論——從硬編碼到註解驅動的極致解耦

     編輯:Java 綜合教學

場景引入:司機換車的演進之路

抽象產品 `Car`,具體產品 `BenzCar`、`BMWCar`、`LandRoverCar`。司機(客戶端)需要靈活切換車型。

版本 1:硬編碼強耦合


BenzCar car = new BenzCar(); car.drive(); // 換車必須改代碼、重編譯

版本 2:配置文件 + 多態 + Switch 判斷


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。

版本 3:簡單工廠封裝創建邏輯(CarFactory1)


public class CarFactory1 {
    public static Car newCar() {
        String name = loadConfig("factory1.name");
        switch (name) { ... } // 同版本 2
        return car;
    }
}
// 客戶端
Car car = CarFactory1.newCar(); car.drive();

進步:創建邏輯集中到工廠類,客戶端零感知。但工廠內部 Switch 仍需修改。

版本 4:反射機制實現開閉原則(CarFactory2)


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 核心即基於此原理。

版本 5:註解掃描 + 別名映射,極致易用(CarFactory3)


// 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` 掃描。

簡單工廠模式正式定義與角色

  • 定義:靜態工廠方法模式。專門類負責創建同一抽象類/接口下的不同具體實例,封裝實例化邏輯,實現創建與使用分離。
  • 角色:
    1. 工廠角色:核心,提供靜態工廠方法
    2. 抽象產品角色:父類/接口,定義公共契約
    3. 具體產品角色:工廠創建的目標實例
  • 類圖:簡單工廠類圖

優缺點權衡

優點缺點
創建邏輯集中,客戶端解耦工廠類集中所有創建邏輯,違反 SRP,一旦故障全系統受影響
配置/反射/註解實現 OCP產品族擴展時(新接口),工廠需判斷創建哪類產品,邏輯膨脹
屏蔽具體類名,降低記憶負擔靜態方法無法繼承覆寫,難以構建工廠等級結構

與 OOP 原則對照

  • ✅ 依賴倒置(客戶端依賴抽象 Car)
  • ✅ 迪米特法則(客戶端不直接 new 具體類)
  • ✅ 裡氏替換(具體產品可替換抽象產品)
  • ✅ 接口隔離(抽象產品接口精簡)
  • ⚠️ 開閉原則(靜態 Switch 違反,反射/註解版滿足)
  • ❌ 單一職責(工廠兼顧邏輯判斷與實例化)

JDK 經典應用:JDBC DriverManager


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()`。

何時選擇簡單工廠 vs 工廠方法 vs 抽象工廠

  • 簡單工廠:產品種類固定/變化緩慢、創建邏輯簡單、無需工廠擴展(如 JDBC 驅動)
  • 工廠方法:產品結構穩定但創建邏輯需子類定製、框架預留擴展點(如 `Collection.iterator()`)
  • 抽象工廠:需創建多個相關/依賴產品族、運行時切換整個產品族(如跨平台 GUI、多数据源適配器)

本文代碼完整版可見 GitHub:github.com/jasongj/design-patterns/simple-factory

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved