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

依類型名稱動態建立物件:C++ Factory 模式實作指南

     編輯:關於C++

前言:為什麼需要動態建立物件?

在實際專案中,常面臨需透過統一介面控制多種型號裝置,且須支援未來擴充新型號的需求。若在編譯期硬編碼裝置配置,系統將失去彈性。理想目標是能在執行期根據字串型號名稱,動態建立對應的裝置物件實例,例如:Motor* motor = ClassByName("IM9001");。

核心架構:協議類與繼承體系

首先定義一個抽象基類 IntelligentMotor 作為協議介面,所有具體裝置類別(如 IM9001、IM9002)皆繼承此基類並實作純虛擬函式:

class IntelligentMotor {
public:
    IntelligentMotor(const std::string& port_name);
    virtual bool Start() = 0;
    virtual bool Stop() = 0;
    virtual ~IntelligentMotor();
};

class IM9001 : public IntelligentMotor {
public:
    IM9001(const std::string& port_name);
    virtual bool Start();
    virtual bool Stop();
    virtual ~IM9001();
private:
    // 具體實作細節...
};

class IM9002 : public IntelligentMotor { /* ... */ };
// 更多型號...

演進 1:簡單條件分支

最直覺的做法是在 ClassByName 中使用 if-else 鏈:

IntelligentMotor* ClassByName(const std::string& model_name, const std::string& port_name) {
    if (model_name == "IM9001") return new IM9001(port_name);
    else if (model_name == "IM9002") return new IM9002(port_name);
    // 所有支援型號...
    else throw "不支援該型號裝置。";
}

缺點: 型號增加時代碼膨脹,維護性差,違反開放封閉原則。

演進 2:映射表儲存實例指標

建立型號名稱到物件指標的映射表:

struct Map { const std::string ModelName; IntelligentMotor* Device; };
Map ModelTable[] = { {"IM9001", new IM9001}, {"IM9002", new IM9002} };

嚴重缺陷:

  1. 類別缺乏預設建構函式,無法編譯通過。
  2. 預先建立所有型號實例造成記憶體浪費,實際可能僅使用少數型號。

演進 3:映射表儲存建構器

改為儲存函式指標,延遲建立實例:

template <class T>
IntelligentMotor* Creator(const std::string& port_name) { return new T(port_name); }

typedef IntelligentMotor* (*CreatorFunc)(const std::string&);
struct Map { const std::string ModelName; CreatorFunc DeviceCreator; };
Map model_table[] = { 
    {"IM9001", &Creator<IM9001>}, 
    {"IM9002", &Creator<IM9002>} 
};

IntelligentMotor* ClassByName(const std::string& model_name, const std::string& port_name) {
    for (auto& entry : model_table) {
        if (model_name == entry.ModelName) return entry.DeviceCreator(port_name);
    }
    throw "不支援該型號裝置。";
}

此法解決記憶體浪費與建構函式問題,且擴充新型號只需在表格加入一行。

演進 4:使用關聯容器優化查找

若型號眾多,線性搜尋效率低,改用 std::map 實現 O(log N) 查找:

typedef std::map<std::string, CreatorFunc> LookupTable;
LookupTable lookup_table;
// 初始化時填入 model_table 內容...

IntelligentMotor* ClassByName(const std::string& model_name, const std::string& port_name) {
    auto it = lookup_table.find(model_name);
    if (it == lookup_table.end()) throw "不支援該型號裝置。";
    return it->second(port_name);
}

此為「一次建立索引,多次攤銷成本」策略,視實際情況權衡。

工程化建議:封裝為獨立函式庫

將實作封裝為動態連結庫,僅輸出 ClassByName 介面與基類定義。新增型號時只需重新編譯對應模組,主程式無需重新編譯甚至重新連結,實現真正的模組化擴充。

總結與延伸閱讀

本文示範了從簡單條件判斷演進至 Factory Pattern 的完整過程。更通用、高效的物件工廠實作請參考 Andrei Alexandrescu 《Modern C++ Design》第 8 章 Object Factory。

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