Existem alternativas viáveis ​​para o GOF Singleton Pattern?

91

Vamos encarar. O Singleton Pattern é um tópico altamente controverso com hordas de programadores em ambos os lados da cerca. Há aqueles que acham que o Singleton nada mais é do que uma variável global glorificada, e outros que juram pelo padrão e o usam incessantemente. Não quero que a controvérsia do Singleton esteja no centro da minha pergunta, entretanto. Todos podem ter um cabo de guerra e batalhar para ver quem vence por mim . O que estou tentando dizer é que não acredito que haja uma única resposta correta e não estou intencionalmente tentando inflamar disputas partidárias. Estou simplesmente interessado em alternativas singleton quando faço a pergunta:

Existem alternativas específicas para o GOF Singleton Pattern?

Por exemplo, muitas vezes, quando usei o padrão singleton no passado, estou simplesmente interessado em preservar o estado / valores de uma ou várias variáveis. O estado / valores das variáveis, entretanto, podem ser preservados entre cada instanciação da classe usando variáveis ​​estáticas em vez de usar o padrão singleton.

Que outra ideia você tem?

EDIT: Eu realmente não quero que este seja outro post sobre "como usar o singleton corretamente." Novamente, estou procurando maneiras de evitá-lo. Para se divertir, ok? Acho que estou fazendo uma pergunta puramente acadêmica na voz de seu melhor trailer de filme: "Em um universo paralelo onde não há um único, o que poderíamos fazer?"

CodingWithoutComments
fonte
O que? Não é bom nem ruim, mas como posso substituí-lo? Para todas as pessoas que dizem que é bom - não participe. Todos vocês que dizem que é ruim, provem me mostrando como posso viver sem isso. Parece argumentativo para mim.
S.Lott,
@CodingWithoutComents: leu a postagem inteira. Foi assim que comecei a sentir "não responda se achar que Singletons está bem".
S.Lott,
2
Bem, se foi isso que aconteceu, peço desculpas. Achei que tomei medidas significativas para evitar a polarização. Pensei ter feito a pergunta de uma forma que os amantes e odiadores de Singletons pudessem sair com que, como programadores, todos nós temos escolhas - que nunca é apenas uma maneira correta
CodingWithoutComments
Se eu usar Singletons, não tenho nenhuma contribuição possível sobre como contorná-los. Parece polarizador para mim.
S.Lott,
9
Eu uso Singletons todos os dias, mas isso me impede de pensar que pode haver uma maneira melhor de fazer as coisas? Os padrões de design existem há apenas 14 anos. Eu os considero verdades bíblicas? Paramos de tentar pensar fora da caixa? Não tentamos avançar a disciplina de CS?
CodingWithoutComments

Respostas:

76

Alex Miller em " Patterns I Hate " cita o seguinte:

"Quando um singleton parece ser a resposta, acho muitas vezes mais sensato:

  1. Crie uma interface e uma implementação padrão do seu singleton
  2. Construa uma única instância de sua implementação padrão no “topo” de seu sistema. Isso pode ser em uma configuração Spring, ou em código, ou definido de várias maneiras, dependendo do seu sistema.
  3. Passe a única instância para cada componente que precisa dela (injeção de dependência)
Jacek Szymański
fonte
2
Eu sei que isso tem quase 5 anos, mas você poderia expandir isso? Acho que fui ensinado que a maneira certa de criar um singleton era com uma interface. Eu estaria interessado se o que eu estava fazendo fosse 'OK', e não tão terrível quanto me disseram.
Pureferret
97

Para entender a maneira adequada de contornar os Singletons, você precisa entender o que há de errado com os Singletons (e o estado global em geral):

Singletons ocultam dependências.

Por que isso é importante?

Porque se você ocultar as dependências, você tende a perder o controle da quantidade de acoplamento.

Você pode argumentar que

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

é mais simples que

void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, 
                    String creditCardNumber, int price){
  creditCardProcessor.debit(creditCardNumber, amount);
  cart.addLaptop();
}

mas pelo menos a segunda API deixa claro exatamente quais são os colaboradores do método.

