O Singleton é um dos design patterns mais simples de se entender e também mais fácil de ser implementado. Ele garante que apenas uma única instância vai poder ser criada para um objeto. Seu funcionamento é bastante simples:
- A classe singleton é definida com um construtor privado (ou protegido se for o caso);
- A classe singleton mantém uma referência privada e estática ao objeto unicamente instanciado;
- Um método público e estático é disponibilizado para que os objetos que acessarão o singleton possam recuperar sua instância.
Há vários problemas com esse design. Você vai encontrar várias publicações e discussões em newsgroups sobre isso. Mas vou me concentrar no problema da dificuldade de extensibilidade e de suporte a variações para a classe singleton.
Depois de implementado e largamente utilizado em toda a sua aplicação, o que acontece se o seu singleton precisar variar? Ou seja, hoje ele trabalha de uma forma, mas agora eu me deparo com um requisito de negócio que me obriga a variar a implementação do singleton em diferentes cenários de uso da minha aplicação. É nessa hora que você se arrepende com todas as forças de ter utilizado o singleton para projetar sua solução.
Isso aconteceu comigo recentemente quando precisei adequar minha aplicação para trabalhar com um cenário de negócio diferente. Eu desenvolvi aqui na empresa uma solução de instalação para o nosso sistema, o Phidelis. Depois de usar arquivos de instalação MSI durante muito tempo, percebemos que ele não tinha os facilitadores que precisávamos para fazer uma instalação (one-step) do nosso aplicativo. Partimos então para criar o nosso próprio aplicativo de instalação. Esse aplicativo possibilita a instalação do sistema simultaneamente em várias máquinas, considerando o uso de clusters e de módulos do sistema sendo executados em ambientes diferenciados. O instalador também gerencia a instalação do sistema em vários ambientes (produção, homologação, etc).
No projeto desse aplicativo eu criei uma classe Singleton chamada ListaDeModulos que me permitia ter acesso a lista de módulos do sistema que o aplicativo instalaria no ambiente de um cliente. O código é mais ou menos o seguinte:
public class ListaDeModulos
{
List<Modulo> _modulos = new List<Modulo>();
private static ListaDeModulos _instance;
private ListaDeModulos()
{
_modulos.Add(new IntranetAdministrativa());
_modulos.Add(new PortalDoAluno());
_modulos.Add(new PortalDoProfessor());
_modulos.Add(new CobrancaExterna());
_modulos.Add(new Servicos());
_modulos.Add(new VestibularOnLine());
}
public static ListaDeModulos GetInstance()
{
if (_instance == null)
_instance = new ListaDeModulos();
return _instance;
}
public List<Modulo> All
{
get { return _modulos; }
}
public Modulo WithId(string id)
{
foreach (Modulo each in _modulos)
{
if (each.Id.ToLower() == id.ToLower())
return each;
}
return null;
}
No início parecia um bom design. O princípio Open-closed parecia estar atendido, já que a classe estava aberta para receber novos módulos ou para a remoção de módulos que ficassem depreciados. Bastaria para isso, criar uma nova implementação para a classe abstrata Modulo e adiciona-la à lista gerenciada pelo singleton.
O problema de design surgiu quando um novo requisito de negócio foi proposto. A empresa agora está trabalhando em uma simplificação do sistema para atingir clientes pequenos a um custo mais baixo. Esta nova versão do sistema chama-se Phidelis Express e terá uma lista de módulos completamente diferente da lista de módulos oferecida para clientes corporativos.
BAD DESIGN!
Meu projeto não atende a esse cenário. Eu tenho agora dois instaladores e a lista de módulos estará variando quando eu usar o instalador para o sistema Corp e para o sistema Express. Só que minha lista de módulos é rígida, e pior, ela é pública e acessada em várias partes do código com uma única instrução que agora deveria estar variando conforme o tipo de instalação que estiver ocorrendo:
ListaDeModulos.GetInstance().All;
Estou agora sentindo as mazelas do singleton na carne. A melhor solução agora será refatorar todo o projeto removendo o singleton e tratando a ListaDeModulos como uma abstração que pode ser implementada de formas diferentes em cenários alternativos. Muito trabalho para extender uma funcionalidade que poderia ser muito mais facilmente resolvida se o singleton não fizesse parte do projeto.
Moral da história... Pense bem da próxima vez que precisar recorrer ao Singleton para resolver um problema de design. Você pode estar retirando toda a flexibilidade do seu projeto e dificultando a evolução do seu sistema.
d0656bca-c656-49ba-9c31-6b8b1f8750b1|0|.0