Singletons . Cerca de 10 a 15 anos atrás, os singletons eram o grande padrão de design a se conhecer. No entanto, hoje em dia eles são menosprezados. Eles são muito mais fáceis de multiencadear, mas você deve limitar seu uso a um encadeamento por vez, o que nem sempre é o que você deseja. O rastreamento das vidas úteis é tão difícil quanto às variáveis globais.
Uma classe singleton típica será mais ou menos assim:
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!
}
Injeção de Dependência (DI) . Isso significa apenas passar o serviço como um parâmetro construtor. Um serviço já deve existir para passá-lo para uma classe; portanto, não há como dois serviços confiarem um no outro; em 98% dos casos, é isso que você deseja (e para os outros 2%, você sempre pode criar um setWhatever()
método e passar o serviço posteriormente) . Por esse motivo, o DI não tem os mesmos problemas de acoplamento que as outras opções. Ele pode ser usado com multithreading, porque cada encadeamento pode simplesmente ter sua própria instância de cada serviço (e compartilhar apenas aqueles absolutamente necessários). Também torna o código testável por unidade, se você se importa com isso.
O problema com a injeção de dependência é que ela ocupa mais memória; agora toda instância de uma classe precisa de referências para todos os serviços que ela usará. Além disso, fica chato de usar quando você tem muitos serviços; existem estruturas que atenuam esse problema em outras linguagens, mas, devido à falta de reflexão do C ++, as estruturas DI no C ++ tendem a ser ainda mais trabalhosas do que apenas 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.
Vejo esta página (na documentação do Ninject, uma estrutura C # DI) para outro exemplo.
A injeção de dependência é a solução usual para esse problema e é a resposta que você verá mais altamente votada para perguntas como essa no StackOverflow.com. DI é um tipo de Inversão de Controle (IoC).
Localizador de serviço . Basicamente, apenas uma classe que possui uma instância de todos os serviços. Você pode fazer isso usando reflexão ou simplesmente adicionar uma nova instância a cada vez que desejar criar um novo serviço. Você ainda tem o mesmo problema de antes - Como as classes acessam este localizador? - que pode ser resolvido de qualquer uma das maneiras acima, mas agora você só precisa fazer isso para sua ServiceLocator
classe, e não para dezenas de serviços. Esse método também é testável por unidade, se você se importa com esse tipo de coisa.
Localizadores de serviço são outra forma de Inversão de controle (IoC). Geralmente, estruturas que executam injeção automática de dependência também terão um localizador de serviço.
XNA (estrutura de programação de jogos em C # da Microsoft) inclui um localizador de serviço; Para saber mais, consulte esta resposta .