Portanto, a maneira de contornar os Singletons não é usar variáveis ​​estáticas ou localizadores de serviço, mas alterar as classes Singleton em instâncias, que são instanciadas no escopo onde fazem sentido e injetadas nos componentes e métodos que precisam delas. Você pode usar um framework IoC para lidar com isso, ou você pode fazer isso manualmente, mas o importante é se livrar do seu estado global e tornar as dependências e colaborações explícitas.

Rasmus Faber
fonte
+1 Para discutir a raiz do problema, não apenas como contorná-lo.
Phil
9
Em um aplicativo multicamadas completo, o segundo método geralmente cria uma grande quantidade de código desnecessário. Ao poluir o código das camadas intermediárias com lógica para transportar para as camadas inferiores, objetos que são desnecessários naquele nível, não estamos criando dependências que não precisam ser? Digamos que a camada 4 em uma pilha de navegação inicia uma ação em segundo plano, como um upload de arquivo. Agora, digamos que você queira alertar o usuário quando ele for concluído, mas então o usuário pode estar em uma parte totalmente diferente do aplicativo que compartilha apenas a camada 1 em comum com a visualização inicial. Toneladas de código desnecessário ...
Hari Karam Singh
1
@RasmusFaber Um padrão Singleton não esconde dependências. Sua reclamação sobre ocultar dependências é uma preocupação separada do padrão. É verdade que o padrão torna mais fácil cometer esse erro, mas é muito possível executar o padrão IoC e Singleton juntos em harmonia. Por exemplo, eu posso criar uma biblioteca de terceiros que precisa de gerenciamento de ciclo de vida especial de suas instâncias, e qualquer pessoa que use minha biblioteca ainda deve "passar" uma instância de meu singleton para suas classes usando a injeção de dependência clássica em vez de "sky hook" meu singleton de cada pointcut.
Martin Bliss
@HariKaramSingh você pode usar o padrão de assinatura / publicação ou o barramento de evento de todo o sistema (que deve ser compartilhado por todas as partes interessadas, não um singleton) para contornar isso.
Victor Sorokin
4
Além disso, os padrões pub / sub ou observador podem ser tão ruins em "perder o controle do acoplamento" quanto qualquer um que teve que depurar uma estrutura que depende muito deles pode atestar. Pelo menos com singleton's, o nome do método que está sendo invocado está no mesmo lugar que o código de invocação :) Pessoalmente, acho que todas essas estratégias têm seu lugar e nenhuma pode ser implementada cegamente. Codificadores desleixados farão espaguete com qualquer padrão e codificadores responsáveis ​​não precisam se sobrecarregar excessivamente com limitações ideológicas para criar códigos elegantes.
Hari Karam Singh
14

A melhor solução que encontrei é usar o padrão de fábrica para construir instâncias de suas classes. Usando o padrão, você pode garantir que haja apenas uma instância de uma classe que é compartilhada entre os objetos que a usam.

Achei que seria complicado de gerenciar, mas depois de ler esta postagem do blog "Para onde foram todos os solteiros?" , parece tão natural. E como um aparte, isso ajuda muito a isolar seus testes de unidade.

Em resumo, o que você precisa fazer? Sempre que um objeto depende de outro, ele receberá uma instância dele apenas por meio de seu construtor (nenhuma palavra-chave nova em sua classe).

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

E então, a fábrica.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

Como você irá instanciar sua fábrica apenas uma vez, haverá uma única instanciação de exSingleton. Sempre que você chamar buildNeedy, a nova instância de NeedyClass será empacotada com exSingleton.

Eu espero que isso ajude. Por favor, indique quaisquer erros.

