Singletons . Hace unos 10-15 años, los singletons eran el gran patrón de diseño para conocer. Sin embargo, hoy en día son menospreciados. Son mucho más fáciles de subprocesos múltiples, pero debe limitar su uso a un subproceso a la vez, que no siempre es lo que desea. El seguimiento de vidas es tan difícil como con las variables globales.
Una clase de singleton típica se verá así:
class MyClass
{
private:
static MyClass* _instance;
MyClass() {} //private constructor
public:
static MyClass* getInstance();
void method();
};
...
MyClass* MyClass::_instance = NULL;
MyClass* MyClass::getInstance()
{
if(_instance == NULL)
_instance = new MyClass(); //Not thread-safe version
return _instance;
//Note that _instance is *never* deleted -
//it exists for the entire lifetime of the program!
}
Inyección de dependencia (DI) . Esto solo significa pasar el servicio como un parámetro constructor. Un servicio ya debe existir para pasarlo a una clase, por lo que no hay forma de que dos servicios dependan el uno del otro; en el 98% de los casos, esto es lo que desea (y para el otro 2%, siempre puede crear un setWhatever()
método y pasar el servicio más adelante) . Debido a esto, DI no tiene los mismos problemas de acoplamiento que las otras opciones. Se puede usar con subprocesos múltiples, porque cada subproceso puede simplemente tener su propia instancia de cada servicio (y compartir solo aquellos que absolutamente necesita). También hace que el código sea comprobable por unidad, si te importa eso.
El problema con la inyección de dependencia es que ocupa más memoria; ahora cada instancia de una clase necesita referencias a cada servicio que usará. Además, se vuelve molesto usarlo cuando tienes demasiados servicios; existen marcos que mitigan este problema en otros lenguajes, pero debido a la falta de reflexión de C ++, los marcos DI en C ++ tienden a ser aún más trabajo que simplemente hacerlo manualmente.
//Example of dependency injection
class Tower
{
private:
MissileCreationService* _missileCreator;
CreepLocatorService* _creepLocator;
public:
Tower(MissileCreationService*, CreepLocatorService*);
}
//In order to create a tower, the creating-class must also have instances of
// MissileCreationService and CreepLocatorService; thus, if we want to
// add a new service to the Tower constructor, we must add it to the
// constructor of every class which creates a Tower as well!
//This is not a problem in languages like C# and Java, where you can use
// a framework to create an instance and inject automatically.
Vea esta página (de la documentación de Ninject, un marco C # DI) para otro ejemplo.
La inyección de dependencia es la solución habitual para este problema, y es la respuesta que verá con más votos a preguntas como esta en StackOverflow.com. DI es un tipo de Inversión de Control (IoC).
Localizador de servicios . Básicamente, solo una clase que contiene una instancia de cada servicio. Puede hacerlo utilizando la reflexión , o simplemente puede agregarle una nueva instancia cada vez que desee crear un nuevo servicio. Todavía tiene el mismo problema que antes: ¿cómo acceden las clases a este localizador? - que puede resolverse de cualquiera de las formas anteriores, pero ahora solo necesita hacerlo para su ServiceLocator
clase, en lugar de para docenas de servicios. Este método también es comprobable por unidad, si te importa ese tipo de cosas.
Los localizadores de servicios son otra forma de inversión de control (IoC). Por lo general, los marcos que realizan la inyección automática de dependencias también tendrán un localizador de servicios.
XNA (marco de programación de juegos C # de Microsoft) incluye un localizador de servicios; para obtener más información al respecto, vea esta respuesta .