Singletons: bom design ou uma muleta?

Os singletons são um padrão de design bastante debatido, por isso estou interessado no que a comunidade do Stack Overflow pensou sobre eles.

Por favor, forneça razões para suas opiniões, não apenas “Singletons são para programadores preguiçosos!”

Aqui está um artigo bastante bom sobre o assunto, embora seja contra o uso de Singletons: scientificninja.com: performant-singletons .

Alguém tem algum outro bom artigo sobre eles? Talvez em apoio a Singletons?

Em defesa dos singletons:

  • Eles não são tão ruins quanto globals porque globals não possuem uma ordem de boot padronizada, e você pode facilmente ver bugs não-determinísticos devido a pedidos de dependência ingênuos ou inesperados. Singletons (assumindo que eles estão alocados no heap) são criados depois de todos os globais, e em um lugar muito previsível no código.
  • Eles são muito úteis para sistemas de cache / recurso preguiçoso , como uma interface para um dispositivo de E / S lento. Se você criar inteligentemente uma interface singleton para um dispositivo lento e ninguém o chamar, você não perderá tempo. Se outro código o chamar de vários locais, seu singleton poderá otimizar o armazenamento em cache para ambos simultaneamente e evitar consultas duplas. Você também pode evitar facilmente qualquer condição de deadlock no recurso controlado por singleton.

Contra singletons:

  • Em C ++, não há uma maneira legal de limpar automaticamente após singletons. Existem alternativas, e maneiras ligeiramente hacky de fazê-lo, mas simplesmente não há uma maneira simples e universal de garantir que o destruidor de seu singleton seja sempre chamado. Isso não é tão terrível em termos de memory – basta pensar nisso como mais variables ​​globais, para esse propósito. Mas pode ser ruim se o seu singleton aloca outros resources (por exemplo, bloqueia alguns arquivos) e não os libera.

Minha opinião:

Eu uso singletons, mas evitá-los se houver uma alternativa razoável. Isso funcionou bem para mim até agora, e eu os achei testáveis, embora um pouco mais de trabalho para testar.

O Google tem um Detector de Singleton para Java que, acredito, começou como uma ferramenta que deve ser executada em todo o código produzido no Google. A razão de nutshell para remover Singletons:

porque eles podem dificultar o teste e esconder problemas com o seu design

Para uma explicação mais explícita, consulte ” Por que os singletons são controversos ” do Google.

Um singleton é apenas um monte de variables ​​globais em um vestido extravagante.

Variáveis ​​globais têm seus usos, assim como singletons, mas se você acha que está fazendo algo legal e útil com um singleton em vez de usar uma variável global yucky (todo mundo sabe que globals são ruins mmkay), infelizmente você está enganado.

O objective de um Singleton é garantir que uma class tenha apenas uma instância e fornecer um ponto global de access a ela. Na maioria das vezes, o foco está no único ponto de instância. Imagine se fosse chamado de Globalton. Soaria menos atraente, pois isso enfatiza as conotações (geralmente) negativas de uma variável global.

A maioria dos bons argumentos contra os singletons tem a ver com a dificuldade que eles apresentam nos testes, pois criar duplas de teste para eles não é fácil.

Há três boas postagens no blog sobre Singletons por Miško Hevery no blog do Google Testing.

  1. Singletons são mentirosos patológicos
  2. Onde todos os singletons foram?
  3. Causa Raiz dos Singletons

Singleton não é um padrão horrível, embora seja muito usado . Eu acho que este uso indevido é porque é um dos padrões mais fáceis e mais novos para o singleton são atraídos para o efeito colateral global.

Erich Gamma disse que o singleton é um padrão que ele não queria que fosse incluído no livro do GOF e é um projeto ruim. Eu tenho a tendência de discordar.

Se o padrão for usado para criar uma única instância de um object a qualquer momento, o padrão estará sendo usado corretamente. Se o singleton é usado para dar um efeito global, ele está sendo usado incorretamente.

Desvantagens:

  • Você está acoplando a uma class em todo o código que chama o singleton
    • Cria um incômodo com o teste de unidade porque é difícil replace a instância por um object simulado
    • Se o código precisar ser refatorado mais tarde devido à necessidade de mais de uma instância, será mais doloroso do que se a class singleton fosse passada para o object (usando uma interface) que a utiliza

