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

設計模式深度解析:簡單工廠 vs 工廠方法 vs 抽象工廠 進化史與實戰選型

     編輯:Java 綜合教學

設計模式深度解析:簡單工廠 vs 工廠方法 vs 抽象工廠 進化史與實戰選型

工廠模式族群是創建型模式的基石,解決**「對象創建邏輯與使用邏輯耦合」**的核心問題。本文將三大工廠模式置於同一坐標系,通過**雷鋒案例**、**數據庫訪問案例**雙實戰,揭示其層層遞進的抽象演化邏輯,並給出實戰選型決策表。

一、三大模式關係圖譜

簡單工廠 (Simple Factory)
    │  靜態工廠方法、上帝類、違反開閉原則
    ▼
工廠方法 (Factory Method)  ── GOF 正統、多態工廠、符合開閉原則
    │  抽象工廠接口 + 多個具體工廠子類,每個工廠只創建一種產品
    ▼
抽象工廠 (Abstract Factory)  ── 工具箱、產品族、多等級結構
    一個工廠接口創建多個相關/依賴產品 (如:椅子+沙發、MySQL User+MySQL Student)

二、簡單工廠模式:起點與妥協

結構

  • 工廠類:核心邏輯,含 `switch/case` 或 `if/else` 判斷創建哪個具體產品
  • 抽象產品:接口/抽象類
  • 具體產品:實現類

代碼骨架

public class LeiFengFactory {
    public static LeiFeng createLeiFeng(String type) {
        if ("student".equals(type)) return new Student();
        if ("volunteer".equals(type)) return new Volunteer();
        throw new IllegalArgumentException("未知類型");
    }
}
// 客戶端
LeiFeng lf = LeiFengFactory.createLeiFeng("student");
lf.sweep();

缺陷

  • 新增產品**必須修改工廠類源碼** (違反開閉原則)
  • 邏輯集中導致「上帝類」膨脹
  • 靜態方法難以擴展、測試、Mock

三、工廠方法模式:多態解耦

核心升級

  • 將「判斷邏輯」從工廠類**下沉到子類**:每個具體工廠只負責創建一種產品
  • 引入**抽象工廠接口** 定義創建契約
  • 客戶端面向抽象工廠編程,具體工廠由配置/依賴注入決定

雷鋒案例完整代碼

// 抽象產品
public abstract class LeiFeng {
    public void sweep() { System.out.println("掃地"); }
    public void wash() { System.out.println("洗衣服"); }
}
// 具體產品
public class Student extends LeiFeng {}
public class Volunteer extends LeiFeng {}

// 抽象工廠
public interface IFactory {
    LeiFeng getLeiFeng();
}
// 具體工廠
public class StudentFactory implements IFactory {
    public LeiFeng getLeiFeng() { return new Student(); }
}
public class VolunteerFactory implements IFactory {
    public LeiFeng getLeiFeng() { return new Volunteer(); }
}

// 客戶端
IFactory factory = new StudentFactory(); // 或從配置/IOC 獲取
LeiFeng lf = factory.getLeiFeng();
lf.sweep();

優勢

  • 新增產品 = 新增具體產品類 + 新增具體工廠類,**零修改現有代碼**
  • 符合開閉原則、單一職責原則
  • 易於單元測試 (Mock IFactory)

四、抽象工廠模式:產品族與多等級結構

適用場景

系統有**多個產品等級結構** (如:User、Student),且**不同數據庫實現構成產品族** (SqlUser+SqlStudent、AccessUser+AccessStudent)。客戶端需**一次性獲得一族相關產品**。

數據庫訪問案例完整代碼

// 兩個產品等級結構
public interface IUser { void insert(User u); User getUser(int id); }
public interface IStudent { void insert(Student s); Student getStudent(int id); }

// SQL Server 族
public class SqlUser implements IUser { ... }
public class SqlStudent implements IStudent { ... }
// Access 族
public class AccessUser implements IUser { ... }
public class AccessStudent implements IStudent { ... }

// 抽象工廠:定義創建全族產品的契約
public interface IFactory {
    IUser createUser();
    IStudent createStudent();
}
// 具體工廠:實現族內創建
public class SqlFactory implements IFactory {
    public IUser createUser() { return new SqlUser(); }
    public IStudent createStudent() { return new SqlStudent(); }
}
public class AccessFactory implements IFactory {
    public IUser createUser() { return new AccessUser(); }
    public IStudent createStudent() { return new AccessStudent(); }
}

// 客戶端
IFactory factory = new SqlFactory(); // 切換數據庫只需換工廠
IUser userDao = factory.createUser();
IStudent stuDao = factory.createStudent();
userDao.insert(new User());
stuDao.insert(new Student());

關鍵概念:產品族

位於**不同等級結構中、功能相關聯**的產品組成的家族。如 `SqlUser` + `SqlStudent` 屬於「SQL Server 產品族」。抽象工廠在**類內部約束了族內關聯**,確保客戶端獲得的始終是兼容的一族對象。

五、三模式深度對比表

維度簡單工廠工廠方法抽象工廠
產品等級結構數11多 (≥2)
每個工廠創建產品數多1多 (一族)
開閉原則違反 (修改工廠類)符合 (擴展子類)符合 (擴展子類)
客戶端耦合度耦合具體工廠類耦合抽象工廠接口耦合抽象工廠接口
增加新產品等級修改工廠類新增工廠接口方法+所有子類實現 (較麻煩)同工廠方法
典型應用JDK Calendar.getInstance()、數據庫連接池獲取連接JDBC DriverManager、集合迭代器、文檔構建器UI 皮膚庫、跨平台 GUI、數據庫多廠商適配

六、實戰選型決策指南

  1. 單一產品、創建邏輯固定、極少擴展 → 簡單工廠 (或直接 new)
  2. 單一產品等級、頻繁新增產品類型、需解耦創建邏輯 → 工廠方法
  3. 多個產品等級構成族、需保證族內一致性、支持切換整族實現 → 抽象工廠
  4. 產品族擴展頻繁 (新增產品等級) → 謹慎用抽象工廠,考慮 Builder/Prototype/依賴注入容器替代

七、演化視角:模式邊界的模糊性

「工廠方法加個方法變抽象工廠,抽象工廠減個方法變工廠方法。」
實戰中**不必糾結名稱**,核心目標只有一個:**降低創建與使用的耦合度**。靈活組合:工廠方法創建單品、抽象工廠組裝族、簡單工廠封裝邏輯、依賴注入容器管理工廠實例生命週期。

八、總結

三大工廠模式是**抽象層級遞增**的演化鏈:

  • 簡單工廠:集中創建,違反開閉,適合穩定場景
  • 工廠方法:多態分發,符合開閉,適合單一等級擴展
  • 抽象工廠:族級約束,符合開閉,適合多等級一致性場景

掌握其**核心結構差異 (產品等級數、工廠創建數)、擴展痛點 (新增產品 vs 新增等級)、實戰適用邊界**,即可在架構設計中精準落子,避免過度設計與不足設計。

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