Singletons são realmente tão ruins assim?

Duplicar Possível:
O que há de tão ruim em Singletons?

É compreensível que muitos padrões de design possam, em alguns casos, ser abusados ​​e, como a mãe, sempre disse: ” Muito de uma coisa boa nem sempre é boa!

Estou notando que hoje em dia, estou usando muito o Singleton, e estou preocupado que eu possa estar abusando do padrão de design, e me aprofundando mais e mais em um tipo de prática ruim.

Estamos desenvolvendo um aplicativo Flex que possui uma estrutura de dados hierárquica bastante grande mantida na memory enquanto o usuário trabalha nela. O usuário pode carregar, salvar, alterar e atualizar os dados sob demanda.

Esses dados são centralizados por meio de uma class Singleton, que agrega alguns ArrayCollections, Arrays, objects de valor e algumas outras variables ​​de membros nativos expostas por meio de getters e setters.

Para obter uma referência aos nossos dados de qualquer lugar no aplicativo, fazemos todo o tipo de coisa do método Model.getInstance (), com o qual todos estão familiarizados. Isso garante que sempre tenhamos access à mesma cópia de dados, pois, quando projetamos, dissemos que apenas uma vez é permitida a existência de uma instância durante a vida útil da aplicação.

A partir desse repository de dados central, é fácil, por exemplo, despachar events alterados de propriedade e ter vários componentes de interface do usuário que fazem referência aos dados centrais e atualizar suas exibições para refletir as alterações de dados ocorridas.

Até agora, esta abordagem tem sido eficaz e comprovada, muito prática para as nossas circunstâncias.

Eu estou achando, no entanto, que eu sou um pouco overeager ao criar novas classs. Perguntas como se uma class fosse um Singleton, ou deveria ser gerenciada de alguma outra maneira, como talvez usar uma fábrica, por exemplo, tendem a se tornar um pouco difíceis, com um pouco de incerteza.

Onde desenho a linha com singletons? Existe uma boa diretriz para decidir quando usar Singletons e quando ficar longe deles.

Além disso, alguém pode recomendar um bom livro sobre padrões de design?

A principal coisa a lembrar é que os padrões de design são apenas uma ferramenta para ajudá-lo a entender os conceitos abstratos. Uma vez que você tenha essa compreensão, restringir-se especificamente a uma “receita” de um livro é inútil e prejudica sua capacidade de escrever o código mais apropriado para o seu propósito.

Dito isso, a leitura de livros como o GoF apresentará a você mais maneiras de pensar sobre os problemas, de modo que quando chegar a hora de implementar algo por conta própria, você terá um conjunto mais amplo de outlook para abordar o problema.

No seu caso, se usar singleton faz sentido em todos os casos, vá em frente. Se isso “se encheckbox” e você precisa implementá-lo de alguma forma desajeitada, é necessário criar uma nova solução. Forçar um padrão que não é perfeito é como marcanvasr um pino quadrado em um buraco redondo.

Dado que você diz “esta abordagem tem sido eficaz e comprovada, muito prática para as nossas circunstâncias”, eu acho que você está indo bem.

Aqui estão alguns bons livros:

Gang of Four Book – o livro clássico para padrões de design

Head First Design Patterns – Eu ouvi isso recomendado por algumas pessoas como uma alternativa

Sim, singletons são ruins. Eles são ruins porque tudo o que fazem por você é combinar duas propriedades, cada uma das quais é ruim em 95% do tempo. (O que significaria que, em média, os singletons são ruins 99,75% do tempo;))

Um singleton, conforme definido pelo GoF, é uma estrutura de dados que:

  1. Concede access global a um object e
  2. Força que apenas uma instância do object possa existir.

O primeiro é geralmente considerado uma coisa ruim. Nós não gostamos de globals. O segundo é um pouco mais sutil, mas, em geral, praticamente não há casos em que essa seja uma restrição razoável a ser aplicada .