seuvitor
fonte
5
para garantir que seu Factory seja instanciado apenas quando você precisar de um construtor privado e definir o método buildNeedy como um método estático.
Julien Grenier,
16
Julien está certo, essa correção tem uma falha fundamental, que é que você está implicitamente dizendo que só pode instanciar uma fábrica. Se você tomar as precauções necessárias para garantir que apenas uma fábrica seja instanciada (como disse Julien), você acabará com ... um singleton! Na realidade, essa abordagem apenas adiciona uma camada desnecessária de abstração no topo do singleton.
Bob Somers
Existe outra maneira de ter apenas uma instância. Você pode instanciar sua fábrica apenas uma vez. Não há necessidade de impor isso pelo compilador. Isso também ajuda com os testes de unidade em que você deseja uma instância diferente.
início de
Esta é a melhor solução que já vi para o problema que é resolvido incorretamente com singletons - adicionar dependências sem bagunçar cada chamada de método (e, portanto, redesenhar a interface e refatorar suas dependências). Uma ligeira modificação é perfeita para minha situação em que não é importante que haja apenas uma instância do "singleton", mas é importante que todos compartilhem a mesma instância por padrão (a modificação é: em vez de usar uma fábrica, use estática métodos para definir externamente a instância comum e usar essa instância durante a construção).
meustrus
Então, basicamente, a vantagem dessa abordagem é que você só precisa embaralhar uma instância de objeto (a fábrica) em vez de potencialmente um monte de objetos (para os quais você está optando por não usar Singletons)?
Hari Karam Singh
9

Spring ou qualquer outro IoC-Container faz um trabalho razoavelmente bom nisso. Como as classes são criadas e gerenciadas fora do próprio aplicativo, o contêiner pode criar singletons de classes simples e injetá-los onde necessário.

Kai
fonte
Tem algum link sobre os tópicos mencionados?
CodingWithoutComments
Essas estruturas parecem específicas para java - alguma outra opção específica de linguagem? Ou agnóstico de linguagem?
CodingWithoutComments
para .NET: Castle Windsor castleproject.org/container/index.html Microsoft Unity msdn.microsoft.com/en-us/library/cc468366.aspx O Unity é na verdade uma estrutura de injeção de dependência, mas provavelmente funcionará neste tópico.
Erik van Brakel,
1
porque não há muitos votos para isso? O IOC é a solução perfeita para evitar o Singleton.
Sandeep Jindal
@CodingWithoutComments Eu sei que o PHP tem o Symfony Service Container. Os caras do rubi têm outras maneiras de injetar dependências. Você tem um idioma específico no qual está interessado?
Bevelopper
9

Você não deveria ter que sair do seu caminho para evitar qualquer padrão. O uso de um padrão é uma decisão de design ou um ajuste natural (ele simplesmente se encaixa). Ao projetar um sistema, você tem a opção de usar um padrão ou não usá-lo. No entanto, você não deve sair de seu caminho para evitar qualquer coisa que seja, em última análise, uma escolha de design.

Não evito o Padrão Singleton. Ou é apropriado e eu uso ou não é apropriado e eu não uso. Acredito que seja simples assim.

A adequação (ou falta dela) do Singleton depende da situação. É uma decisão de design que deve ser tomada e as consequências dessa decisão devem ser compreendidas (e documentadas).

Thomas Owens
fonte
Eu ia dizer a mesma coisa, mas não responde exatamente à pergunta dele :)
Swati,
2
Fale sobre em sua resposta quando é apropriado ou não apropriado. Pretty Please.
CodingWithoutComments
Isso responde à pergunta. Não tenho técnicas para evitar o uso do Padrão Singleton porque não saio do meu caminho para evitá-lo e não acho que nenhum desenvolvedor deveria.
Thomas Owens,
Eu não acho que você pode generalizar quando é ou não apropriado. É tudo específico do projeto e depende muito do design do sistema em questão. Eu não uso "regras do polegar" para decisões como essa.
Thomas Owens,
Hmm. Eu não entendo por que isso está sendo rejeitado. Eu respondi à pergunta - Quais são suas técnicas para evitar o uso do Padrão Singleton? - dizendo que não tenho técnicas porque não me esforço para evitar o padrão. Os downvoters poderiam elaborar seu raciocínio?
Thomas Owens,
5

Monostate (descrito no Agile Software Development de Robert C. Martin) é uma alternativa ao singleton. Nesse padrão, os dados da classe são todos estáticos, mas os getters / setters não são estáticos.

Por exemplo:

public class MonoStateExample
{
    private static int x;

    public int getX()
    {
        return x;
    }

    public void setX(int xVal)
    {
        x = xVal;
    }
}

public class MonoDriver
{
    public static void main(String args[])
    {
        MonoStateExample m1 = new MonoStateExample();
        m1.setX(10);

        MonoStateExample m2 = new MonoStateExample();
        if(m1.getX() == m2.getX())
        {
            //singleton behavior
        }
    }
}

