Как разработать простую фабрику объекта C++?

Если структура ввода постоянна, я бы воспользовался этим, вместо того, чтобы пытаться группировать по конечному числу.

Например, вы можете сделать:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/results">
    <xsl:copy>
        <xsl:copy-of select="status | info"/>
        <products>
            <xsl:for-each select="*[starts-with(name(), 'prod')]">
                <product>
                    <prod>
                        <xsl:value-of select="." />
                    </prod>
                    <pub>
                        <xsl:value-of select="following-sibling::*[1]" />
                    </pub>
                    <sub>
                        <xsl:value-of select="following-sibling::*[2]" />
                    </sub>
                    <subtype>
                        <xsl:value-of select="following-sibling::*[3]" />
                    </subtype>
                </product>
            </xsl:for-each>
        </products>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
12
задан kshahar 9 February 2009 в 15:15
поделиться

14 ответов

Я думаю, что здесь существует две отдельных проблемы.

Одна проблема: как TheManager называет класс, который он должен создать? Это должно сохранить некоторый указатель на "способ создать класс". Возможные решения:

  • при хранении отдельного указателя для каждого вида класса, со способом установить его, но Вы уже сказали, что Вам не нравится это, поскольку он нарушает принцип DRY
  • хранение своего рода таблицы, где ключ является перечислением или строкой; в этом случае метод set является единственной функцией с параметрами (конечно, если ключ является перечислением, можно использовать вектор вместо карты),

Другая проблема: что этот "путь состоит в том, чтобы создать класс"? К сожалению, мы не можем сохранить указатели на конструкторов непосредственно, но мы можем:

  • создайте, как другие указали, фабрика для каждого класса
  • просто добавьте, что помехи "создают" функцию для каждого класса; если они сохраняют последовательную подпись, можно просто использовать их указатели на функции

Шаблоны могут помочь в предотвращении ненужного дублирования кода в обоих случаях.

10
ответ дан 2 December 2019 в 03:49
поделиться

Принимая класс (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>();

Я думаю, что это - намного лучшее решение. Кроме того, 'plugin_factory' класс мог добавить себя к 'factory_map' при передаче costructor строка.

17
ответ дан 2 December 2019 в 03:49
поделиться

Я ответил в другом ТАК на вопрос о фабриках C++. Посмотрите там, если гибкая фабрика представляет интерес. Я пытаюсь описать старый путь с ET ++ для использования макросов, которые работали отлично для меня.

ET ++ было проектом портировать старого MacApp на C++ и X11. В усилии его Eric Gamma и т.д. начал думать о Шаблонах разработки

3
ответ дан 2 December 2019 в 03:49
поделиться

Вот решение, о котором я думал, это не лучшее, но возможно это поможет думать о лучших решениях:

Для каждого класса был бы класс создателя:

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 - для каждого создателя класса, которого я должен был бы записать методу set/методу считывания в TheCreator.

2
ответ дан 2 December 2019 в 03:49
поделиться

Я использовал бы шаблоны как это, поскольку я не вижу точку классов фабрик:

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>;
1
ответ дан 2 December 2019 в 03:49
поделиться

Я создал бы "основную" фабрику, которая имеет виртуальные методы для создания всех основных менеджеров, и позвольте "meta менеджер" (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;
  }
2
ответ дан 2 December 2019 в 03:49
поделиться

Это кажется, что было бы намного более просто с шаблонной обработкой функции в противоположность Шаблону "абстрактная фабрика"

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

Править: В чтении других ответов я понял, что это очень похоже на решение FactorySystem Dave Van den Eynde, но я использую указатель шаблона функции вместо того, чтобы инстанцировать шаблонных классов фабрики. Я думаю, что мое решение немного более легко. Из-за статических функций, единственный объект, который инстанцируют, является самой картой. Если Вам нужна фабрика для выполнения других функций (DestroyManager, и т.д.), я думаю, что его решение более расширяемо.

2
ответ дан 2 December 2019 в 03:49
поделиться

Вы могли реализовать объектную фабрику со статическими методами, которые возвращают экземпляр Класса Менеджера. На фабрике Вы могли создать метод для типа по умолчанию менеджера и метод для любого типа менеджера, которого Вы даете аргументу, представляющему тип Класса Менеджера (скажите с перечислением). Этот последний метод должен возвратить Интерфейс, а не Класс.

Править: Я попытаюсь дать некоторый код, но ум, что мои времена 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);
1
ответ дан 2 December 2019 в 03:49
поделиться

Необходимо смотреть на учебное руководство по http://downloads.sourceforge.net/papafactory/PapaFactory20080622.pdf?use_mirror=fastbull

Это содержит большое учебное руководство при реализации Абстрактной фабрики в C++ и исходном коде, который идет с ним, также очень устойчиво

Chris

1
ответ дан 2 December 2019 в 03:49
поделиться

