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

Java 工廠方法模式深度解析:從簡單工廠演進到多態工廠的架構升級

     編輯:Java 相關知識

工廠方法模式是簡單工廠模式的進一步抽象化與推廣。簡單工廠中單一工廠類承擔所有產品建立決策(如交通指揮員),而工廠方法將決策權下放給具體工廠子類(如交通局管理多個指揮員),從而支援更複雜的多維度產品變化。本文通過「小花果園引入蔬菜類」的擴展案例,系統講解工廠方法的結構定義、核心代碼、適用場景與 COM/EJB/JMS 三大企業級技術架構中的實戰應用。

一、為何需要工廠方法模式?

簡單工廠的擴展痛點

簡單工廠:FruitGardener 掌握所有水果生殺大權

當系統需新增「蔬菜類」(需施藥、不同殺蟲劑)時,若繼續用簡單工廠:

  • 方案 A:修改 FruitGardener 加入蔬菜判斷 → 違反單一職責、類膨脹
  • 方案 B:新建 VeggieGardener → 兩個工廠類結構高度相似,缺乏共同抽象

核心矛盾:產品維度增加(水果 → 水果+蔬菜),簡單工廠的單一工廠類無法優雅支援多產品族。

二、工廠方法模式正式定義

UML 結構

工廠方法模式類圖

四大角色

角色說明對應類
抽象工廠核心抽象,與應用無關,定義工廠方法Gardener (abstract class/interface)
具體工廠應用相關,實作工廠方法建立具體產品FruitGardener, VeggieGardener
抽象產品產品共同介面/父類PlantIF
具體產品實際建立的物件Apple, Tomato 等

位圖視角:從一維到二維

  • 簡單工廠:具體產品分布在一維軸上(1, 2, 3...)
  • 工廠方法:具體產品分布在二維平面(工廠維度 × 產品維度)

工廠方法二維位圖

三、小花果園系統實現

1. 統一產品介面 PlantIF


public interface PlantIF {
    void plant();
    void grow();
    void harvest();
}

2. 抽象工廠類 Gardener


public abstract class Gardener {
    public abstract PlantIF factory(String which) throws BadPlantException;
}

3. 具體工廠:水果園丁 vs 蔬菜園丁


// 水果園丁
public class FruitGardener extends Gardener {
    @Override
    public PlantIF factory(String which) throws BadPlantException {
        if (which.equalsIgnoreCase("apple")) return new Apple();
        else if (which.equalsIgnoreCase("strawberry")) return new Strawberry();
        else if (which.equalsIgnoreCase("grape")) return new Grape();
        else throw new BadPlantException("Bad fruit request");
    }
}

// 蔬菜園丁
public class VeggieGardener extends Gardener {
    @Override
    public PlantIF factory(String which) throws BadPlantException {
        if (which.equalsIgnoreCase("tomato")) return new Tomato();
        else if (which.equalsIgnoreCase("potato")) return new Potato();
        else if (which.equalsIgnoreCase("broccoli")) return new Broccoli();
        else throw new BadPlantException("Bad veggie request");
    }
}

4. 產品實現:多介面身份標識


// 蔬菜:同時實作 PlantIF(工廠產品身份)與 VeggieIF(領域身份)
public class Broccoli implements VeggieIF, PlantIF {
    public void pesticideDust() { ... }  // VeggieIF 專屬
    // PlantIF 方法...
}

// 水果:實作 PlantIF 與 FruitIF
public class Apple implements FruitIF, PlantIF {
    private int treeAge;  // FruitIF 專屬屬性
    // PlantIF 方法...
}

設計精髓:VeggieIF 與 FruitIF 代表不同領域身份,PlantIF 代表工廠產品身份。雖功能上可合併,但原則上不應合併——介面即身份,身份分離是領域建模的核心。

四、適用場景判斷:簡單工廠 vs 工廠方法