Às vezes, só faz sentido ter uma instância de um object. Nesse caso, você escolhe criar apenas um. Você não precisa de um singleton para impor isso.

E geralmente, mesmo quando “faz sentido” ter apenas uma instância, acaba por não fazer sentido. Mais cedo ou mais tarde, você precisará de mais de um logger. Ou mais de um database. Ou você terá que recriar resources para cada um dos seus testes de unidade, o que significa que temos que ser capazes de criá-los à vontade. Está removendo prematuramente a flexibilidade do nosso código antes de entendermos as conseqüências.

Singletons ocultam dependencies e aumentam o acoplamento (cada class pode potencialmente depender de um singleton, o que significa que a class não pode ser reutilizada em outros projetos, a menos que reutilizemos todos os singletons) e porque essas dependencies não são visíveis imediatamente (como parâmetros de function / construtor) ), não os notamos e normalmente não pensamos nisso quando os criamos. É tão fácil simplesmente pegar um singleton, ele age quase como uma variável local e tudo, então nós tendemos a usá-los muito quando eles estão lá. E isso os torna quase impossíveis de remover novamente. Você acaba, talvez não com código de espaguete, mas com charts de dependência de espaguete. E, mais cedo ou mais tarde, suas dependencies descontroladas significarão que os singletons começam dependendo uns dos outros, e então você obtém dependencies circulares quando se tenta inicializar.