Monostate tem comportamento semelhante ao singleton, mas o faz de uma maneira em que o programador não está necessariamente ciente do fato de que um singleton está sendo usado.

Mark M
fonte
Isso merece mais votos; Vim aqui do Google em busca de uma atualização do MonoState!
mahemoff de
@Mark Parece-me que este design é muito difícil de travar em termos de segurança de thread. Devo criar um bloqueio de instância para cada variável estática em MonoStateExample ou devo criar um bloqueio que todas as propriedades aproveitam? Ambos têm ramificações sérias que são facilmente resolvidas quando executadas em um padrão Singleton.
Martin Bliss
Este exemplo é uma alternativa ao padrão Singleton, mas não resolve os problemas do padrão Singleton.
Sandeep Jindal
4

O padrão singleton existe porque há situações em que um único objeto é necessário para fornecer um conjunto de serviços .

Mesmo se esse for o caso, ainda considero inadequada a abordagem de criar singletons usando um campo / propriedade estática global que representa a instância. É impróprio porque cria uma dependência no código entre o campo estático e o objeto não, os serviços que o objeto fornece.

Portanto, em vez do padrão clássico, singleton, recomendo usar o padrão de serviço 'like' com contêineres atendidos , onde em vez de usar seu singleton por meio de um campo estático, você obtém uma referência a ele por meio de um método solicitando o tipo de serviço necessário.

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

em vez de global único

*pseudocode* singletonType.Instance

Dessa forma, quando você quiser alterar o tipo de um objeto de singleton para outra coisa, terá um tempo fácil para fazer isso. Além disso, como um benefício adicional, você não precisa passar todas as instâncias de objeto para cada método.

Veja também Inversão de controle , a ideia é que, ao expor singletons diretamente ao consumidor, você cria uma dependência entre o consumidor e a instância do objeto, não os serviços de objeto fornecidos pelo objeto.

Minha opinião é esconder o uso do padrão singleton sempre que possível, porque nem sempre é possível evitá-lo, ou desejável.

Pop Catalin
fonte
Não sigo bem o seu exemplo de contêineres atendidos. Você tem um recurso online para o qual poderia criar um link?
CodingWithoutComments
Dê uma olhada em "Castle Project - Windsor Container" castleproject.org/container/index.html . Infelizmente, é muito difícil encontrar publicações abstratas sobre este assunto.
Pop Catalin
2

Se você estiver usando um Singleton para representar um único objeto de dados, poderá, em vez disso, passar um objeto de dados como um parâmetro de método.

(embora eu diria que esta é a maneira errada de usar um Singleton em primeiro lugar)

David Koelle
fonte
1

Se o seu problema é que você deseja manter o estado, você deseja uma classe MumbleManager. Antes de começar a trabalhar com um sistema, seu cliente cria um MumbleManager, onde Mumble é o nome do sistema. O estado é retido por meio disso. Provavelmente, seu MumbleManager conterá uma sacola de propriedades que contém seu estado.

Este tipo de estilo parece muito com o C e não muito com o objeto - você descobrirá que os objetos que definem seu sistema terão uma referência ao mesmo MumbleManager.

plinto
fonte
Sem advogar a favor ou contra Singleton, devo dizer que essa solução é um antipadrão. MumbleManager se torna uma "classe de Deus" que contém todos os tipos de conhecimento díspar, violando a responsabilidade única. Pode muito bem ser um Singleton para todos os cuidados de aplicativos.
Martin Bliss
0

Use um objeto simples e um objeto fábrica. A fábrica é responsável por policiar a instância e os detalhes do objeto simples apenas com as informações de configuração (que contém, por exemplo) e comportamento.

David
fonte
1
Uma fábrica não costuma ser um único? É assim que geralmente vejo os padrões de fábrica implementados.
Thomas Owens,
0

Na verdade, se você projetar do zero para evitar Singeltons, pode não ter que contornar o problema de não usar Singletons usando variáveis ​​estáticas. Ao usar variáveis ​​estáticas, você também está criando um Singleton mais ou menos, a única diferença é que você está criando diferentes instâncias de objeto, porém internamente todos eles se comportam como se estivessem usando um Singleton.