判斷條件選擇
系統僅有一個產品類別層次,且可預見未來不會增加新類別簡單工廠
存在多個產品類別層次(水果/蔬菜/花卉),或未來需頻繁新增類別工廠方法
產品建立邏輯極簡,無需多態擴展簡單工廠
不同產品族需要不同建立邏輯、參數、資源管理工廠方法

五、企業級架構中的工廠方法實戰

1. Microsoft COM:IClassFactory

COM 架構序列圖

  • Creator = IClassFactory 介面
  • ConcreteCreator = CFactory 類
  • 流程:CoCreateInstance → CoGetClassObject → DllGetClassObject → new CFactory() → IClassFactory::CreateInstance → new CEmployee()
  • 意義:封裝物件建立的所有細節(資源分配、協調等)於工廠類內部

2. Sun EJB:Home 介面

EJB 架構序列圖


// 客戶端代碼
Context ctx = new InitialContext();
EmployeeHome home = (EmployeeHome) ctx.lookup("Employee"); // 獲取 Home 介面(工廠)
Employee emp = home.create(1001, "John", "Smith");         // 工廠方法建立 EJB 實例
emp.setTel("212-657-7879");
  • Home 介面 = 工廠方法角色
  • create() = 工廠方法
  • 容器負責實現 Home 介面,開發者專注業務邏輯

3. JMS:ConnectionFactory/Session 多層工廠

JMS 架構序列圖


Context ctx = new InitialContext(props);
TopicConnectionFactory tcf = (TopicConnectionFactory) ctx.lookup("myTCF"); // 工廠1
TopicConnection tCon = tcf.createTopicConnection();                         // 工廠方法1
TopicSession tSess = tCon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); // 工廠方法2
TopicPublisher publisher = tSess.createPublisher(topic);                    // 工廠方法3
TextMessage msg = tSess.createTextMessage("Hello");                         // 工廠方法4
publisher.publish(msg);
  • JMS 架構中工廠方法無處不在:ConnectionFactory → Connection → Session → Producer/Message
  • 每一層都封裝了複雜的資源管理(連接、事務、會話狀態)

六、實現變體與最佳實踐

  1. 抽象工廠形式:介面 vs 抽象類 vs 具體類(含預設實現)
  2. 工廠方法參數化:經典定義無參數,實戰常加參數(如 factory(String which))以支援單工廠多產品
  3. 命名規約:方法命名為 createXxx()、factory()、newInstance() 等,明確表達工廠意圖

七、常見問題答疑

  1. Q: 類圖左上角夾子符號代表什麼?
    A: 表示該類有屬性——即帶 getter/setter 的成員變量。JavaBean 慣例要求有屬性才算 Bean。
  2. Q: 為何 ConcreteProduct 類圖只出現一次?
    A: 類圖展示框架結構,實際具體產品類數量視系統而定。UML 關聯線旁標數字表示實例數量,而非類數量。
  3. Q: 為何 Broccoli 實作 VeggieIF 和 PlantIF 兩個介面?
    A: 介面代表身份。VeggieIF = 蔬菜領域身份,PlantIF = 工廠產品身份。身份分離是良好領域建模的體現。
  4. Q: Product 應為抽象類還是介面?
    A: 若具體產品間有共同邏輯 → 抽象類;無共同邏輯 → 介面(建議)。非強制。
  5. Q: 為何 factory() 用 if-else 而非多態建立產品?
    A: 多態是物件建立後的行為。物件建立過程本質上是過程性的,必須有條件判斷決定建立哪個類。

八、總結

工廠方法模式通過引入抽象工廠層,將「建立什麼產品」的決策下放給具體工廠子類,實現了開閉原則:新增產品族只需新增具體工廠類,無需修改核心抽象層。它是 COM、EJB、JMS 等企業級架構的基石模式。理解其與簡單工廠的本質區別——一維 vs 二維、硬編碼 vs 多態擴展——是架構師進行模式選型的關鍵判斷力。

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