【问题标题】:How to design a simple C++ object factory?如何设计一个简单的 C++ 对象工厂?
【发布时间】:2008-12-02 09:02:24
【问题描述】:

在我的应用程序中,有 10-20 个类被实例化一次[*]。这是一个例子:

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};

类的实例包含在一个对象中:

class TheManager {
public:
    virtual SomeManagerClass* someManagerClass() const;
    virtual SomeOtherManager* someOtherManager() const;
    /** More objects... up to 10-20 */
};

目前 TheManager 使用 new 操作符来创建对象。

我的意图是能够使用插件替换 SomeManagerClass (或任何其他类)实现与另一个。为了替换实现,需要两个步骤:

  1. 定义一个类 DerivedSomeManagerClass,继承 SomeManagerClass [插件]
  2. 创建新类 (DerivedSomeManagerClass) 而不是默认的 (SomeManagerClass) [应用程序]

我想我需要某种对象工厂,但它应该相当简单,因为总是只有一种类型可以创建(默认实现或用户实现)。

你知道如何设计一个像我刚才描述的那样简单的工厂吗?考虑到将来可能会有更多类,所以应该很容易扩展。

[*] 我不在乎它是否不止一次发生。

编辑:请注意,TheManager 中包含两个以上的对象。

【问题讨论】:

  • 您想在运行时还是在设计时替换 SomeManagerClass 实例?
  • SomeManagerClass 只创建一次 - 在应用程序启动时,所以我想更改这个创建。但它应该在运行时,因为替换默认实现的代码驻留在动态链接的插件中。
  • FWIW,我认为任何时候你将一个类命名为“Manager”,这是因为你不知道它的真正作用。你也可以称它为“盒子”或“东西”。这个名字是一种气味,恕我直言。

标签: c++ factory


【解决方案1】:

假设一个类 (plugin1) 继承自 SomeManagerClass,您需要一个类层次结构来构建您的类型:

class factory
{
public:
    virtual SomeManagerClass* create() = 0;
};

class plugin1_factory : public factory
{
public:
    SomeManagerClass* create() { return new plugin1(); }
};

然后你可以将这些工厂分配给一个 std::map,在那里它们被绑定到字符串

std::map<string, factory*>  factory_map;
...
factory_map["plugin1"] = new plugin1_factory();

最后,您的 TheManager 只需要知道插件的名称(作为字符串),并且只需一行代码即可返回 SomeManagerClass 类型的对象:

SomeManagerClass* obj = factory_map[plugin_name]->create();

编辑:如果你不喜欢每个插件都有一个插件工厂类,你可以修改之前的模式:

template <class plugin_type>
class plugin_factory : public factory
{
public:
   SomeManagerClass* create() { return new plugin_type(); }
};

factory_map["plugin1"] = new plugin_factory<plugin1>();

我认为这是一个更好的解决方案。此外,如果您将字符串传递给 costructor,则“plugin_factory”类可以将自身添加到“factory_map”中。

