多態工廠設計通過模板與靜態多態避免rtti實現動態對象創建,其核心在于使用工廠注冊表根據類型標識符生成對象。1. 定義統一基類與派生類;2. 創建工廠接口與具體工廠模板;3. 使用注冊表管理工廠實例;4. 利用智能指針或raii原則管理內存以防止泄漏;5. 應用于游戲開發中創建角色、特效、ui元素等不同對象。相比抽象工廠,多態工廠側重單個產品族的動態創建,而抽象工廠關注多個相關產品族的創建與兼容性。
多態工廠設計旨在解決一個核心問題:如何在運行時根據不同的條件創建不同類型的對象,而無需依賴運行時類型識別(RTTI)。簡而言之,它提供了一種靈活的方式來動態生成對象,同時避免了RTTI帶來的潛在性能問題和代碼復雜性。
解決方案
多態工廠的核心思想是利用模板和靜態多態(CRTP,Curiously Recurring Template Pattern)來創建一個類型安全的工廠,該工廠可以根據類型標識符創建相應的對象。
-
基類和派生類:
首先,定義一個所有可創建對象都繼承的基類。
class Base { public: virtual ~Base() = default; virtual void doSomething() = 0; }; class DerivedA : public Base { public: void doSomething() override { std::cout << "DerivedA doing something" << std::endl; } }; class DerivedB : public Base { public: void doSomething() override { std::cout << "DerivedB doing something" << std::endl; } };
-
工廠接口:
定義一個工廠接口,它提供一個創建對象的方法。
class Factory { public: virtual Base* create() = 0; virtual ~Factory() = default; };
-
具體工廠:
為每個可創建的類創建一個具體的工廠類,該工廠類繼承自工廠接口,并負責創建該類的對象。
template <typename T> class ConcreteFactory : public Factory { public: Base* create() override { return new T(); } };
-
工廠注冊表:
創建一個工廠注冊表,用于存儲所有可用的工廠。可以使用std::map或std::unordered_map,將類型標識符映射到相應的工廠。
#include <map> #include <string> #include <iostream> class FactoryRegistry { public: using FactoryMap = std::map<std::string, Factory*>; template <typename T> static void registerFactory(const std::string& typeName) { instance().m_factories[typeName] = new ConcreteFactory<T>(); } static Base* createObject(const std::string& typeName) { auto it = instance().m_factories.find(typeName); if (it != instance().m_factories.end()) { return it->second->create(); } return nullptr; // Or throw an exception } private: FactoryRegistry() = default; ~FactoryRegistry() { for (auto& pair : m_factories) { delete pair.second; } } static FactoryRegistry& instance() { static FactoryRegistry registry; return registry; } FactoryMap m_factories; };
-
注冊工廠:
在程序啟動時,將所有具體的工廠注冊到工廠注冊表中。
int main() { FactoryRegistry::registerFactory<DerivedA>("DerivedA"); FactoryRegistry::registerFactory<DerivedB>("DerivedB"); Base* objA = FactoryRegistry::createObject("DerivedA"); if (objA) { objA->doSomething(); delete objA; } Base* objB = FactoryRegistry::createObject("DerivedB"); if (objB) { objB->doSomething(); delete objB; } return 0; }
如何避免內存泄漏?
內存泄漏是使用工廠模式時需要特別注意的問題。以下是一些避免內存泄漏的方法:
- 智能指針: 使用智能指針(如std::unique_ptr或std::shared_ptr)來管理工廠創建的對象。這樣,當對象不再需要時,智能指針會自動釋放內存。
- RAII: 確保所有分配的資源都在對象析構函數中釋放。這可以通過使用RAII(Resource Acquisition Is Initialization)原則來實現。
- 工廠所有權: 明確工廠是否負責管理對象的生命周期。如果工廠負責,則需要在工廠的析構函數中釋放所有已創建的對象。
- 避免裸指針: 盡量避免在代碼中使用裸指針。使用智能指針可以更容易地管理內存,并減少內存泄漏的風險。
- 代碼審查和測試: 定期進行代碼審查和測試,以發現潛在的內存泄漏問題。可以使用內存泄漏檢測工具來幫助識別這些問題。
多態工廠和抽象工廠的區別?
多態工廠和抽象工廠都是創建型設計模式,但它們解決的問題略有不同。
- 多態工廠: 主要關注于根據類型標識符創建不同類型的對象,而無需依賴 RTTI。它通常用于創建單個產品族中的不同產品。上述解決方案就是一個多態工廠的例子。
- 抽象工廠: 主要關注于創建一組相關的產品族,而無需指定具體的類。它通常用于創建多個產品族,并確保這些產品族之間的兼容性。
例如,一個抽象工廠可以用于創建不同操作系統的 UI 元素(如按鈕、文本框等)。每個操作系統都有自己的具體工廠,負責創建該操作系統下的 UI 元素。
多態工廠在游戲開發中的應用場景?
多態工廠在游戲開發中有很多應用場景,以下是一些常見的例子:
- 創建游戲對象: 可以使用多態工廠來創建不同類型的游戲對象,如角色、敵人、道具等。每個游戲對象都有自己的具體類,工廠可以根據游戲對象的類型標識符創建相應的對象。
- 創建特效: 可以使用多態工廠來創建不同類型的特效,如爆炸、火焰、煙霧等。每個特效都有自己的具體類,工廠可以根據特效的類型標識符創建相應的對象。
- 創建 UI 元素: 可以使用多態工廠來創建不同類型的 UI 元素,如按鈕、文本框、滑塊等。每個 UI 元素都有自己的具體類,工廠可以根據 UI 元素的類型標識符創建相應的對象。
- 創建 AI 代理: 可以使用多態工廠來創建不同類型的 AI 代理,例如不同的敵人AI行為。
總而言之,多態工廠模式在需要運行時動態創建對象,同時又希望避免 RTTI 的情況下,是一種非常有效的解決方案。通過合理的設計和實現,可以提高代碼的靈活性、可維護性和可擴展性。