2008-12-02 17 views
12

En mi aplicación, hay 10-20 clases que se instancian una vez [*]. He aquí un ejemplo:¿Cómo diseñar una fábrica simple de objetos en C++?

class SomeOtherManager; 

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

instancias de las clases están contenidas en un objeto:

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

Actualmente TheManager utiliza el operador nuevo con el fin de crear objetos.

Mi intención es poder reemplazar, usando complementos, la implementación SomeManagerClass (o cualquier otra clase) con otra. Con el fin de sustituir la aplicación, son necesarios 2 pasos:

  1. definir un DerivedSomeManagerClass clase, que hereda SomeManagerClass [Plugin]
  2. Crear la nueva clase (DerivedSomeManagerClass) en lugar del predeterminado (SomeManagerClass) [aplicación]

Supongo que necesito algún tipo de fábrica de objetos, pero debería ser bastante simple ya que siempre hay solo un tipo para crear (la implementación predeterminada o la implementación del usuario).

¿Alguna idea sobre cómo diseñar una fábrica simple como la que acabo de describir? Considere el hecho de que podría haber más clases en el futuro, por lo que debería ser fácil de ampliar.

[*] No me importa si sucede más de una vez.

Editar: Tenga en cuenta que hay más de dos objetos que están contenidos en TheManager.

+0

¿Desea reemplazar las instancias de SomeManagerClass en tiempo de ejecución o en tiempo de diseño? –

+0

SomeManagerClass solo se crea una vez, al inicio de la aplicación, por lo que quiero cambiar esta creación. Debe estar en tiempo de ejecución, ya que el código que reemplaza la implementación predeterminada reside en un complemento dinámicamente vinculado. – kshahar

+0

FWIW, creo que cada vez que nombra una clase "Manager", es porque no sabe lo que realmente hace. También puede llamarlo "caja" o "cosa". El nombre es un olor, imho. – metao

Respuesta

10

Creo que hay dos problemas separados aquí.

Un problema es: ¿cómo hace TheManager el nombre la clase que tiene que crear? Debe mantener algún tipo de puntero a "una forma de crear la clase". Las posibles soluciones son:

  • mantener un puntero separado para cada tipo de clase, con una manera de establecer, pero que ya se ha dicho que no le gusta esto, ya que viola el principio DRY
  • mantener algún tipo de la tabla donde la clave es una enumeración o una cadena; en este caso el colocador es una función única con parámetros (por supuesto si la clave es una enumeración puede usar un vector en lugar de un mapa)

El otro problema es: ¿qué es esta "forma de crear una clase? "¿?" Por desgracia, no puede almacenar punteros a los constructores directamente, pero podemos:

  • crear, como otros han señalado, una fábrica para cada clase
  • acaba de añadir una función estática "crear" para cada clase; si mantienen una firma coherente, puede utilizar sus punteros para las funciones

Las plantillas pueden ayudar a evitar la duplicación innecesaria de códigos en ambos casos.

+0

Esta respuesta me hizo repensar sobre las diversas posibilidades de diseño e implementación. También es bueno como guía para las otras respuestas, por lo que recibe la recompensa. Me gustaría agradecer a todos por participar, no tengo suficientes personajes para agradecerles a todos :) – kshahar

+0

@UncleZeiv Después de todos estos años, todavía te odio por "robar" mi placa: P – Emiliano

+0

@Emiliano te refieres a la generosidad :) ¡y hasta te gané el voto! <3 – UncleZeiv

1

Puede implementar una fábrica de objetos con métodos estáticos que devuelvan una instancia de un Manager-Class. En la fábrica, podría crear un método para el tipo predeterminado de administrador y un método para cualquier tipo de administrador, a la que le da un argumento que representa el tipo de Manager-Class (por ejemplo, con una enumeración). Este último método debería devolver una interfaz en lugar de una clase.

Editar: Trataré de dar algún código, pero tenga en cuenta que mis tiempos de C++ son hace bastante tiempo y solo estoy haciendo Java y algunos scripts por el momento.

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

Ahora usted debería ser capaz de llamar a la fábrica a través de (si has sido capaz de hacer el trabajo de la muestra de código):

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