Vantagens:

  • Uma instância de uma class é representada em qualquer ponto no tempo.
    • Por design você está impondo isso
  • Instância é criada quando é necessário
  • Acesso global é um efeito colateral

Garotas me procuram porque eu raramente uso singleton e quando faço isso normalmente é algo incomum. Não, sério, eu amo o padrão singleton. Você sabe porque? Porque:

  1. Sou preguiçosa.
  2. Nada pode dar errado.

Claro, os “especialistas” irão falar sobre “testes unitários” e “injeção de dependência”, mas isso é tudo uma carga de rins do dingo. Você diz que o singleton é difícil de testar na unidade? Sem problemas! Apenas declare tudo público e transforme sua turma em uma casa divertida de bondade global. Você se lembra do show Highlander dos anos 90? O singleton é assim porque: A. Ele nunca pode morrer; e B. Pode haver apenas um. Então, pare de ouvir todos os weenies do DI e implemente seu singleton com abandono. Aqui estão mais algumas boas razões …

  • Todo mundo está fazendo isso.
  • O padrão singleton faz você invencível.
  • Singleton rima com “win” (ou “diversão” dependendo do seu sotaque).

Eu acho que há um grande mal-entendido sobre o uso do padrão Singleton. A maioria dos comentários aqui se refere a ele como um local para acessar dados globais. Precisamos ter cuidado aqui – Singleton como um padrão não é para acessar globals.

Singleton deve ser usado para ter apenas uma instância da class dada. O Repositório de Padrões tem ótimas informações sobre o Singleton .

Um dos colegas com quem trabalhei era muito preocupado com o Singleton. Sempre que havia algo que era uma espécie de gerente ou chefe, ele transformava isso em um singleton, porque ele achava que deveria haver apenas um chefe. E cada vez que o sistema tomava alguns novos requisitos, descobriu-se que havia razões perfeitamente válidas para permitir várias instâncias.

Eu diria que singleton deve ser usado se o modelo de domínio ditar (não ‘sugere’) que existe um. Todos os outros casos são apenas casos únicos de uma class.

Eu tenho tentado pensar em uma maneira de chegar ao resgate do pobre singelton aqui, mas devo admitir que é difícil. Eu vi muito poucos usos legítimos deles e com a unidade atual para fazer injeção de dependência e testes de unidade, eles são apenas difíceis de usar. Eles definitivamente são a manifestação do “culto da carga” de programação com padrões de design que eu trabalhei com muitos programadores que nunca quebraram o livro “GoF”, mas eles sabem ‘Singelton’ e, portanto, eles conhecem ‘Patterns’.

Eu tenho que discordar de Orion, porém, na maioria das vezes eu vi singeltons em excesso não são variables ​​globais em um vestido, mas mais como serviços globais (methods) em um vestido. É interessante notar que, se você tentar usar Singeltons no SQL Server 2005 no modo de segurança por meio da interface CLR, o sistema sinalizará o código. O problema é que você tem dados persistentes além de qualquer transação que pode ser executada, é claro, se você fizer a variável de instância somente ler, você pode contornar o problema.

Essa questão levou a um monte de retrabalho para mim um ano.

Guerras santas! Ok, deixe-me ver .. Da última vez eu verifiquei o design da polícia disse ..

Singletons são ruins porque atrapalham o teste automático – instâncias não podem ser criadas novamente para cada caso de teste. Em vez disso, a lógica deve estar em uma class (A) que pode ser facilmente instanciada e testada. Outra class (B) deve ser responsável por restringir a criação. Princípio da Responsabilidade Única à frente! Deve ser o conhecimento da equipe que você deve passar por B para acessar A – uma espécie de convenção de equipe.

Eu concordo principalmente ..

Muitos aplicativos exigem que haja apenas uma instância de alguma class, portanto, o padrão de ter apenas uma instância de uma class é útil. Mas existem variações de como o padrão é implementado .

Existe o singleton estático , no qual a class força que só pode haver uma instância da class por processo (em Java, na verdade, uma por ClassLoader). Outra opção é criar apenas uma instância .

Singletons estáticos são maus – um tipo de variables ​​globais. Eles dificultam os testes, porque não é possível executar os testes em isolamento total. Você precisa de uma configuração complicada e de um código para limpar o sistema entre todos os testes, e é muito fácil esquecer de limpar um estado global corretamente, o que pode resultar em um comportamento não especificado nos testes.