Você pode dar um exemplo detalhado onde usa um Singleton ou onde um Singleton é usado atualmente e você está tentando evitar usá-lo? Isso poderia ajudar as pessoas a encontrar uma solução mais sofisticada como a situação poderia ser tratada sem um Singleton.

Aliás, eu pessoalmente não tenho problemas com Singletons e não consigo entender os problemas que outras pessoas têm em relação a Singletons. Não vejo nada de ruim neles. Isto é, se você não está abusando deles. Toda técnica útil pode ser abusada e, se for abusada, levará a resultados negativos. Outra técnica comumente mal utilizada é a herança. Ainda assim, ninguém diria que herança é algo ruim só porque algumas pessoas abusam dela terrivelmente.

Mecki
fonte
0

Pessoalmente, para mim, uma maneira muito mais sensata de implementar algo que se comporta como um singleton é usar uma classe totalmente estática (membros estáticos, métodos estáticos, propriedades estáticas). Na maioria das vezes eu o implemento desta forma (não consigo pensar em nenhuma diferença de comportamento do ponto de vista do usuário)

user18834
fonte
0

Acho que o melhor lugar para policiar o singleton é no nível de design da classe. Nesse estágio, você deve ser capaz de mapear as interações entre as classes e ver se algo absolutamente, definitivamente requer que apenas 1 instância dessa classe exista em qualquer momento da vida do aplicativo.

Se for esse o caso, então você tem um singleton. Se você está adicionando singletons como uma conveniência durante a codificação, então você realmente deve revisitar seu design e também parar de codificar os referidos singletons :)

E sim, 'polícia' é a palavra que eu quis dizer aqui, em vez de 'evitar'. O singleton não é algo a ser evitado (da mesma forma que goto e variáveis ​​globais não são algo a ser evitado). Em vez disso, você deve monitorar seu uso e garantir que é o melhor método para fazer o que deseja com eficácia.

workmad3
fonte
1
Eu entendo perfeitamente o que você está dizendo workmad. E eu concordo totalmente. No entanto, sua resposta ainda não responde especificamente à minha pergunta. Eu não queria que essa fosse outra questão de "quando é apropriado usar o singleton?" Eu estava interessado apenas em alternativas viáveis ​​para o singleton.
CodingWithoutComments
0

Eu uso o singleton principalmente como "contêiner de métodos", sem nenhum estado. Se eu precisar compartilhar esses métodos com muitas classes e quiser evitar a carga de instanciação e inicialização, crio um contexto / sessão e inicializo todas as classes lá; tudo o que se refere à sessão também tem acesso ao "singleton" nele contido.

Manrico Corazzi
fonte
0

Não tendo programado em um ambiente intensamente orientado a objetos (por exemplo, Java), não estou completamente informado sobre os meandros da discussão. Mas eu implementei um singleton no PHP 4. Eu fiz isso como uma forma de criar um manipulador de banco de dados 'caixa preta' que inicializava automaticamente e não precisava passar por chamadas de função para cima e para baixo em uma estrutura incompleta e um tanto quebrada.

Depois de ler alguns links de padrões singleton, não tenho certeza se iria implementá-los exatamente da mesma maneira novamente. O que era realmente necessário eram vários objetos com armazenamento compartilhado (por exemplo, o identificador de banco de dados real) e isso é basicamente o que minha chamada se tornou.

Como a maioria dos padrões e algoritmos, usar um singleton 'apenas porque é legal' é The Wrong Thing To Do. Eu precisava de uma chamada verdadeiramente 'caixa-preta' que parecia muito com um singleton. E, IMO, essa é a maneira de lidar com a questão: esteja ciente do padrão, mas também observe seu escopo mais amplo e em que nível sua instância precisa ser única.

staticsan
fonte
-1

O que você quer dizer com quais são as minhas técnicas para evitá-lo?

Para "evitá-lo", isso implica que há muitas situações que encontro nas quais o padrão singleton é um ajuste naturalmente bom e, portanto, tenho que tomar algumas medidas para neutralizar essas situações.

Mas não existem. Não tenho que evitar o padrão singleton. Simplesmente não surge.

DrPizza
fonte