【讨论】:

    【解决方案2】:

    我认为这里有两个不同的问题。

    一个问题是:TheManager 如何命名它必须创建的类?它必须保留某种指向“创建类的方法”的指针。可能的解决方案是:

    • 为每种类保留一个单独的指针,并提供一种设置方法,但您已经说过您不喜欢这样,因为它违反了 DRY 原则
    • 保留某种表,其中键是枚举或字符串;在这种情况下,setter 是带有参数的单个函数(当然,如果键是枚举,您可以使用向量而不是映射)

    另一个问题是:这种“创建类的方式”是什么?不幸的是,我们不能直接存储指向构造函数的指针,但是我们可以:

    • 正如其他人指出的那样,为每个类创建一个工厂
    • 只需为每个类添加一个静态“创建”函数;如果他们保持一致的签名,你可以只使用他们指向函数的指针

    模板可以帮助避免在这两种情况下不必要的代码重复。

    【讨论】:

    • 这个答案让我重新思考了各种设计和实现的可能性。作为其他答案的指南,它也很好,所以它得到了赏金。感谢大家的参与,我没有足够的字符来感谢大家:)
    • @UncleZeiv 这么多年了,我还是恨你“偷”了我的徽章:P
    • @Emiliano 你的意思是赏金:) 我什至支持你!
    【解决方案3】:

    我已经回答了关于 C++ 工厂的另一个 SO 问题。如果对灵活工厂感兴趣,请参阅there。我尝试从 ET++ 中描述一种使用宏的旧方法,这对我来说非常有用。

    ET++ 是一个将旧 MacApp 移植到 C++ 和 X11 的项目。在此过程中,Eric Gamma 等人开始考虑设计模式

    【讨论】:

      【解决方案4】:

      我将创建一个“基础”工厂,它具有用于创建所有基本管理器的虚拟方法,并让“元管理器”(您的问题中的 TheManager)将指向基础工厂的指针作为构造函数参数。

      我假设“工厂”可以通过派生 CXYZWManager 的实例来自定义它们,但是 CXYZWManager 的构造函数可以在“自定义”工厂中采用不同的参数。

      输出“CSomeManager”和“CDerivedFromSomeManager”的冗长代码示例:

      #include <iostream>
      //--------------------------------------------------------------------------------
      class CSomeManager
        {
        public:
          virtual const char * ShoutOut() { return "CSomeManager";}
        };
      
      //--------------------------------------------------------------------------------
      class COtherManager
        {
        };
      
      //--------------------------------------------------------------------------------
      class TheManagerFactory
        {
        public:
          // Non-static, non-const to allow polymorphism-abuse
          virtual CSomeManager   *CreateSomeManager() { return new CSomeManager(); }
          virtual COtherManager  *CreateOtherManager() { return new COtherManager(); }
        };
      
      //--------------------------------------------------------------------------------
      class CDerivedFromSomeManager : public CSomeManager
        {
        public:
          virtual const char * ShoutOut() { return "CDerivedFromSomeManager";}
        };
      
      //--------------------------------------------------------------------------------
      class TheCustomManagerFactory : public TheManagerFactory
        {
        public:
          virtual CDerivedFromSomeManager        *CreateSomeManager() { return new CDerivedFromSomeManager(); }
      
        };
      
      //--------------------------------------------------------------------------------
      class CMetaManager
        {
        public:
          CMetaManager(TheManagerFactory *ip_factory)
            : mp_some_manager(ip_factory->CreateSomeManager()),
              mp_other_manager(ip_factory->CreateOtherManager())
            {}
      
          CSomeManager  *GetSomeManager()  { return mp_some_manager; }
          COtherManager *GetOtherManager() { return mp_other_manager; }
      
        private:
          CSomeManager  *mp_some_manager;
          COtherManager *mp_other_manager;
        };
      
      //--------------------------------------------------------------------------------
      int _tmain(int argc, _TCHAR* argv[])
        {
        TheManagerFactory standard_factory;
        TheCustomManagerFactory custom_factory;
      
        CMetaManager meta_manager_1(&standard_factory);
        CMetaManager meta_manager_2(&custom_factory);
      
        std::cout << meta_manager_1.GetSomeManager()->ShoutOut() << "\n";
        std::cout << meta_manager_2.GetSomeManager()->ShoutOut() << "\n";
        return 0;
        }
      

      【讨论】:

      • 我喜欢这个解决方案,但考虑一下 TheManagerFactory 需要返回 20 个不同类的情况。这意味着它必须知道(使用#include 语句)所有 20 个类。
      • 基础工厂类可以前向声明头文件中的所有管理器类型(无依赖关系),虽然 cpp 将包含所有管理器,但它应该只需要构建一次(或几次)。自定义工厂只需要#include 他们覆盖的任何管理器。
      【解决方案5】:

      这是我想到的解决方案,它不是最好的,但也许它会帮助你想出更好的解决方案:

      每个类都有一个创建者类:

      class SomeManagerClassCreator {
      public:
          virtual SomeManagerClass* create(SomeOtherManager* someOtherManager) { 
              return new SomeManagerClass(someOtherManager); 
          }
      };
      

      然后,创作者将聚集在一个班级中:

      class SomeManagerClassCreator;
      class SomeOtherManagerCreator;
      
      class TheCreator {
      public:
          void setSomeManagerClassCreator(SomeManagerClassCreator*);
          SomeManagerClassCreator* someManagerClassCreator() const;
      
          void setSomeOtherManagerCreator(SomeOtherManagerCreator*);
          SomeOtherManagerCreator* someOtherManagerCreator() const;
      private:
          SomeManagerClassCreator* m_someManagerClassCreator;
          SomeOtherManagerCreator* m_someOtherManagerCreator;
      };
      

      TheManager 将使用 TheCreator 创建用于内部创建:

      class TheManager {
      public:
          TheManager(TheCreator*);
          /* Rest of code from above */
      };
      

      这个解决方案的问题在于它违反了 DRY - 对于每个类创建者,我必须在 TheCreator 中编写 setter/getter。

      【讨论】:

        【解决方案6】:

        与抽象工厂模式相比,函数模板似乎要简单得多

        class ManagerFactory
        {
        public:
            template <typename T> static BaseManager * getManager() { return new T();}
        };
        
        BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();
        

        如果你想通过字符串获取它们,你可以创建一个从字符串到函数指针的标准映射。这是一个有效的实现:

        #include <map>
        #include <string>
        
        class BaseManager
        {
        public:
            virtual void doSomething() = 0;
        };
        
        class DerivedManager1 : public BaseManager
        {
        public:
            virtual void doSomething() {};
        };
        
        class DerivedManager2 : public BaseManager
        {
        public:
            virtual void doSomething() {};
        };
        
        class ManagerFactory
        {
        public:
            typedef BaseManager * (*GetFunction)();
            typedef std::map<std::wstring, GetFunction> ManagerFunctionMap;
        private:
            static ManagerFunctionMap _managers;
        
        public:
            template <typename T> static BaseManager * getManager() { return new T();}
            template <typename T> static void registerManager(const std::wstring& name)
            {
                _managers[name] = ManagerFactory::template getManager<T>;
            }
            static BaseManager * getManagerByName(const std::wstring& name)
            {
                if(_managers.count(name))
                {
                    return _managers[name]();
                }
                return NULL;
            }
        };
        // the static map needs to be initialized outside the class
        ManagerFactory::ManagerFunctionMap ManagerFactory::_managers;
        
        
        int _tmain(int argc, _TCHAR* argv[])
        {
            // you can get with the templated function
            BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();
            manager1->doSomething();
            // or by registering with a string
            ManagerFactory::template registerManager<DerivedManager1>(L"Derived1");
            ManagerFactory::template registerManager<DerivedManager2>(L"Derived2");
            // and getting them
            BaseManager * manager2 = ManagerFactory::getManagerByName(L"Derived2");
            manager2->doSomething();
            BaseManager * manager3 = ManagerFactory::getManagerByName(L"Derived1");
            manager3->doSomething();
            return 0;
        }
        

        编辑:在阅读其他答案时,我意识到这与 Dave Van den Eynde 的 FactorySystem 解决方案非常相似,但我使用的是函数模板指针而不是实例化模板工厂类。我认为我的解决方案更轻量级。由于静态函数,唯一被实例化的对象是地图本身。如果你需要工厂执行其他功能(DestroyManager等),我认为他的解决方案更具扩展性。

        【讨论】:

          【解决方案7】:

          您可以使用返回 Manager-Class 实例的静态方法来实现对象工厂。在工厂中,您可以为默认类型的管理器创建一个方法,并为任何类型的管理器创建一个方法,您可以为它提供一个表示 Manager-Class 类型的参数(例如使用枚举)。最后一个方法应该返回一个接口而不是一个类。

          编辑:我会尝试提供一些代码,但请注意,我的 C++ 时代已经很久了,我暂时只做 Java 和一些脚本。

          class Manager { // aka Interface
              public: virtual void someMethod() = 0;
          };
          
          class Manager1 : public Manager {
              void someMethod() { return null; }
          };
          
          class Manager2 : public Manager {
              void someMethod() { return null; }
          };
          
          enum ManagerTypes {
              Manager1, Manager2
          };
          
          class ManagerFactory {
              public static Manager* createManager(ManagerTypes type) {
                  Manager* result = null;
                  switch (type) {
                  case Manager1:
                       result = new Manager1();
                       break;
                  case Manager2:
                       result = new Manager2();
                       break;
                  default:
                       // Do whatever error logging you want
                       break;
                  }
                  return result;
               }
           };
          

          现在您应该可以通过以下方式调用工厂(如果您能够使代码示例工作):

          Manager* manager = ManagerFactory.createManager(ManagerTypes.Manager1);
          

          【讨论】:

          • 感谢您的代码,我的问题是我想替换“new Manager2();”创建派生类的语句。
          • 我不明白这一点,抱歉。您可以扩展枚举和工厂以派生其他类。
          【解决方案8】:

          我会使用这样的模板,因为我看不到工厂类的意义:

          class SomeOtherManager;
          
          class SomeManagerClass {
          public:
              SomeManagerClass(SomeOtherManager*);
              virtual void someMethod1();
              virtual void someMethod2();
          };
          
          
          class TheBaseManager {
          public:
                // 
          };
          
          template <class ManagerClassOne, class ManagerClassOther> 
          class SpecialManager : public TheBaseManager {
              public:
                  virtual ManagerClassOne* someManagerClass() const;
                  virtual ManagerClassOther* someOtherManager() const;
          };
          
          TheBaseManager* ourManager = new SpecialManager<SomeManagerClass,SomeOtherManager>;
          

          【讨论】:

            【解决方案9】:

            你应该看看教程 http://downloads.sourceforge.net/papafactory/PapaFactory20080622.pdf?use_mirror=fastbull

            它包含一个很棒的教程,介绍了用 C++ 实现抽象工厂,并且附带的源代码也非常强大

            克里斯

            【讨论】:

              【解决方案10】:

              嗯,我百分百看不懂,而且我并不是真正喜欢书籍和文章中的工厂产品。


              如果您的所有管理器共享一个类似的接口,您可以从一个基类派生,并在您的程序中使用这个基类。 根据将在何处创建哪个类的决定,您必须使用标识符进行创建(如上所述)或处理在内部实例化哪个管理器的决定。


              另一种方法是通过使用模板来实现它的“策略”。这样 You ManagerClass::create() 返回一个特定的 SomeOtherManagerWhatever 实例。这将决定哪个经理在使用您的经理的代码中做出 - Maye 这不是故意的。

              或者那样:


              template<class MemoryManagment>
              class MyAwesomeClass
              {
                  MemoryManagment m_memoryManager;
              };
              

              (或类似的东西) 使用此构造,您只需更改 MyAwesomeClass 的实例即可轻松使用其他管理器。


              此外,用于此目的的类可能有点过头了。在您的情况下,我猜想使用工厂功能。嗯,这更多的是个人喜好问题。

              【讨论】:

                【解决方案11】:

                如果您计划支持动态链接的插件,您的程序将需要提供稳定的 ABI(应用程序二进制接口),这意味着您不能使用 C++ 作为主接口,因为 C++ 没有标准 ABI。

                如果您希望插件实现您自己定义的接口,您必须向插件程序员提供接口的头文件,并标准化一个非常简单的 C 接口,以便创建和删除对象。

                您不能提供允许您按原样“新建”插件类的动态库。这就是为什么您需要标准化 C 接口以创建对象的原因。只要您的参数都不使用可能不兼容的类型,例如 STL 容器,就可以使用 C++ 对象。您将无法使用其他库返回的向量,因为您无法确保它们的 STL 实现与您的相同。

                经理.h

                class Manager
                {
                public:
                  virtual void doSomething() = 0;
                  virtual int doSomethingElse() = 0;
                }
                
                extern "C" {
                Manager* newManager();
                void deleteManager(Manager*);
                }
                

                插件管理器.h

                #include "Manager.h"
                
                class PluginManager : public Manager
                {
                public:
                  PluginManager();
                  virtual ~PluginManager();
                
                public:
                  virtual void doSomething();
                  virtual int doSomethingElse();
                }
                

                插件管理器.cpp

                #include "PluginManager.h"
                
                Manager* newManager()
                {
                  return new PluginManager();
                }
                void deleteManager(Manager* pManager)
                {
                  delete pManager;
                }
                
                PluginManager::PluginManager()
                {
                  // ...
                }
                
                PluginManager::~PluginManager()
                {
                  // ...
                }
                
                void PluginManager::doSomething()
                {
                  // ...
                }
                
                int PluginManager::doSomethingElse()
                {
                  // ...
                }
                

                【讨论】:

                  【解决方案12】:

                  你没有谈论 TheManager。看起来你想要控制正在使用哪个类?或者你想把它们串起来?

                  听起来您需要一个抽象基类和一个指向当前使用的类的指针。如果您希望链接,您可以在抽象类和管理器类中进行。如果是抽象类,则将成员添加到链中的下一个类,如果是管理器,则按您在列表中使用的顺序对其进行排序。您需要一种添加类的方法,因此您需要在管理器中添加一个 addMe()。听起来您知道自己的选择应该是正确的。我的建议是一个带有 addMe 函数的列表,如果你只想要一个活动类,那么 TheManager 中的一个函数决定它会很好。

                  【讨论】:

                    【解决方案13】:

                    这可能比您需要的要重,但听起来您正在尝试制作一个支持插件的框架工作类。

                    我会把它分成三个部分。

                    1) FrameWork 类将拥有插件。 该类负责发布插件提供的接口。

                    2) PlugIn 类将拥有完成工作的组件。 该类负责注册导出的接口,并将导入的接口绑定到组件。

                    3) 第三部分,组件是接口的提供者和消费者。

                    为了使事物具有可扩展性,启动和运行可能会分成多个阶段。

                    1. 创造一切。
                    2. 把所有东西都连接起来。
                    3. 开始一切。

                    分解事物。

                    1. 停止一切。
                    2. 摧毁一切。
                    类 IFrameWork { 上市: 虚拟 ~IFrameWork() {} 虚拟 void RegisterInterface( const char*, void* ) = 0; 虚拟 void* GetInterface( const char* name ) = 0; }; 类IPlugIn { 上市: 虚拟 ~IPlugIn() {} 虚拟 void BindInterfaces(IFrameWork* frameWork) {}; 虚拟无效开始(){}; 虚拟无效停止(){}; }; 结构 SamplePlugin :public IPlugIn { ILogger* 记录器; 组件1组件1; 网络服务器网络服务器; 上市: SamplePlugin(IFrameWork* 框架) :logger( (ILogger*)frameWork->GetInterface( "ILogger" ) ), //假设'System'插件暴露了这个 组件1(), 网络服务器(组件 1) { logger->Log("MyPlugin Tor()"); frameWork->RegisterInterface("ICustomerManager", dynamic_cast(&component1)); frameWork->RegisterInterface("IVendorManager", dynamic_cast(&component1)); frameWork->RegisterInterface("IAAccountingManager", dynamic_cast(&webServer)); } 虚拟无效 BindInterfaces(IFrameWork* 框架){ logger->Log("MyPlugin BindInterfaces()"); IProductManager* productManager(static_cast(frameWork->GetInterface("IProductManager"))); IshippingManager* shippingManager(static_cast(frameWork->GetInterface("IshippingManager"))); component1.BindInterfaces(记录器,productManager); webServer.BindInterfaces(记录器,productManager,shippingManager); } 虚拟无效开始(){ logger->Log("MyPlugin Start()"); webServer.Start(); } 虚拟无效停止(){ logger->Log("MyPlugin Stop()"); webServer.Stop(); } }; 类框架:公共 IFrameWork { 矢量插件; 地图界面; 上市: 虚拟无效寄存器接口(常量字符*名称,无效* itfc){ 接口[名称] = itfc; } 虚拟 void* GetInterface( const char* name ) { 返回接口[名称]; } 框架() { //只有'SystemPlugin'中的接口可以被其他插件的所有方法使用 plugins.push_back(new SystemPlugin(this)); plugins.push_back(new SamplePlugin(this)); //这里添加其他插件 for_each(plugIns.begin(),plugIns.end(),bind2nd(mem_fun(&IPlugIn::BindInterfaces),this)); for_each(plugIns.begin(), plugins.end(), mem_fun(&IPlugIn::Start)); } 〜框架(){ for_each(plugIns.rbegin(), plugins.rend(), mem_fun(&IPlugIn::Stop)); for_each(plugIns.rbegin(), plugins.rend(), Delete()); } };

                    【讨论】:

                      【解决方案14】:

                      这是我在大约 15 分钟内想出的最小工厂模式实现。我们使用一个类似的,它使用更高级的基类。

                      #include "stdafx.h"
                      #include <map>
                      #include <string>
                      
                      class BaseClass
                      {
                      public:
                          virtual ~BaseClass() { }
                          virtual void Test() = 0;
                      };
                      
                      class DerivedClass1 : public BaseClass 
                      { 
                      public:
                          virtual void Test() { } // You can put a breakpoint here to test.
                      };
                      
                      class DerivedClass2 : public BaseClass 
                      { 
                      public:
                          virtual void Test() { } // You can put a breakpoint here to test.
                      };
                      
                      class IFactory
                      {
                      public:
                          virtual BaseClass* CreateNew() const = 0;
                      };
                      
                      template <typename T>
                      class Factory : public IFactory
                      {
                      public:
                          T* CreateNew() const { return new T(); }
                      };
                      
                      class FactorySystem
                      {
                      private:
                          typedef std::map<std::wstring, IFactory*> FactoryMap;
                          FactoryMap m_factories;
                      
                      public:
                          ~FactorySystem()
                          {
                              FactoryMap::const_iterator map_item = m_factories.begin();
                              for (; map_item != m_factories.end(); ++map_item) delete map_item->second;
                              m_factories.clear();
                          }
                      
                          template <typename T>
                          void AddFactory(const std::wstring& name)
                          {
                              delete m_factories[name]; // Delete previous one, if it exists.
                              m_factories[name] = new Factory<T>();
                          }
                      
                          BaseClass* CreateNew(const std::wstring& name) const
                          {
                              FactoryMap::const_iterator found = m_factories.find(name);
                              if (found != m_factories.end())
                                  return found->second->CreateNew();
                              else
                                  return NULL; // or throw an exception, depending on how you want to handle it.
                          }
                      };
                      
                      int _tmain(int argc, _TCHAR* argv[])
                      {
                          FactorySystem system;
                          system.AddFactory<DerivedClass1>(L"derived1");
                          system.AddFactory<DerivedClass2>(L"derived2");
                      
                          BaseClass* b1 = system.CreateNew(L"derived1");
                          b1->Test();
                          delete b1;
                          BaseClass* b2 = system.CreateNew(L"derived2");
                          b2->Test();
                          delete b2;
                      
                          return 0;
                      }
                      

                      只需复制并粘贴到 VS2005/2008 中的初始 Win32 控制台应用程序即可。我想指出一点:

                      • 您不需要为每个类创建一个具体的工厂。模板会为您做到这一点。
                      • 我喜欢将整个工厂模式放在自己的类中,这样您就不必担心创建工厂对象和删除它们。您只需注册您的类,一个工厂类由编译器创建,一个工厂对象由模式创建。在其生命周期结束时,所有工厂都被彻底摧毁。我喜欢这种封装形式,因为不会混淆由谁来管理工厂的生命周期。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多