Criar apenas uma instância é bom . Você acabou de criar uma instância quando os programas são iniciados e, em seguida, passa o ponteiro para essa instância para todos os outros objects que precisam dela. Estruturas de injeção de dependência tornam isso fácil – você apenas configura o escopo do object, e a estrutura de DI cuidará de criar a instância e passá-la para todos que precisam dela. Por exemplo, em Guice, você anotaria a class com @Singleton e a estrutura DI criaria apenas uma instância da class (por aplicativo – você pode ter vários aplicativos em execução na mesma JVM). Isso facilita o teste, porque você pode criar uma nova instância da class para cada teste e deixar que o coletor de lixo destrua a instância quando ela não for mais usada. Nenhum estado global irá vazar de um teste para outro.

Para mais informações: The Clean Code Talks – “Global State and Singletons”

Singleton como um detalhe de implementação é bom. Singleton como uma interface ou como um mecanismo de access é um gigante PITA.

Um método estático que não recebe nenhum parâmetro retornando uma instância de um object é apenas um pouco diferente de usar apenas uma variável global. Se, em vez disso, um object tiver uma referência ao object singleton passado, seja por meio do construtor ou de outro método, não importa como o singleton é realmente criado e o padrão inteiro não é importante.

Não era apenas um monte de variables ​​em um vestido extravagante, porque isso tinha dezenas de responsabilidades, como comunicar-se com a camada de persistência para salvar / recuperar dados sobre a empresa, lidar com funcionários e preços, etc.

Devo dizer que você não está realmente descrevendo algo que deveria ser um único object e é discutível que qualquer um deles, além da serialização de dados, deveria ter sido um singelton.

Eu posso ver pelo menos três conjuntos de classs que eu normalmente criaria, mas eu tenho a tendência de favorecer objects menores e mais simples que fazem um conjunto estreito de tarefas muito bem. Eu sei que esta não é a natureza da maioria dos programadores. (Sim, eu trabalho em 5000 monstruosidades de class de linha todos os dias, e tenho um amor especial pelos 1200 methods de linha que algumas pessoas escrevem.)

Eu acho que o ponto é que na maioria dos casos você não precisa de um singelton e muitas vezes o seu apenas tornando a sua vida mais difícil.

O maior problema com os singletons é que eles dificultam o teste de unidade , particularmente quando você deseja executar seus testes em paralelo, mas de forma independente.

A segunda é que as pessoas geralmente acreditam que a boot lenta com trava dupla é uma boa maneira de implementá-las.

Finalmente, a menos que seus singletons sejam imutáveis, eles podem facilmente se tornar um problema de desempenho quando você tentar dimensionar seu aplicativo para executar em vários threads em vários processadores. A synchronization forçada é cara na maioria dos ambientes.

Os singletons têm seus usos, mas é preciso ter cuidado ao usá-los e expô-los, porque eles são muito fáceis de abusar , difíceis de testar verdadeiramente na unidade, e é fácil criar dependencies circulares baseadas em dois singletons que acessam uns aos outros.

É útil, no entanto, para quando você quer ter certeza de que todos os seus dados estão sincronizados em várias instâncias, por exemplo, configurações para um aplicativo distribuído, por exemplo, podem depender de singletons para garantir que todas as conexões usem as mesmas up-to- data set de dados.

Eu acho que você tem que ter muito cuidado com o porquê você está decidindo usar um singleton. Como outros já mencionaram, é essencialmente o mesmo problema que usar variables ​​globais. Você deve ser muito cauteloso e considerar o que você poderia estar fazendo usando um.

É muito raro usá-los e geralmente há uma maneira melhor de fazer as coisas. Eu me deparei com situações em que fiz algo com um singleton e depois tive que vasculhar meu código para retirá-lo depois que descobri o quanto ele piorou as coisas (ou depois que eu encontrei uma solução muito melhor e mais sensata). )

Eu usei singletons várias vezes em conjunit com Spring e não a considerei uma muleta ou preguiça.

O que esse padrão me permitiu fazer foi criar uma única class para vários valores de tipo de configuração e compartilhar a instância única (não-mutável) dessa instância de configuração específica entre vários usuários do meu aplicativo da web.