Eles tornam extremamente difícil o teste da unidade. (Como você testa uma function que chama funções em um object singleton? Não queremos que o código singleton real seja exercitado, mas como evitar isso?

Sim, singletons são ruins.

Às vezes, você realmente quer um global. Então use um global, não um singleton.

Às vezes, muito raramente, você pode ter uma situação em que criar várias instâncias de uma class é um erro, onde isso não pode ser feito sem causar erros. (Sobre o único caso em que posso pensar, e mesmo isso é complicado, é se você está representando algum dispositivo de hardware. Você só tem uma GPU, então se você for mapeá-la para um object em seu código, ela faz sentido que apenas uma instância possa existir). Mas se você se encontrar em tal situação (e novamente, para ênfase, uma situação em que várias instâncias causam erros graves, não apenas uma situação em que “não consigo pensar em nenhum caso de uso para mais de uma instância”), essa restrição, mas faça isso sem também tornar o object visível globalmente.

Cada uma dessas duas propriedades pode ser útil, em casos raros. Mas não consigo pensar em um único caso em que a combinação deles seria uma coisa boa.

Infelizmente, muitas pessoas têm a ideia de que “Singletons são globals compatíveis com OOP”. Não, eles não são. Eles ainda sofrem os mesmos problemas que os globals, além de apresentar alguns outros, completamente não relacionados. Não há absolutamente nenhuma razão para preferir um singleton a um velho e simples global.

Desenvolvedores de software parecem ser divididos em dois campos, dependendo se eles favorecem um estilo de codificação idealista ou um estilo pragmático:

  • Idealista: nunca use o padrão singleton.
  • Pragmática: Evite o padrão singleton.

Pessoalmente, sou a favor da abordagem pragmática. Às vezes, faz sentido quebrar as regras, mas somente se você realmente entender o que está fazendo e estiver disposto a aceitar os riscos associados. Se você puder responder “sim” às perguntas abaixo sobre seu caso de uso específico, o padrão singleton pode gerar alguns benefícios práticos.

  • O singleton é externo ao seu aplicativo? Bancos de dados, serviços de enfileiramento e ESBs são exemplos de macro perfeitamente válidos do padrão singleton.
  • KISS: O seu aplicativo inteiro é limitado a 2-3 singletons internos?
  • DRY: Esses singletons são inerentemente globais e, portanto, resultariam na necessidade de inserir referências em quase todos os objects em seu aplicativo? (por exemplo, um registrador ou mediador de componente)?
  • Seus singletons dependem apenas uns dos outros e / ou do ambiente operacional?
  • Você garantiu sequências adequadas de boot e desligamento para cada singleton, incluindo considerações sobre gerenciamento de memory? Por exemplo, um pool de threads estilo “Grand Central” pode precisar ter os methods Run () e Shutdown () da instância em main () para garantir que as tarefas sejam executadas somente quando os objects em que operam estiverem em um estado válido.

Singletons não matam programas, programadores matam programas.

Como qualquer construção de programação, quando usada apropriadamente, você não vai se atirar no pé.

Os livros recomendados são bons, mas nem sempre fornecem informações suficientes sobre quando você pode escolher usar o Singleton.

Essa experiência só acontece quando você descobre que o Singleton é uma má escolha quando você precisa ter várias instâncias e, de repente, você tem muitos problemas para injetar as referências de objects em todos os lugares.

Às vezes, é melhor seguir em frente e ter as referências de object no lugar, mas o fato de você estar usando o Singleton ajuda a identificar o escopo do problema que você enfrentaria se tivesse que refatorá-lo para um design diferente. O que eu acredito é uma coisa muito boa: isto é, ter apenas uma aula (mesmo que mal projetada) dá alguma habilidade para ver os efeitos de uma mudança na class.

Nós começamos um projeto onde estamos basicamente enfrentando a mesma questão, isto é, como acessar o modelo , e especialmente seu elemento-raiz. O projeto não é um aplicativo Flex, mas um jogo! aplicativo da web, mas isso não importa realmente.

Ter um único object único no sistema é bom, o problema é como acessá-lo . Assim, o debate sobre o singleton está relacionado à noção de inversão de dependência (DI) e como obter objects.

Os principais argumentos para o DI são os seguintes:

  • testabilidade e zombaria
  • dissociação da instanciação de objects do uso (o que pode levar ao gerenciamento do ciclo de vida)
  • separação de preocupações

Possíveis abordagens para DI são (veja o artigo clássico de Fowler):

  • passar o object nos parâmetros do método
  • localizador de serviço
  • Estrutura de DI

Sob essa perspectiva, o padrão singleton é apenas um tipo de localizador de serviço, por exemplo, Model.getInstance() .

Mas, para fornecer flexibilidade máxima diante de mudanças futuras, a referência ao object exclusivo deve ser passada o mais possível e obtida com Model.getInstance() somente quando necessário. Isso também renderá código mais limpo.

Na minha opinião, o uso de Singletons sinaliza diretamente uma falha de design. A razão é simplesmente que eles permitem ignorar os mecanismos normais de criação e destruição de objects embutidos no C ++. Se um object precisar de uma referência a outro object, ele deve passar uma referência a ele na construção ou criar uma nova instância dele internamente. Mas quando você usa um singleton, você está explicitamente ofuscando o ciclo de criação e desassembly. Um problema relacionado é que é extremamente difícil controlar o tempo de vida de um singleton. Como resultado, muitos pacotes que incluem implementações singleton genéricas também incluem gerenciadores de vida útil de objects desajeitados e similares. Às vezes me pergunto se isso não existe simplesmente para administrar os singletons.

Basicamente, se você precisar usar um object em muitos lugares, ele deve ser criado explicitamente no ponto mais alto da pilha e depois transmitido por referência a todos que o usam. Às vezes, as pessoas usam Singletons porque têm problemas ao passar vários argumentos para novos threads, mas não se encheckboxm nisso, definam explicitamente seus argumentos de thread e os passem para o novo thread da mesma maneira. Você verá que seu programa flui muito mais limpo e não há surpresas desagradáveis ​​devido a dependencies de boot estática ou a uma desassembly incorreta.

Singletons certamente não são ruins. Eles têm seus usos, alguns deles muito bons. Singletons tendem a ser usados ​​em demasia por desenvolvedores inexperientes, já que muitas vezes é o primeiro patten de design que eles aprendem, e é bastante simples, então eles jogam em torno de todo o lugar sem pensar nas implicações.

Toda vez que você quiser usar um singleton, tente considerar por que você está fazendo isso, e quais são os benefícios e negativos de usar esse padrão.

Singletons efetivamente criam um conjunto global acessível de ‘coisas’ (dados ou methods) e eu acho que a maioria das pessoas concorda que usar muitas variables ​​globais não é uma ótima idéia. O objective das classs e da orientação a objects é agrupar as coisas em áreas distintas, em vez de apenas colocar tudo em um espaço global massivo.

Um dos “padrões” que eu acho que tendem a preferir ao invés de singletons é passar os objects necessários para baixo do topo. Eu os crio uma vez durante a fase de boot de meus aplicativos e os passo por todos os objects que precisam acessá-los. Ela imita a parte de “criação única” de um padrão singleton, mas sem a parte “global”.

O ponto inteiro de um singleton é que é para objects onde apenas 1 deveria existir. Você menciona um conjunto de classs de controle de dados. Talvez considere que, na verdade, há casos em que um aplicativo pode querer criar dois conjuntos de classs de controle de dados, de modo que, talvez, impingir um singleton nisto não seja correto. Em vez disso, se você criasse essas classs de dados no init do aplicativo e as transmitisse, estaria criando apenas 1 conjunto, já que é o que o aplicativo atual exige, mas você deixa em aberto a possibilidade de que, em algum momento, precisar de um segundo conjunto você pode facilmente criá-los. Além disso, as classs de controle de dados devem ter access globalizado de qualquer lugar no aplicativo. Eu acho que não, em vez disso, eles provavelmente só seriam acessíveis a partir de uma camada de access a dados de nível mais baixo.

Algumas pessoas recomendaram o livro GOF. Eu diria, sim, que é um ótimo livro, mas primeiro tente encontrar um livro sobre arquitetura geral primeiro, leia sobre design de 2/3 / n camadas, encapsulamento, abstração e esses tipos de princípios primeiro. Isso lhe dará uma base mais sólida com a qual entender o uso apropriado dos padrões discutidos pelo GOF.

[Edit: A outra vez que uma variante singleton pode ser útil é quando você quer um único ponto de access para algo, mas o detalhe da implementação pode, na verdade, ser mais de uma coisa. O chamador não precisa saber que, sob as capas, sua solicitação para o object singleton é realmente resolvida contra vários objects disponíveis e um é retornado. Estou pensando em algo como um pool de threads aqui, onde o uso vai, hey, apenas me consiga um thread, eu preciso de 1, mas eu não me importo com qual]

Eu sei que este é um tópico antigo, mas ninguém parecia mencionar o padrão real que se ajusta ao que o OP estava tentando fazer. O que eu acredito que ele está descrevendo uma necessidade é chamado de Padrão Mediador . SourceMaking é um site fantástico para aprender / referenciar este tipo de informação. Definitivamente o meu lugar para introduzir pessoas a padrões de software. Além disso, geralmente é uma boa idéia não acreditar na noção de que qualquer padrão de design é necessariamente inerentemente bom ou ruim. Todos eles têm o seu uso, é apenas aprender quando e onde usá-los, esse é o truque. Pessoas que afirmam nunca usar Singletons, para mim, não entendem sua utilidade.

Não, eles não são necessariamente ruins.

Quanto a um livro, você precisa começar com os clássicos .

Singletons não são “tão ruins”. Se você tem um monte de Singletons relacionados e pode replace / consolidar um número deles usando uma Fábrica, sem perder nada com o que se importa, então é quando você deve fazer isso.

Quanto aos livros, bem, há uma espécie de cânone .

Eu pensei que singletons eram bons .

O Google parece estar convencido de que Singletons são uma má ideia.

Isso não é para sugerir que tudo que o Google faz é perfeito ou que toda a sua opinião é o fim de qualquer argumento, mas eles foram tão longe a ponto de escrever este detector Singleton para erradicá-los. Faça sua própria mente.