摘要
本文主要介紹了簡單工廠模式,包括其定義、結構、實現方式、適用場景、實戰示例以及思考。簡單工廠模式是一種創建型設計模式,通過工廠類根據參數決定創建哪一種產品類的實例,封裝了對象創建的細節,使客戶端無需關心具體類的創建邏輯。文章詳細闡述了其角色組成、類圖、時序圖,探討了兩種常見的實現方式,分析了適合與不適合的場景,并提供了 Spring 項目和可插拔式策略工廠的實戰示例。最后,還提出了支持 SPI 機制、注解標記策略名稱和配置中心切換策略等思考方向。
1. 簡單工廠模式定義
簡單工廠模式是一種創建型設計模式,它通過一個工廠類根據傳入的參數決定創建哪一種產品類的實例。
核心要點:
- 核心角色:工廠類(Factory)
-
- 負責創建產品對象。
- 客戶端只需傳遞參數,不關心具體類的創建邏輯。
- 目標:封裝對象創建的細節,將創建對象的邏輯從使用者中分離出來。
組成結構:
角色 | 說明 |
Product | 抽象產品類(接口或抽象類) |
ConcreteProduct | 具體產品類,實現 Product 接口 |
Factory | 工廠類,包含創建產品對象的靜態方法 |
Client | 客戶端,調用工廠方法獲取產品對象并使用它 |
2. 簡單工廠模式結構
2.1. 簡單工廠類圖
2.2. 簡單工廠時序圖
3. 簡單工廠模式實現方式
實現方式主要分為兩種:
實現方式 | 描述 |
1. 靜態方法創建(常用) | 工廠方法是 |
2. 實例方法創建(靈活) | 工廠需要先實例化,再調用方法創建對象,適合支持不同配置或依賴注入 |
3.1. ? 實現方式 1:靜態方法創建產品對象(最常見)
// 抽象產品
public interface Product {void doWork();
}// 具體產品 A
public class ProductA implements Product {public void doWork() {System.out.println("產品A正在工作");}
}// 具體產品 B
public class ProductB implements Product {public void doWork() {System.out.println("產品B正在工作");}
}// 簡單工廠
public class SimpleFactory {public static Product createProduct(String type) {if ("A".equalsIgnoreCase(type)) {return new ProductA();} else if ("B".equalsIgnoreCase(type)) {return new ProductB();}throw new IllegalArgumentException("不支持的產品類型:" + type);}
}// 客戶端
public class Client {public static void main(String[] args) {Product product = SimpleFactory.createProduct("A");product.doWork(); // 輸出:產品A正在工作}
}
3.2. ? 實現方式 2:使用工廠實例方法創建產品對象(便于配置)
// 工廠類
public class SimpleFactory {public Product createProduct(String type) {if ("A".equalsIgnoreCase(type)) {return new ProductA();} else if ("B".equalsIgnoreCase(type)) {return new ProductB();}throw new IllegalArgumentException("不支持的產品類型:" + type);}
}// 客戶端
public class Client {public static void main(String[] args) {SimpleFactory factory = new SimpleFactory();Product product = factory.createProduct("B");product.doWork(); // 輸出:產品B正在工作}
}
3.3. 🧠 簡單工廠實現總結
對比項 | 靜態方法工廠 | 實例方法工廠 |
是否需要工廠對象 | 否 | 是 |
是否支持配置 | 較差 | 支持通過構造函數注入 |
使用場景 | 通用工廠、工具類 | 多配置或狀態的工廠類 |
4. 簡單工廠模式適合場景
4.1. ? 適合的場景(推薦使用簡單工廠):
場景描述 | 原因 |
產品種類有限,變化不大 | 工廠中 if-else/switch 可維護性尚可,結構清晰 |
客戶端不關心對象創建細節 | 封裝了具體類的創建過程,減少耦合 |
多個子類實現一個接口/抽象類,客戶端按條件選擇創建哪一個 | 工廠根據傳參決定具體返回哪個子類 |
代碼結構相對簡單,開發階段處于初期 | 不想引入復雜設計,簡單工廠最直觀、易懂 |
希望集中管理對象創建 | 比 scattered new 更好維護和修改 |
📌 示例:日志輸出、支付方式選擇、消息發送器選擇(短信/郵件/微信)等。
4.2. ? 不適合的場景(不推薦使用簡單工廠):
場景描述 | 問題 |
1. 產品種類經常變化或增加 | 每新增一個產品都要修改工廠類,違反“開放封閉原則” |
2. 產品種類太多,邏輯復雜 | 工廠類會膨脹成一個巨大類,不利于維護 |
3. 需要依賴注入、構造過程復雜 | 無法靈活注入依賴,缺乏拓展性 |
4. 對產品創建過程有定制要求 | 不能對不同產品的創建過程進行細粒度控制 |
5. 分布式、多模塊開發場景 | 集中式工廠類會形成模塊間強依賴,不利于解耦和部署 |
📌 示例:大型系統中涉及復雜對象生命周期控制的場景(如數據庫連接池、線程池、配置驅動類等)。
4.3. ? 使用建議總結:
建議 | 原因 |
? 使用于產品穩定、變化少的小規模項目 | 簡單、易維護 |
? 不建議用于大型、產品體系復雜的項目 | 違背開閉原則、擴展性差 |
👉 可結合工廠方法模式或抽象工廠模式演進 | 更好地支持擴展與解耦 |
5. 簡單工廠模式實戰示例
5.1. Spring項目中簡單工廠示例
我們以“消息發送系統”為例,演示如何使用簡單工廠根據輸入創建不同的消息發送器(如短信、郵件、微信等)。
5.1.1. ? 場景目標
在 Spring 項目中,根據配置選擇不同的消息發送器進行發送,如:
- 配置為
sms
:發送短信; - 配置為
email
:發送郵件; - 配置為
wechat
:發送微信。
5.1.2. ? 定義統一接口
public interface MessageSender {void send(String message);
}
5.1.3. ? 三種發送器的實現類
@Component("smsSender")
public class SmsSender implements MessageSender {public void send(String message) {System.out.println("發送短信:" + message);}
}@Component("emailSender")
public class EmailSender implements MessageSender {public void send(String message) {System.out.println("發送郵件:" + message);}
}@Component("wechatSender")
public class WechatSender implements MessageSender {public void send(String message) {System.out.println("發送微信:" + message);}
}
5.1.4. ? 簡單工廠類(使用 Spring 容器管理)
@Component
public class MessageSenderFactory {@Autowiredprivate ApplicationContext applicationContext;public MessageSender createSender(String type) {switch (type.toLowerCase()) {case "sms":return applicationContext.getBean("smsSender", MessageSender.class);case "email":return applicationContext.getBean("emailSender", MessageSender.class);case "wechat":return applicationContext.getBean("wechatSender", MessageSender.class);default:throw new IllegalArgumentException("不支持的消息類型:" + type);}}
}
5.1.5. ? 使用示例(Controller 或 Service)
@Service
public class NotifyService {@Autowiredprivate MessageSenderFactory messageSenderFactory;// 模擬從配置文件或參數中讀取類型@Value("${message.type:sms}")private String messageType;public void notifyUser(String content) {MessageSender sender = messageSenderFactory.createSender(messageType);sender.send(content);}
}
5.1.6. ? 應用入口(或 Controller)
@RestController
public class MessageController {@Autowiredprivate NotifyService notifyService;@GetMapping("/send")public String send(@RequestParam String msg) {notifyService.notifyUser(msg);return "消息已發送";}
}
5.1.7. ? application.yml
示例配置
message:type: email
5.1.8. ? 簡單工廠示例總結
設計點 | 說明 |
工廠類 |
|
接口統一 | 所有發送器實現 |
解耦 |
|
可配置 | 支持通過配置控制行為,符合 Spring 項目實踐 |
5.2. 可插拔式策略工廠(使用 SPI、Map 注冊) 示例
下面是一個 可插拔式策略工廠(基于 Map 注冊 + Spring 管理) 的完整示例,非常適用于需要動態切換、按配置擴展的業務策略類,比如:消息發送、支付渠道、風控策略、營銷活動處理器等。實現一種 靈活可擴展 的策略選擇工廠,每個策略自動注冊 到一個 Map<String, Strategy>
中,不需要手動 switch-case
。
5.2.1. ? 定義統一接口(策略接口)
public interface MessageSender {String type(); // 每個實現類提供自己的標識void send(String message);
}
5.2.2. ? 三種發送實現類(@Component 自動注入)
@Component
public class SmsSender implements MessageSender {public String type() {return "sms";}public void send(String message) {System.out.println("【短信】發送:" + message);}
}@Component
public class EmailSender implements MessageSender {public String type() {return "email";}public void send(String message) {System.out.println("【郵件】發送:" + message);}
}@Component
public class WechatSender implements MessageSender {public String type() {return "wechat";}public void send(String message) {System.out.println("【微信】發送:" + message);}
}
5.2.3. ? 自動注冊策略工廠(Map 注冊方式)
@Component
public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();// Spring 會自動注入所有 MessageSender 的實現類@Autowiredpublic MessageSenderFactory(List<MessageSender> senders) {for (MessageSender sender : senders) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息類型: " + type);}return sender;}
}
5.2.4. ? 使用策略工廠的服務類
@Service
public class NotifyService {@Autowiredprivate MessageSenderFactory messageSenderFactory;public void notifyUser(String type, String content) {MessageSender sender = messageSenderFactory.getSender(type);sender.send(content);}
}
5.2.5. ? Controller 示例
@RestController
@RequestMapping("/msg")
public class MessageController {@Autowiredprivate NotifyService notifyService;@GetMapping("/send")public String send(@RequestParam String type, @RequestParam String msg) {notifyService.notifyUser(type, msg);return "消息已發送";}
}
5.2.6. ? 示例調用
請求:
GET /msg/send?type=sms&msg=Hello_SMS
GET /msg/send?type=email&msg=Hello_Email
GET /msg/send?type=wechat&msg=Hello_WeChat
輸出:
【短信】發送:Hello_SMS
【郵件】發送:Hello_Email
【微信】發送:Hello_WeChat
5.2.7. 🚀 可插拔式策略工廠優勢總結
特性 | 說明 |
可插拔 | 新增一個實現類即可生效,無需改動工廠或業務代碼 |
高擴展性 | 支持多策略、動態注冊 |
易維護 | 擺脫 if-else / switch |
Spring友好 | 利用 Spring 自動注入 & 生命周期 |
5.3. 🔌 簡單工廠設計模式可拓展方向(高級)
- 支持 SPI 機制(META-INF/services),用于插件式加載(可用于 jar 擴展、熱插拔);
- 支持 注解標記策略名稱;
- 支持配置中心切換策略,如使用
@Value("${message.type}")
注入默認策略。
6. 簡單工廠模式思考
6.1. 支持 SPI 機制(META-INF/services),用于插件式加載(可用于 jar 擴展、熱插拔);
下面是一個完整的 基于 Java SPI(Service Provider Interface)機制的插件式策略工廠實現示例,它常用于實現模塊解耦、可插拔、動態擴展功能。以“消息發送策略”為例,不使用 Spring 注解掃描,而是通過 SPI 機制動態加載所有實現類。
6.1.1. 🧩 定義 SPI 接口(策略接口)
// src/main/java/com/example/spi/MessageSender.java
package com.example.spi;public interface MessageSender {String type(); // 返回類型標識符void send(String message); // 發送消息邏輯
}
6.1.2. 📝 實現類(位于獨立模塊 / jar 中)
// src/main/java/com/example/impl/SmsSender.java
package com.example.impl;import com.example.spi.MessageSender;public class SmsSender implements MessageSender {@Overridepublic String type() {return "sms";}@Overridepublic void send(String message) {System.out.println("【短信發送】" + message);}
}
6.1.3. 🗂? 3SPI 注冊文件
在 jar 包中的 resources/META-INF/services/
路徑下創建文件:
META-INF/services/com.example.spi.MessageSender
內容如下(每一行一個實現類的全限定名):
com.example.impl.SmsSender
? 當你有多個策略實現時,添加多行即可,如:
com.example.impl.SmsSender
com.example.impl.EmailSender
6.1.4. ?? 工廠類:動態加載所有策略實現
package com.example.factory;import com.example.spi.MessageSender;import java.util.*;public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();public MessageSenderFactory() {// 加載spi接口所有接口實現類ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);for (MessageSender sender : loader) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息類型: " + type);}return sender;}
}
ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);
是 Java 原生 SPI(Service Provider Interface)機制 的核心。它的底層原理其實并不復雜,是通過讀取類路徑下的 META-INF/services/
目錄下的文件,并利用反射機制加載實現類。以下是 ServiceLoader
的簡化執行邏輯:
// 從 META-INF/services/ 下查找接口對應的配置文件
Enumeration<URL> urls = classLoader.getResources("META-INF/services/" + serviceClass.getName());// 讀取文件中的類名
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String implClassName = reader.readLine();// 通過反射實例化對象
Class<?> clazz = Class.forName(implClassName);
Object instance = clazz.newInstance();
每個實現類會被加載成一個對象,并返回一個延遲加載的迭代器。
6.1.5. 🧪 使用示例(main 方法中調用)
public class Main {public static void main(String[] args) {MessageSenderFactory factory = new MessageSenderFactory();MessageSender sender = factory.getSender("sms");sender.send("Hello SPI!");}
}
6.1.6. 🎯 整體結構
src/
├── main/
│ ├── java/
│ │ ├── com.example.spi/MessageSender.java
│ │ ├── com.example.impl/SmsSender.java
│ │ └── com.example.factory/MessageSenderFactory.java
│ └── resources/
│ └── META-INF/services/com.example.spi.MessageSender
6.1.7. 📌 SPI 模式的優點
優點 | 說明 |
插件化、低耦合 | 實現類可來自外部 jar 包,主程序不需修改 |
動態擴展能力強 | 實現類可以熱插拔(配合 OSGi、模塊系統) |
框架內部廣泛使用 | Dubbo、JDBC、Spring Boot 自動配置( |
6.1.8. 🚫 注意事項
- SPI 默認是懶加載 + 非線程安全(如有并發需求請做好緩存);
- IDE 無法提示類名(注冊文件中填寫錯誤 IDE 不會報警);
- 多個 jar 中存在相同接口實現可能沖突;
- 如果你使用的是 SpringBoot,推薦改用 Spring 的 SPI(如
spring.factories
或spring.factories
→spring/org.springframework.boot.autoconfigure.EnableAutoConfiguration
)機制配合。
6.2. 支持注解標記策略名稱
在 策略模式 中,支持通過 注解 來標記每個策略的名稱,可以極大提升策略類的可讀性和擴展性,尤其適用于 SPI、Map 注冊、Spring 容器注冊等場景。
- 多個
MessageSender
策略實現類 - 每個類通過注解標記其類型名稱(如
@MessageType("sms")
) - 使用 Map 注冊所有策略,支持動態獲取
6.2.1. ? 定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageType {String value(); // 策略類型標識
}
6.2.2. ? 定義接口
public interface MessageSender {void send(String message);
}
6.2.3. ? 實現策略類 + 注解標記
@MessageType("sms")
public class SmsSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("發送短信: " + message);}
}@MessageType("email")
public class EmailSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("發送郵件: " + message);}
}
6.2.4. ? 自動注冊工廠類(注解掃描)
public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();public MessageSenderFactory() {// SPI 加載所有 MessageSender 實現類ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);for (MessageSender sender : loader) {MessageType annotation = sender.getClass().getAnnotation(MessageType.class);if (annotation != null) {senderMap.put(annotation.value(), sender);}}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息類型: " + type);}return sender;}
}
6.2.5. ? 使用示例
public class Main {public static void main(String[] args) {MessageSenderFactory factory = new MessageSenderFactory();factory.getSender("sms").send("驗證碼123456");factory.getSender("email").send("歡迎郵件");}
}
6.2.6. ? 優點
- 注解直觀標記類型,無需硬編碼注冊
- 擴展一個新策略只需添加一個類并標注注解
- 與
ServiceLoader
/Spring 配合使用,支持動態發現
6.3. 支持配置中心切換策略,如使用 @Value("${message.type}")
注入默認策略。
你希望實現一種 支持配置中心動態切換策略 的能力,例如通過配置中心的屬性(如 Nacos、Apollo、Spring Cloud Config 等)控制當前策略的類型:實現思路(Spring + 注解 + Map 注冊 + 配置驅動)
6.3.1. ? 定義策略注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageType {String value();
}
6.3.2. ? 定義通用接口
public interface MessageSender {void send(String message);
}
6.3.3. ? 多個策略實現類
@Component
@MessageType("sms")
public class SmsSender implements MessageSender {public void send(String message) {System.out.println("發送短信: " + message);}
}@Component
@MessageType("email")
public class EmailSender implements MessageSender {public void send(String message) {System.out.println("發送郵件: " + message);}
}
6.3.4. ? 策略工廠(Spring 容器自動注冊 + 配置驅動)
@Component
public class MessageSenderFactory implements InitializingBean {@Value("${message.type:email}") // 默認email,可由配置中心動態更新private String defaultType;@Autowiredprivate List<MessageSender> senderList;private final Map<String, MessageSender> senderMap = new HashMap<>();@Overridepublic void afterPropertiesSet() {for (MessageSender sender : senderList) {MessageType annotation = sender.getClass().getAnnotation(MessageType.class);if (annotation != null) {senderMap.put(annotation.value(), sender);}}}/** 如果使用默認方式,采用的就是nacos 配置策略*/public MessageSender getSender() {MessageSender sender = senderMap.get(defaultType);if (sender == null) {throw new IllegalArgumentException("未找到對應的策略類型: " + defaultType);}return sender;}/** 使用入參對應的策略*/public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("未找到對應的策略類型: " + type);}return sender;}
}
6.3.5. ? 使用示例
@RestController
public class MessageController {@Autowiredprivate MessageSenderFactory factory;/** 如果使用默認方式,采用的就是nacos配置策略*/@GetMapping("/send")public String send(@RequestParam String msg) {factory.getSender().send(msg);return "消息已發送";}/** 如果使用入參對應方式發送*/@GetMapping("/email/send")public String send(@RequestParam String sendType, @RequestParam String msg) {factory.getSender(sendType).send(msg);return "消息已發送";}
}
6.3.6. ? 配置變更如何生效(支持熱刷新)?
如果你使用配置中心(如 Nacos),你還可以讓 @Value("${message.type}")
自動刷新:
@RefreshScope
@Component
public class MessageSenderFactory implements InitializingBean {...
}
這樣,在 Nacos 修改 message.type=email
為 sms
,會實時切換策略。
6.3.7. ? 總結優勢
特性 | 實現方式 |
策略標識 | 注解 |
掃描所有策略實現 | Spring 注入 |
配置驅動選擇策略 |
|
動態刷新策略選擇 |
|
6.4. 使用SPI 機制(META-INF/services),用于插件式加載(可用于 jar 擴展、熱插拔);與普通簡單工廠方法實現區別?
使用 SPI 機制(META-INF/services
) 和 普通簡單工廠模式 都是實現“根據類型返回不同實現類”的策略工廠思想,但它們在實現機制、應用場景和擴展性上有很大不同。以下是詳細對比和說明:
6.4.1. 🔍 核心區別對比表
項目 | SPI機制( | 簡單工廠模式(自己注冊/Map) |
加載方式 | JDK 內置 自動發現實現類 | 自己手動注冊,或用注解 + Spring 掃描 |
實現類注冊位置 |
| 代碼中通過 |
是否依賴容器(如Spring) | 否(純 JDK 原生機制) | 是(通常依賴 Spring 注入、配置) |
擴展性(插件支持) | 強,可動態加入 jar 包擴展實現 | 弱,必須修改代碼或重啟注冊邏輯 |
熱插拔能力 | 支持(添加 jar 即生效,重啟可用) | 不支持,必須重啟或改代碼 |
類型標識方式 | 實現類自己定義類型(如 | 通常用注解或手動 |
適合插件機制 | ? 非常適合 | ? 不適合 |
使用門檻 | 中(需要手動寫 SPI 配置文件) | 低(代碼中實現注冊) |
6.4.2. 🧩 SPI機制(JDK ServiceLoader)工作原理
- 你要做的:
-
- 定義一個接口,比如:
MessageSender
- 實現多個類,比如:
EmailSender
、SmsSender
并實現該接口 - 在
resources/META-INF/services/com.example.spi.MessageSender
文件中列出所有實現類的 全限定類名
- 定義一個接口,比如:
com.example.sender.EmailSender
com.example.sender.SmsSender
- 運行時使用
ServiceLoader
自動掃描并實例化:
ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);
for (MessageSender sender : loader) {senderMap.put(sender.type(), sender);
}
📦 實現類可以是外部 jar 包,插件式部署。無需改主程序代碼。
6.4.3. 🛠? 簡單工廠模式實現方式
實現邏輯通常在代碼里“硬編碼”注冊每種類型與實現類的關系:
public class SenderFactory {private static final Map<String, MessageSender> map = new HashMap<>();static {map.put("email", new EmailSender());map.put("sms", new SmsSender());}public static MessageSender getSender(String type) {return map.get(type);}
}
🧱 實現類和工廠強耦合,新增類型時,必須改代碼、重編、重新部署。
6.4.4. 🚦 使用場景對比
場景 | 建議使用方式 |
插件式架構,第三方擴展點 | ? 使用 SPI |
配置中心控制策略選擇 | ? 使用 Spring + Map |
內部服務、類型少且固定 | ? 簡單工廠 |
支持 jar 級別熱插拔、可拔插插件 | ? SPI + jar 模塊化 |
多環境配置動態選擇實現 | ? Spring 結合 @Value |
7. 博文參考
- 1. 簡單工廠模式( Simple Factory Pattern ) — Graphic Design Patterns
- 創建型 - 簡單工廠(Simple Factory) | Java 全棧知識體系