Gracias por el código, lo que quise decir en mi pregunta es que quiero reemplazar el "nuevo Manager2();" declaraciones con creación de clases derivadas. – kshahar

+0

No entiendo el punto, lo siento. Puede expandir la enumeración y la fábrica para derivar otras clases. – boutta

2

Aquí está la solución pensé, no es la mejor pero tal vez uno que le ayudará a pensar en mejores soluciones:

para cada clase no habría una clase creador:

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

Entonces, los creadores se reunieron en una clase:

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

Y TheManager se creará con TheCreator para la creación interna:

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

El problema de esta solución es que viola SECO - para cada creador de clase, tendría que escribir setter/getter en TheCreator.

0

Mh No entiendo al cien por cien, y no me gustan las cosas de la fábrica de libros y artículos.


Si todos sus administradores comparten una interfaz similar, puede derivar de una clase base y utilizar esta clase base en su programa. Dependiendo de dónde se tomará la decisión de qué clase se creará, debe usar un identificador para la creación (como se indicó anteriormente) o manejar la decisión de qué administrador crear instancias internas.


Otra forma sería implementar su "política", como mediante el uso de plantillas. Para que You ManagerClass :: create() devuelva una instancia específica SomeOtherManagerWhatever. Esto tomaría la decisión que el administrador debe hacer en el código que usa su Administrador - Maye esto no es intencionado.

O esa manera:


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

(o algo así) con esta construcción se puede utilizar fácilmente otros gestores cambiando sólo la creación de instancias de MyAwesomeClass.


También Una clase para este propósito podría ser un poco exagerada. En tu caso, supongo que una función de fábrica. Bueno, es más una cuestión de preferencia personal.

2

Creé una fábrica "base" que tiene métodos virtuales para la creación de todos los administradores básicos, y dejo que el "meta manager" (TheManager en su pregunta) tome un puntero a la fábrica base como parámetro de constructor.

Supongo que la "fábrica" ​​puede personalizar las instancias de CXYZWManager derivando de ellas, pero alternativamente, el constructor de CXYZWManager podría tomar diferentes argumentos en la fábrica "personalizada".

Un ejemplo de código largo que da salida a "CSomeManager" y "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; 
    } 
+0

Me gusta esta solución, pero considere el caso donde TheManagerFactory necesita devolver 20 clases diferentes. Significa que tiene que saber (usando la declaración #include) las 20 clases. – kshahar

+0

La clase de fábrica base puede reenviar-declarar todos los tipos de administrador en el encabezado (sin dependencias), y mientras el cpp incluirá a todos los administradores, solo se debe crear una vez (o algunas veces). Las fábricas personalizadas solo necesitan #incluir los gerentes que están anulando. –

1

Me gustaría utilizar las plantillas como éste, ya que no puedo ver el punto de fábricas clases:

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>; 
+0

pero quiere poder elegir en tiempo de ejecución, no compilar el tiempo ... – UncleZeiv

0

Si planea admitir complementos que están vinculados dinámicamente, su programa deberá proporcionar un ABI (Application Binary Interface) estable, lo que significa que no puede usar C++ como su interfaz principal ya que C++ no tiene ABI estándar.

Si desea que los complementos implementen una interfaz que usted mismo defina, deberá proporcionar el archivo de encabezado de la interfaz para el programador de complementos y estandarizar en una interfaz C muy sencilla para crear y eliminar el objeto.

No puede proporcionar una biblioteca dinámica que le permita "actualizar" la clase de complemento como está. Es por eso que necesita estandarizar en una interfaz C para crear el objeto. El uso del objeto C++ es posible siempre que ninguno de tus argumentos use tipos posiblemente incompatibles, como contenedores STL. No podrá usar un vector devuelto por otra biblioteca, porque no puede asegurarse de que su implementación de STL sea la misma que la suya.

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() 
{ 
    // ... 
} 
3

he contestado en otro SO pregunta sobre fábricas de C++. Consulte there si una fábrica flexible es de su interés. Intento describir una antigua forma de ET ++ para usar macros, que me ha funcionado muy bien.

ET++ fue un proyecto para portar MacApp antiguo a C++ y X11. En el esfuerzo de ello Eric Gamma, etc. comenzó a pensar en Patrones de diseño

0

No hablaste de TheManager. Parece que quieres que eso controle qué clase se está utilizando? o tal vez tratando de encadenarlos?

Parece que necesita una clase base abstracta y un puntero a la clase actualmente utilizada. Si desea encadenar, puede hacerlo tanto en la clase abstracta como en la clase administrativa. Si es clase abstracta, agregue un miembro a la siguiente clase en la cadena, si el administrador lo ordena para que pueda usarlo en una lista. Necesitará una forma de agregar clases, por lo que necesitará un addMe() en el administrador. Parece que sabes lo que estás haciendo para que, si eliges, sea el correcto. Una lista con un func addMe es mi recomendación y si solo quieres 1 clase activa, entonces una función en TheManager decide que sería buena.

17

Suponiendo una clase (Plugin1) que hereda de SomeManagerClass, necesita una jerarquía de clases para construir sus tipos:

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

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

A continuación, puede asignar esas fábricas a un std :: mapa, donde están obligados a cuerdas

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

Finalmente su TheManager sólo necesita conocer el nombre del plugin (como cadena) y pueden devolver un objeto de tipo SomeManagerClass con una sola línea de código:

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

EDITAR: Si no les gusta tener una clase de fábrica plug-in para cada plugin, se puede modificar el patrón previo con esto:

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

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

creo que esto es una solución mucho mejor. Además, la clase 'plugin_factory' podría agregarse al 'factory_map' si pasa a costructor la cadena.

0

Esto quizás sea más pesado de lo que necesita, pero parece que está intentando hacer una clase de trabajo de marco que admita plugins.

Lo dividiría en 3 secciones.

1) La clase FrameWork sería propietaria de los complementos. Esta clase es responsable de las interfaces de publicación suministradas por los complementos.

2) Una clase PlugIn sería propietaria de los componentes que hacen el trabajo. Esta clase es responsable de registrar las interfaces exportadas y vincular las interfaces importadas con los componentes.

3) La tercera sección, los componets son los proveedores y consumidores de las interfaces.

Para hacer que las cosas sean extensibles, poner en marcha las cosas se puede dividir en etapas.

  1. Crea todo.
  2. Conecta todo.
  3. Comience todo.

Para desglosar las cosas.

  1. Detener todo.
  2. Destruye todo.
 
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

Aquí hay una implementación mínima patrón de la fábrica que se me ocurrió en unos 15 minutos. Usamos uno similar que usa clases base más avanzadas.

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

Simplemente copia & pasta sobre una aplicación inicial de consola Win32 en VS2005/2008. Me gustaría señalar algo:

  • No es necesario crear una fábrica de concreto para cada clase. Una plantilla lo hará por usted.
  • Me gusta colocar todo el patrón de fábrica en su propia clase, para que no tenga que preocuparse por crear objetos de fábrica y eliminarlos. Simplemente registra sus clases, el compilador crea una clase de fábrica y el patrón crea un objeto de fábrica. Al final de su vida útil, todas las fábricas se destruyen limpiamente. Me gusta esta forma de encapsulación, ya que no hay confusión sobre quién gobierna la vida de las fábricas.
2

esto parece que sería mucho más simple con plantillas de función en comparación con un patrón Abstract Factory

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

BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>(); 

Si usted quiere conseguirlos a través de una cadena, se puede crear un mapa estándar de las cadenas para funcionar punteros. Aquí es una aplicación que funciona:

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

EDITAR: En la lectura de las otras respuestas me di cuenta que esto es muy similar a Dave Van den solución FactorySystem de Eynde, pero estoy usando un puntero plantilla de función en lugar de instanciar clases de fábrica con plantilla Creo que mi solución es un poco más ligera. Debido a las funciones estáticas, el único objeto que se crea una instancia es el mapa en sí. Si necesita que la fábrica realice otras funciones (DestroyManager, etc.), creo que su solución es más extensible.

Cuestiones relacionadas