No meu caso, o singleton continha critérios de configuração do cliente – localização do arquivo css, critérios de conexão do database, conjuntos de resources, etc. – específicos para esse cliente. Essas classs foram instanciadas e acessadas através do Spring e compartilhadas por usuários com a mesma configuração (ou seja, 2 usuários da mesma empresa). * ** Eu sei que há um nome para esse tipo de aplicativo, mas está me escapando *

Eu acho que seria um desperdício criar (e depois coletar lixo) novas instâncias desses objects “constantes” para cada usuário do aplicativo.

Estou lendo muito sobre “Singleton”, seus problemas, quando usá-lo, etc., e estas são as minhas conclusões até agora:

  • Confusão entre a implementação clássica do Singleton e a exigência real: TER APENAS UMA INSTÂNCIA DE UMA CLASSE!

  • É geralmente mal implementado. Se você quiser uma instância única, não use o padrão (anti) de usar um método estático GetInstance () retornando um object estático. Isso faz com que uma class seja responsável por instanciar uma única instância de si mesma e também executar lógica. Isso quebra o Princípio de Responsabilidade Única . Em vez disso, isso deve ser implementado por uma class de fábrica com a responsabilidade de garantir que apenas uma instância exista.

  • É usado em construtores, porque é fácil de usar e não deve ser passado como um parâmetro. Isso deve ser resolvido usando injeção de dependência , que é um ótimo padrão para obter um modelo de object bom e testável.

  • Não é TDD . Se você fizer TDD, as dependencies serão extraídas da implementação porque você deseja que seus testes sejam fáceis de escrever. Isso faz com que seu modelo de object seja melhor. Se você usar o TDD, não gravará um GetInstance =) estático. BTW, se você pensar em objects com responsabilidades claras em vez de classs, você obterá o mesmo efeito =).

Eu realmente discordo do monte de variables ​​globais em uma ideia de fantasia . Singletons são realmente úteis quando usados ​​para resolver o problema certo. Deixe-me dar um exemplo real.

Certa vez, desenvolvi um pequeno software para um lugar onde trabalhei, e alguns formulários precisavam usar algumas informações sobre a empresa, seus funcionários, serviços e preços. Em sua primeira versão, o sistema continuava carregando os dados do database toda vez que um formulário era aberto. Claro, logo percebi que essa abordagem não era a melhor.

Então eu criei uma class singleton, chamada company , que encapsulava tudo sobre o local, e estava completamente preenchida com dados no momento em que o sistema foi aberto.

Não era apenas um monte de variables ​​em um vestido extravagante, porque isso tinha dezenas de responsabilidades, como comunicar-se com a camada de persistência para salvar / recuperar dados sobre a empresa, lidar com funcionários e preços, etc.

Além disso, era um ponto fixo, amplo e de fácil access para ter os dados da empresa.

Singletons são muito úteis, e usá-los não é em si um anti-padrão. No entanto, eles obtiveram uma má reputação em grande parte porque forçam qualquer código consumidor a reconhecer que eles são um singleton para interagir com eles. Isso significa que, se você precisar “un-singletonize”, o impacto na sua base de código pode ser muito significativo.

Em vez disso, eu sugeriria esconder o Singleton atrás de uma fábrica. Dessa forma, se você precisar alterar o comportamento de instanciação do serviço no futuro, basta alterar a fábrica, em vez de todos os tipos que consomem o Singleton.

Melhor ainda, use uma inversão do contêiner de controle! A maioria permite que você separe o comportamento de instanciação da implementação de suas classs.

Uma coisa assustadora em singletons em Java, por exemplo, é que você pode acabar com várias instâncias do mesmo singleton em alguns casos. A JVM identifica exclusivamente com base em dois elementos: um nome totalmente qualificado da class e o carregador de class responsável por carregá-lo.

Isso significa que a mesma class pode ser carregada por dois carregadores de classs sem saber uns dos outros, e diferentes partes de seu aplicativo teriam instâncias diferentes desse singleton com as quais interagem.

Escreva objects injetáveis ​​normais, testáveis ​​e deixe Guice / Spring / o que quer que manipule a instanciação. A sério.

Isso se aplica até mesmo no caso de caches ou qualquer que seja o caso de uso natural de singletons. Não há necessidade de repetir o horror de escrever código para tentar impor uma instância. Deixe sua estrutura de injeção de dependência lidar com isso. (Eu recomendo Guice para um contêiner DI leve, se você não estiver usando um).