In programming, the design patterns are constantly used. The design patterns are templates oriented to the resolution of recurring problems in programming:
- dependency reduction
- coupling reduction
- interface simplification
- resource management
- ...
One of these patterns is, as commented by @asdasdasd, the Factoria pattern. This pattern raises the possibility of having a unified interface that allows creating objects of different nature.
The design of the factory is somewhat free, although in C ++ the designs are implemented using classes. An example:
class Base;
template<class T>
Base* CreadorDeObjetos()
{ return new T; }
class Factoria
{
static std::unordered_map<std::string,std::function<Base*()>> m_creadores;
public:
static Base* NuevoObjeto(std::string const& nombre)
{
Base* toReturn = nullptr;
auto it = m_creadores.find(nombre);
if( it != m_creadores.end() )
toReturn = it->second();
return toReturn;
}
template<class T>
static void RegistrarObjeto(std::string const& nombre)
{
m_creadores.insert(std::make_pair(nombre,&CreadorDeObjetos<T>));
}
};
std::unordered_map<std::string,std::function<Base*()>> Factoria::m_creadores = {};
struct Base
{
virtual void Test() = 0;
virtual ~Base()
{ }
};
struct Derivada1 : Base
{
void Test() override
{ std::cout << "-- Derivada1 ok\n"; }
};
struct Derivada2 : Base
{
void Test() override
{ std::cout << "-- Derivada2 ok\n"; }
};
int main()
{
Factoria::RegistrarObjeto<Derivada1>("Derivada1");
Factoria::RegistrarObjeto<Derivada2>("Derivada1");
Base* a = Factoria::NuevoObjeto("Derivada1");
if( a )
a->Test();
else
std::cout << "Derivada1 no esta registrado\n";
Base* b = Factoria::NuevoObjeto("Derivada2");
if( b )
b->Test();
else
std::cout << "Derivada2 no esta registrado\n";
Base* c = Factoria::NuevoObjeto("d3");
if( c )
c->Test();
else
std::cout << "d3 no esta registrado\n";
}
Making use of macros the system can be left a little cleaner:
// Factoria de objetos
class Base;
class Factoria
{
protected:
static std::unordered_map<std::string,std::function<Base*()>> m_creadores;
public:
static Base* NuevoObjeto(std::string const& nombre)
{
Base* toReturn = nullptr;
auto it = m_creadores.find(nombre);
if( it != m_creadores.end() )
toReturn = it->second();
return toReturn;
}
};
std::unordered_map<std::string,std::function<Base*()>> Factoria::m_creadores = {};
// Utilidad para registrar los objetos
template<class T>
class RegistradorObjetos : Factoria
{
static Base* CreadorDeObjetos()
{ return new T; }
public:
RegistradorObjetos(std::string const& nombre)
{
m_creadores.insert(std::make_pair(nombre,&CreadorDeObjetos));
}
};
// Macro (opcional) para registrar los objetos
#define RegistrarNuevoObjeto(obj) \
static RegistradorObjetos<obj> obj##_dummy(#obj)
// Clase base
struct Base
{
virtual void Test() = 0;
virtual ~Base()
{ }
};
// Clases a introducir en la factoria
struct Derivada1 : Base
{
void Test() override
{ std::cout << "-- Derivada1 ok\n"; }
};
struct Derivada2 : Base
{
void Test() override
{ std::cout << "-- Derivada1 ok\n"; }
};
// Se registran las clases
// Ya no hace falta poner el nombre convertido en cadena
// y si nos equivocamos en el nombre salta un error en tiempo de compilacion
RegistrarNuevoObjeto(Derivada1);
RegistrarNuevoObjeto(Derivada2);
int main()
{
// Mismo codigo que en el primer ejemplo
}