Если Вы запланируете поддержку плагинов, которые динамично связаны, то Ваша программа должна будет обеспечить стабильный ABI (Двоичный интерфейс приложений), который означает, что Вы не можете использовать C++ в качестве своего основного интерфейса, поскольку C++ не имеет никакого стандартного ABI.

Если Вы хотите, чтобы плагины реализовали интерфейс, Вы определяете себя, необходимо будет обеспечить заголовочный файл интерфейса сменному программисту и стандартизировать в очень простом интерфейсе C, чтобы создать и удалить объект.

Вы не можете обеспечить динамическую библиотеку, которая позволит Вам "новому" сменный класс как есть. Именно поэтому необходимо стандартизировать в интерфейсе C для создания объекта. Используя объект C++ не затем возможно пока ни одно из Вашего использования аргументов возможно несовместимые типы, как контейнеры STL. Вы не сможете использовать вектор, возвращенный другой библиотекой, потому что Вы не можете удостовериться, что их реализация STL совпадает с Вашей.

Manager.h

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()
{
  // ...
}
0
ответ дан 2 December 2019 в 03:49
поделиться

Вы не говорили о TheManager. Похоже, что Вы хотите, чтобы это управляло, какой класс используется? или возможно Вы пытающийся объединить их в цепочку вместе?

Это кажется на необходимость в абстрактном базовом классе и указателе на в настоящее время используемый класс. Если Вы хотите объединить Вас в цепочку, может сделать это и в абстрактном классе и в themanager классе. Если абстрактный класс, добавляет участник к следующему классу в цепочке, если themanager затем сортируют его в порядке Вы, чтобы использовать в списке. Вам будет нужен способ добавить классы, таким образом, Вам будет нужен addMe () в themanager. Это кажется, что Вы знаете то, что Ваше выполнение так w/e, который Вы выбираете, должно быть правильным. Список с addMe func является моей рекомендацией и если бы Вы хотите только 1 активный класс затем функция в TheManager, решая, что это было бы хорошо.

0
ответ дан 2 December 2019 в 03:49
поделиться

Mh я не понимаю сто процентов, и я не действительно в материал фабрики из книг и статей.


Если все Ваши менеджеры совместно используют подобный интерфейс, Вы могли бы произойти из базового класса и использовать этот базовый класс в своей программе. В зависимости от того, где, решение который будет создан класс, будет сделан, необходимо использовать идентификатор для создания (как указано выше) или обработать решение который менеджер инстанцировать внутренне.


Иначе должен был бы реализовать его "политика" как при помощи шаблонов. Так, чтобы Вы ManagerClass:: создайте (), возвращает определенный экземпляр SomeOtherManagerWhatever. Это положило бы решение, какого менеджера сделать в коде, который использует Вашего менеджера - Maye, это не предназначается.

Или тот путь:


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

(или что-то как этот) С этой конструкцией можно легко использовать других менеджеров, только изменяя инстанцирование MyAwesomeClass.


Также класс с этой целью мог бы быть немногим более, чем вершиной. В Вашем случае, который сделала бы функция фабрики, я предполагаю. Хорошо это - больше вопрос персонального предпочтения.

0
ответ дан 2 December 2019 в 03:49
поделиться

Этому, возможно, более тяжелому, чем Вы нужно, но это кажется, что Вы пытаетесь сделать класс основы, который поддерживает плагины.

Я разбил бы его в к 3 разделам.

1) Класс FrameWork владел бы плагинами. Этот класс ответственен за публикацию интерфейсов, предоставленных плагинами.

2) Класс PlugIn владел бы componets, которые делают работу. Этот класс ответственен за регистрацию экспортируемых интерфейсов и привязку импортированных интерфейсов к компонентам.

3) Третий раздел, componets являются поставщиками и потребителями интерфейсов.

Сделать вещи расширяемыми, будя вещи и выполнение могло бы быть, разбился на этапы.

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

0
ответ дан 2 December 2019 в 03:49
поделиться

Вот минимальная реализация шаблона "фабрика", которую я придумал приблизительно за 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;
}

Просто копия и вставка по первоначальному приложению консоли Win32 в VS2005/2008. Мне нравится указывать на что-то:

  • Вы не должны создавать конкретную фабрику для каждого класса. Шаблон сделает это для Вас.
  • Мне нравится помещать весь шаблон "фабрика" в его собственный класс, так, чтобы Вы не должны были волноваться о создании объектов фабрики и удалении их. Вы просто регистрируете свои классы, класс фабрики создается компилятором, и объект фабрики создается шаблоном. В конце ее времени жизни чисто уничтожаются все фабрики. Мне нравится эта форма инкапсуляции, поскольку нет никакого беспорядка по тому, кто управляет временем жизни фабрик.
0
ответ дан 2 December 2019 в 03:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: