工廠模式(Factory Pattern)?是創建型設計模式的核心成員,它通過將對象創建的邏輯封裝起來,實現了創建與使用的解耦。本文將深入探討工廠模式的核心思想、實現技巧以及在C++中的高效實現方式。
為什么需要工廠模式?
在軟件開發中,直接使用new
關鍵字創建對象會帶來諸多問題:
-
緊耦合:客戶端代碼依賴具體類實現
-
難以擴展:添加新產品需要修改客戶端代碼
-
職責混亂:對象創建邏輯分散在各處
-
違反開閉原則:對修改開放,對擴展封閉
工廠模式通過封裝對象創建過程解決了這些問題,提供了一種靈活的創建機制。當你的代碼中存在以下情況時,工廠模式特別有用:
-
無法預知需要創建的具體類型
-
需要集中管理對象的創建邏輯
-
系統需要支持多種類型的產品
-
希望將實例化延遲到子類
工廠模式的兩種形態
1. 簡單工廠模式(靜態工廠)
簡單工廠模式是最基礎的形式,它通過一個靜態方法封裝對象的創建邏輯。
#include <iostream>
#include <memory>
#include <stdexcept>// 抽象產品類:圖形接口
class Shape {
public:virtual void draw() const = 0;virtual ~Shape() = default;
};// 具體產品類:圓形
class Circle : public Shape {
public:void draw() const override {std::cout << "○ 繪制圓形" << std::endl;}
};// 具體產品類:矩形
class Rectangle : public Shape {
public:void draw() const override {std::cout << "□ 繪制矩形" << std::endl;}
};// 具體產品類:三角形
class Triangle : public Shape {
public:void draw() const override {std::cout << "△ 繪制三角形" << std::endl;}
};// 簡單工廠類
class ShapeFactory {
public:// 形狀類型枚舉enum ShapeType { CIRCLE, RECTANGLE, TRIANGLE };// 創建形狀的靜態方法static std::unique_ptr<Shape> createShape(ShapeType type) {switch (type) {case CIRCLE:return std::make_unique<Circle>();case RECTANGLE:return std::make_unique<Rectangle>();case TRIANGLE:return std::make_unique<Triangle>();default:throw std::invalid_argument("錯誤:不支持的形狀類型");}}
};// 創建具體實列
int main() {// 創建圓形auto circle = ShapeFactory::createShape(ShapeFactory::CIRCLE);circle->draw();// 創建矩形auto rect = ShapeFactory::createShape(ShapeFactory::RECTANGLE);rect->draw();// 創建三角形auto triangle = ShapeFactory::createShape(ShapeFactory::TRIANGLE);triangle->draw();return 0;
}
優點:
-
實現簡單,易于理解
-
集中管理對象的創建邏輯
-
客戶端與具體產品類解耦
缺點:
-
違反開閉原則(添加新產品需修改工廠類)
-
工廠類職責過重(所有產品都在一個工廠中創建)
-
不支持運行時動態擴展
2. 工廠方法模式
工廠方法模式將對象創建延遲到子類,解決了簡單工廠的開閉原則問題。
#include <iostream>
#include <memory>
#include <vector>// 抽象產品:日志記錄器
class Logger {
public:virtual void log(const std::string& message) = 0;virtual ~Logger() = default;
};// 具體產品:文件日志
class FileLogger : public Logger {
public:void log(const std::string& message) override {std::cout << "[文件日志] " << message << std::endl;}
};// 具體產品:控制臺日志
class ConsoleLogger : public Logger {
public:void log(const std::string& message) override {std::cout << "[控制臺日志] " << message << std::endl;}
};// 具體產品:網絡日志
class NetworkLogger : public Logger {
public:void log(const std::string& message) override {std::cout << "[網絡日志] " << message << std::endl;}
};// 抽象創建者
class LoggerCreator {
public:virtual std::unique_ptr<Logger> createLogger() = 0;void logMessage(const std::string& message) {auto logger = createLogger();logger->log(message);}virtual ~LoggerCreator() = default;
};// 具體創建者:文件日志工廠
class FileLoggerCreator : public LoggerCreator {
public:std::unique_ptr<Logger> createLogger() override {return std::make_unique<FileLogger>();}
};// 具體創建者:控制臺日志工廠
class ConsoleLoggerCreator : public LoggerCreator {
public:std::unique_ptr<Logger> createLogger() override {return std::make_unique<ConsoleLogger>();}
};// 具體創建者:網絡日志工廠
class NetworkLoggerCreator : public LoggerCreator {
public:std::unique_ptr<Logger> createLogger() override {return std::make_unique<NetworkLogger>();}
};// 創建實例
int main() {std::vector<std::unique_ptr<LoggerCreator>> creators;creators.push_back(std::make_unique<FileLoggerCreator>());creators.push_back(std::make_unique<ConsoleLoggerCreator>());creators.push_back(std::make_unique<NetworkLoggerCreator>());for (auto& creator : creators) {creator->logMessage("應用啟動完成");}return 0;
}
核心思想:
-
定義創建對象的接口,但讓子類決定實例化哪個類
-
工廠方法使一個類的實例化延遲到其子類
-
符合"開閉原則" - 對擴展開放,對修改關閉
工廠方法模式的高級應用
1. 參數化工廠方法
class UniversalCreator : public LoggerCreator {
public:enum LoggerType { FILE, CONSOLE, NETWORK };UniversalCreator(LoggerType type) : type_(type) {}std::unique_ptr<Logger> createLogger() override {switch (type_) {case FILE: return std::make_unique<FileLogger>();case CONSOLE: return std::make_unique<ConsoleLogger>();case NETWORK: return std::make_unique<NetworkLogger>();default: throw std::invalid_argument("無效的日志類型");}}private:LoggerType type_;
};// 使用示例
int main() {UniversalCreator fileCreator(UniversalCreator::FILE);fileCreator.logMessage("保存到文件");UniversalCreator consoleCreator(UniversalCreator::CONSOLE);consoleCreator.logMessage("輸出到控制臺");return 0;
}
2. 工廠方法 + 單例模式
class Database {
public:virtual void connect() = 0;virtual ~Database() = default;
};class MySQL : public Database {
public:void connect() override {std::cout << "連接到MySQL數據庫" << std::endl;}static MySQL& getInstance() {static MySQL instance;return instance;}private:MySQL() = default;
};class PostgreSQL : public Database {
public:void connect() override {std::cout << "連接到PostgreSQL數據庫" << std::endl;}static PostgreSQL& getInstance() {static PostgreSQL instance;return instance;}private:PostgreSQL() = default;
};class DatabaseFactory {
public:virtual Database& create() = 0;virtual ~DatabaseFactory() = default;
};class MySQLFactory : public DatabaseFactory {
public:Database& create() override {return MySQL::getInstance();}
};class PostgreSQLFactory : public DatabaseFactory {
public:Database& create() override {return PostgreSQL::getInstance();}
};// 使用示例
int main() {MySQLFactory mysqlFactory;auto& mysql = mysqlFactory.create();mysql.connect();PostgreSQLFactory pgFactory;auto& pg = pgFactory.create();pg.connect();return 0;
}
工廠模式的優缺點分析
優點:
-
解耦創建者和具體產品
-
符合開閉原則,易于擴展
-
單一職責原則(創建邏輯集中)
-
便于代碼維護和測試
-
支持依賴倒置原則
缺點:
-
引入額外類,增加系統復雜性
-
需要設計良好的繼承體系
-
客戶端可能需要理解工廠結構
-
簡單場景下可能顯得過度設計
工廠模式的典型應用場景
-
框架設計:框架需要為應用提供擴展點
class Plugin { public:virtual void execute() = 0;virtual ~Plugin() = default; };class PluginFactory { public:virtual std::unique_ptr<Plugin> createPlugin() = 0; };
-
跨平臺開發:為不同平臺創建適配對象
class GUIButton { public:virtual void render() = 0; };class WindowsButton : public GUIButton { /*...*/ }; class MacButton : public GUIButton { /*...*/ };class GUIFactory { public:virtual std::unique_ptr<GUIButton> createButton() = 0; };
-
對象池管理:管理可重用對象的創建
class Connection { public:virtual void open() = 0; };class ConnectionPool { public:virtual std::unique_ptr<Connection> createConnection() = 0;virtual void returnConnection(std::unique_ptr<Connection>) = 0; };
-
依賴注入:通過工廠注入依賴對象
class Service { public:virtual void performTask() = 0; };class Client { public:Client(std::unique_ptr<ServiceFactory> factory): factory_(std::move(factory)) {}void execute() {auto service = factory_->createService();service->performTask();}private:std::unique_ptr<ServiceFactory> factory_; };
工廠模式的最佳實踐
-
優先使用工廠方法:除非系統非常簡單,否則優先選擇工廠方法模式
-
結合智能指針:使用
std::unique_ptr
或std::shared_ptr
管理對象生命周期 -
使用模板減少重復代碼
template <typename T> class StandardCreator : public LoggerCreator { public:std::unique_ptr<Logger> createLogger() override {return std::make_unique<T>();} };// 使用 StandardCreator<FileLogger> fileCreator;
-
工廠方法命名規范:
-
createXXX()
-
makeXXX()
-
newXXX()
-
getInstance()
?(單例場景)
工廠模式 vs 簡單工廠
特性 | 簡單工廠模式 | 工廠方法模式 |
---|---|---|
開閉原則 | 違反(修改工廠類) | 支持(擴展子類) |
復雜度 | 簡單 | 中等 |
擴展性 | 有限 | 優秀 |
類數量 | 較少 | 較多(每個產品對應工廠) |
適用場景 | 產品類型少且固定 | 產品類型多或可能擴展 |
總結
工廠模式是C++開發中強大的對象創建工具,通過封裝對象的創建過程,它實現了:
-
解耦:客戶端代碼與具體類實現分離
-
擴展性:輕松添加新產品而不影響現有代碼
-
可維護性:集中管理創建邏輯
-
靈活性:支持運行時決策和配置
在實際開發中,應當根據具體需求選擇合適的工廠模式變體:
-
對于簡單場景,簡單工廠足夠高效
-
對于需要高度擴展的系統,工廠方法是最佳選擇
-
避免過早優化,但為未來擴展留有余地
"設計模式不是銀彈,而是工具箱中的工具。工廠模式是其中最常用且強大的工具之一。" - 設計模式實踐者