代码之家  ›  专栏  ›  技术社区  ›  kshahar

如何设计一个简单的C++对象工厂?

  •  12
  • kshahar  · 技术社区  · 17 年前

    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 */
    };
    

    当前,管理器使用 运算符以创建对象。

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

    1. 定义派生的SomeManagerClass类,该类继承SomeManagerClass[插件]

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

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

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

    14 回复  |  直到 17 年前
        1
  •  19
  •   Emiliano    17 年前

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

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

    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\u factory”类可以将自己添加到“factory\u map”中。

        2
  •  10
  •   UncleZeiv    17 年前

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

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

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

    • 只需为每个类添加一个静态的“create”函数;如果它们保持一致的签名,您可以使用它们的函数指针

    在这两种情况下,模板都有助于避免不必要的代码重复。

        3
  •  3
  •   Community Mohan Dere    9 年前

    我在另一个关于C++工厂的问题上回答了问题。请看 there 如果一个灵活的工厂感兴趣。我试图描述一种从ET++到使用宏的老方法,这种方法对我来说非常有效。

    ET++ 是一个将旧Mac App移植到C++和X11的项目。在它的努力下,Eric Gamma等开始思考

        4
  •  2
  •   Joris Timmermans    17 年前

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

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

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

    #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;
      }
    
        5
  •  2
  •   kshahar    17 年前

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

    每个类都有一个creator类:

    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;
    };
    

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

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

        6
  •  2
  •   vishvananda    17 年前

    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
  •  1
  •   boutta tomriddle_1234    17 年前

    编辑:我会尝试给出一些代码,但是要注意我的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);
    
        8
  •  1
  •   David Allan Finch    17 年前

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

    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
  •  1
  •   chris chris    17 年前

    http://downloads.sourceforge.net/papafactory/PapaFactory20080622.pdf?use_mirror=fastbull

    它包含了C++中实现抽象工厂的一个很好的教程,它附带的源代码也非常健壮。

    克里斯

        10
  •  0
  •   Ronny Brendel    17 年前


    根据将创建哪个类的决策将在何处做出,您必须使用标识符进行创建(如上所述),或者处理要在内部实例化哪个管理器的决策。


    另一种方法是像使用模板一样实现it“策略”。因此,您可以使用ManagerClass::create()返回特定的SomeOtherManagerWhatver实例。这将在使用您的经理的代码中确定要由哪个经理做出决定-可能这不是故意的。


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

    (或诸如此类)


    同样,用于此目的的类可能有点过头了。在您的情况下,我想工厂函数就可以了。这更多的是个人偏好的问题。

        11
  •  0
  •   Vincent Robert    17 年前

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

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

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

    经理

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

    PluginManager.h

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

    PluginManager.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
  •  0
  •   user34537 user34537    17 年前

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

        13
  •  0
  •   jyoung    17 年前

    这可能比您需要的要重,但听起来您正试图创建一个支持插件的框架类。

    1) 框架类将拥有这些插件。 这个类负责发布插件提供的接口。

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

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

    1. 创造一切。
    2. 把一切都接好。
    3. 开始一切。

    把事情搞糟。

    1. 停止一切。
    2. 摧毁一切。
    class IFrameWork {
    public:
        virtual ~IFrameWork() {}
        virtual void RegisterInterface( const char*, void* ) = 0;
        virtual void* GetInterface( const char* name ) = 0;
    };
    
    class IPlugIn {
    public:
        virtual ~IPlugIn() {}
        virtual void BindInterfaces( IFrameWork* frameWork ) {};
        virtual void Start() {};
        virtual void Stop() {};
    };
    
    struct SamplePlugin :public IPlugIn {
        ILogger* logger;
    
        Component1 component1;
        WebServer  webServer;
    
    public:
        SamplePlugin( IFrameWork* frameWork ) 
            :logger( (ILogger*)frameWork->GetInterface( "ILogger" ) ),  //assumes the 'System' plugin exposes this
            component1(),
            webServer( component1 )
        {
            logger->Log( "MyPlugin Ctor()" );
    
            frameWork->RegisterInterface( "ICustomerManager", dynamic_cast( &component1 ) ); 
            frameWork->RegisterInterface( "IVendorManager", dynamic_cast( &component1 ) ); 
            frameWork->RegisterInterface( "IAccountingManager", dynamic_cast( &webServer ) ); 
        }
    
        virtual void BindInterfaces( IFrameWork* frameWork ) {
            logger->Log( "MyPlugin BindInterfaces()" );
    
            IProductManager* productManager( static_cast( frameWork->GetInterface( "IProductManager" ) ) );
            IShippingManager* shippingManager( static_cast( frameWork->GetInterface( "IShippingManager" ) ) );
    
            component1.BindInterfaces( logger, productManager );
            webServer.BindInterfaces( logger, productManager, shippingManager );
        }
    
        virtual void Start() {
            logger->Log( "MyPlugin Start()" );
    
            webServer.Start();
        }
    
        virtual void Stop() {
            logger->Log( "MyPlugin Stop()" );
    
            webServer.Stop();
        }
    };
    
    class FrameWork :public IFrameWork {
        vector plugIns;
        map interfaces;
    public:
        virtual void RegisterInterface( const char* name, void* itfc ) {
            interfaces[ name ] = itfc;
        }
        virtual void* GetInterface( const char* name )  {
            return interfaces[ name ];
        }
    
        FrameWork() {
            //Only interfaces in 'SystemPlugin' can be used by all methods of the other plugins
            plugIns.push_back( new SystemPlugin( this ) );
    
            plugIns.push_back( new SamplePlugin( this ) ); 
            //add other plugIns here
    
            for_each( plugIns.begin(), plugIns.end(), bind2nd( mem_fun( &IPlugIn::BindInterfaces ), this ) );
            for_each( plugIns.begin(), plugIns.end(), mem_fun( &IPlugIn::Start ) );
        }
    
        ~FrameWork() {
            for_each( plugIns.rbegin(), plugIns.rend(), mem_fun( &IPlugIn::Stop ) );
            for_each( plugIns.rbegin(), plugIns.rend(), Delete() );
        }
    };
    
    
        14
  •  0
  •   Dave Van den Eynde    17 年前

    #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控制台应用程序上。我想指出一点:

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