Editar: A partir de outra pergunta, forneci uma resposta com links para muitas perguntas / respostas sobre singletons: Mais informações sobre singletons aqui:
Então, eu li o tópico Singletons: bom design ou muleta?
E o argumento ainda continua.
Eu vejo Singletons como um padrão de design (bom e ruim).
O problema com o Singleton não é o padrão, mas os usuários (desculpe a todos). Todo mundo e seu pai pensam que podem implementá-lo corretamente (e das muitas entrevistas que fiz, a maioria das pessoas não). Também porque todos pensam que podem implementar um Singleton correto, abusam do padrão e o usam em situações que não são apropriadas (substituindo variáveis globais por singletons!).
Portanto, as principais perguntas que precisam ser respondidas são:
- Quando você deve usar um Singleton
- Como você implementa um Singleton corretamente
Minha esperança neste artigo é que possamos coletar juntos em um único local (em vez de precisar pesquisar e pesquisar em vários sites) uma fonte autorizada de quando (e depois como) usar um Singleton corretamente. Também seria apropriada uma lista de anti-usos e implementações ruins comuns, explicando por que eles não funcionam e, para as boas implementações, suas fraquezas.
Portanto, faça a bola rolar:
vou segurar minha mão e dizer que é isso que uso, mas provavelmente tem problemas.
Eu gosto de "Scott Myers" lidar com o assunto em seus livros "Effective C ++"
Boas situações para usar Singletons (não muitos):
- Estruturas de log
- Pools de reciclagem de threads
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
ESTÁ BEM. Vamos reunir algumas críticas e outras implementações.
:-)
fonte
Respostas:
Todos vocês estão errados. Leia a pergunta. Responda:
Use um Singleton se:
Não use um Singleton se:
Como criar o melhor singleton:
fonte
Singletons permitem combinar duas características ruins em uma classe. Isso está errado em praticamente todos os aspectos.
Um singleton fornece:
O número um é direto. Globais são geralmente ruins. Nunca devemos tornar objetos acessíveis globalmente, a menos que realmente precisemos.
O número dois pode parecer que faz sentido, mas vamos pensar sobre isso. Quando foi a última vez que você acidentalmente criou um novo objeto em vez de fazer referência a um existente? Como isso está marcado como C ++, vamos usar um exemplo dessa linguagem. Você escreve acidentalmente
Quando você pretendia escrever
Claro que não. Não precisamos de proteção contra esse erro, porque esse tipo de erro simplesmente não ocorre. Nesse caso, a resposta correta é ir para casa e dormir por 12 a 20 horas e espero que você se sinta melhor.
Se apenas um objeto for necessário, basta criar uma instância. Se um objeto deve ser acessível globalmente, torne-o global. Mas isso não significa que deve ser impossível criar outras instâncias.
A restrição "apenas uma instância é possível" não nos protege realmente contra possíveis erros. Mas não fazer o nosso código muito difícil refatorar e manter. Porque muitas vezes descobrimos mais tarde que precisávamos de mais de uma instância. Nós fazer ter mais de um banco de dados que têm mais de um objeto de configuração, queremos vários madeireiros. Nossos testes de unidade podem querer criar e recriar esses objetos a cada teste, para dar um exemplo comum.
Assim, um singleton deve ser usado se e somente se, precisamos de ambos os traços que ela oferece: Se precisar de acesso global (o que é raro, porque globals são geralmente desencorajados) e que precisam para impedir que alguém que nunca a criação de mais de uma instância de um classe (que me parece um problema de design). A única razão pela qual vejo isso é se a criação de duas instâncias danificaria o estado do aplicativo - provavelmente porque a classe contém vários membros estáticos ou bobagens semelhantes. Nesse caso, a resposta óbvia é consertar essa classe. Não deve depender de ser a única instância.
Se você precisar de acesso global a um objeto, torne-o global, como
std::cout
. Mas não restrinja o número de instâncias que podem ser criadas.Se você absolutamente, positivamente, precisa restringir o número de instâncias de uma classe a apenas uma, e não há como a criação de uma segunda instância possa ser tratada com segurança, então imponha isso. Mas não o faça globalmente acessível também.
Se você precisar dos dois traços, 1) torne-o um singleton e 2) deixe-me saber o que você precisa, porque estou tendo dificuldade em imaginar um caso assim.
fonte
O problema com singletons não é sua implementação. É que eles fundem dois conceitos diferentes, nenhum dos quais é obviamente desejável.
1) Singletons fornecem um mecanismo de acesso global a um objeto. Embora possam ser marginalmente mais seguros para threads ou marginalmente mais confiáveis em idiomas sem uma ordem de inicialização bem definida, esse uso ainda é o equivalente moral de uma variável global. É uma variável global vestida com alguma sintaxe incômoda (foo :: get_instance () em vez de g_foo, digamos), mas serve exatamente ao mesmo propósito (um único objeto acessível em todo o programa) e tem as mesmas desvantagens.
2) Singletons impedem várias instanciações de uma classe. É raro, IME, que esse tipo de recurso seja inserido em uma classe. Normalmente é uma coisa muito mais contextual; muitas das coisas que são consideradas como um e apenas um são realmente apenas um para ser um. Na IMO, uma solução mais apropriada é criar apenas uma instância - até você perceber que precisa de mais de uma instância.
fonte
Uma coisa com os padrões: não generalize . Eles têm todos os casos quando são úteis e quando falham.
Singleton pode ser desagradável quando você precisa testar o código. Geralmente você está preso a uma instância da classe e pode escolher entre abrir uma porta no construtor ou algum método para redefinir o estado e assim por diante.
Outro problema é que o Singleton, na verdade, nada mais é do que uma variável global disfarçada. Quando você tem muito estado compartilhado global em seu programa, as coisas tendem a voltar, todos nós sabemos disso.
Isso pode dificultar o rastreamento de dependências . Quando tudo depende do seu Singleton, é mais difícil alterá-lo, dividido em dois, etc. Você geralmente fica preso a ele. Isso também dificulta a flexibilidade. Investigue alguma estrutura de Injeção de Dependências para tentar aliviar esse problema.
fonte
Os singletons basicamente permitem que você tenha um estado global complexo em idiomas que, de outra forma, dificulta ou é impossível ter variáveis globais complexas.
O Java, em particular, usa singletons como um substituto para variáveis globais, pois tudo deve estar contido em uma classe. O mais próximo possível das variáveis globais são as variáveis estáticas públicas, que podem ser usadas como se fossem globais com
import static
C ++ possui variáveis globais, mas a ordem na qual os construtores de variáveis de classe global são invocadas é indefinida. Como tal, um singleton permite adiar a criação de uma variável global até a primeira vez que essa variável é necessária.
Idiomas como Python e Ruby usam muito pouco os singletons, porque você pode usar variáveis globais dentro de um módulo.
Então, quando é bom / ruim usar um singleton? Praticamente exatamente quando seria bom / ruim usar uma variável global.
fonte
O Design C ++ moderno da Alexandrescu possui um singleton genérico herdável e seguro para threads.
Pelo meu valor de 2p, acho importante ter vidas definidas para seus singletons (quando é absolutamente necessário usá-los). Normalmente, não deixo a
get()
função estática instanciar nada e deixo a instalação e a destruição para alguma seção dedicada do aplicativo principal. Isso ajuda a destacar dependências entre singletons - mas, como enfatizado acima, é melhor evitá-las, se possível.fonte
Há uma questão que eu nunca vi mencionada, algo que encontrei em um trabalho anterior. Tínhamos singletons em C ++ compartilhados entre DLLs e a mecânica usual de garantir que uma única instância de uma classe simplesmente não funcionasse. O problema é que cada DLL obtém seu próprio conjunto de variáveis estáticas, juntamente com o EXE. Se sua função get_instance estiver embutida ou fizer parte de uma biblioteca estática, cada DLL será encerrada com sua própria cópia do "singleton".
A solução é garantir que o código singleton seja definido apenas em uma DLL ou EXE ou criar um gerenciador singleton com essas propriedades para parcelar instâncias.
fonte
O primeiro exemplo não é seguro para threads - se dois threads chamam getInstance ao mesmo tempo, essa estática será uma PITA. Alguma forma de mutex ajudaria.
fonte
Como outros observaram, as principais desvantagens dos singletons incluem a incapacidade de estendê-los e a perda do poder de instanciar mais de uma instância, por exemplo, para fins de teste.
Alguns aspectos úteis dos singletons:
No entanto, você não precisa usar um singleton para obter esses benefícios. Você pode escrever um objeto normal que faça o trabalho e depois fazer com que as pessoas o acessem através de uma fábrica (um objeto separado). A fábrica pode se preocupar apenas em instanciar e reutilizá-lo etc., se necessário. Além disso, se você programar para uma interface em vez de uma classe concreta, a fábrica poderá usar estratégias, ou seja, você poderá ativar e desativar várias implementações da interface.
Finalmente, uma fábrica se presta a tecnologias de injeção de dependência como Spring etc.
fonte
Singletons são úteis quando você tem muito código em execução ao inicializar e se opor. Por exemplo, quando você usa o iBatis ao configurar um objeto de persistência, ele deve ler todas as configurações, analisar os mapas, verificar se tudo está correto, etc. antes de acessar seu código.
Se você fizesse isso sempre, o desempenho seria muito reduzido. Ao usá-lo em um único nó, você recebe esse hit uma vez e, em seguida, todas as chamadas subsequentes não precisam fazer isso.
fonte
A verdadeira queda dos Singletons é que eles quebram a herança. Você não pode derivar uma nova classe para fornecer funcionalidade estendida, a menos que tenha acesso ao código em que o Singleton é referenciado. Portanto, além do fato de o Singleton tornar seu código totalmente acoplado (corrigível por um Padrão de Estratégia ... também conhecido como Injeção de Dependência), ele também impedirá que você feche seções do código da revisão (bibliotecas compartilhadas).
Portanto, mesmo os exemplos de registradores ou conjuntos de threads são inválidos e devem ser substituídos por Estratégias.
fonte
A maioria das pessoas usa singletons quando tenta se sentir bem ao usar uma variável global. Existem usos legítimos, mas na maioria das vezes quando as pessoas os usam, o fato de que só pode haver uma instância é apenas um fato trivial em comparação com o fato de ser acessível globalmente.
fonte
Como um singleton permite apenas que uma instância seja criada, ele controla efetivamente a replicação da instância. por exemplo, você não precisaria de várias instâncias de uma pesquisa - um mapa de pesquisa morse, por exemplo, agrupá-lo em uma classe singleton é adequado. E só porque você tem uma única instância da classe, não significa que você também esteja limitado ao número de referências a essa instância. Você pode enfileirar chamadas (para evitar problemas de encadeamento) na instância e efetuar as alterações necessárias. Sim, a forma geral de um singleton é globalmente pública; você certamente pode modificar o design para criar um singleton com mais acesso restrito. Eu não cansei isso antes, mas com certeza sei que é possível. E para todos aqueles que comentaram que o padrão singleton é totalmente mau, você deve saber disso:
fonte
Mas quando preciso de algo como um Singleton, muitas vezes acabo usando um contador Schwarz para instancia-lo.
fonte
Abaixo está a melhor abordagem para implementar um padrão singleton seguro de thread com a desalocação da memória no próprio destruidor. Mas acho que o destruidor deve ser opcional, porque a instância singleton será automaticamente destruída quando o programa terminar:
Em relação às situações em que precisamos usar classes singleton, podemos: Se queremos manter o estado da instância ao longo da execução do programa. Se estamos envolvidos na gravação no log de execução de um aplicativo em que apenas uma instância do arquivo precisa. ser usado .... e assim por diante. Será apreciável se alguém puder sugerir otimização no meu código acima.
fonte
Eu uso Singletons como um teste de entrevista.
Quando peço a um desenvolvedor que cite alguns padrões de design, se tudo o que eles podem nomear é Singleton, eles não são contratados.
fonte
Anti-uso:
Um grande problema com o uso excessivo de singleton é que o padrão impede a fácil extensão e troca de implementações alternativas. O nome da classe é codificado permanentemente onde quer que o singleton seja usado.
fonte
Eu acho que esta é a versão mais robusta para c #:
Aqui está a versão otimizada para .NET :
Você pode encontrar esse padrão em dotfactory.com .
fonte
O padrão singleton de Meyers funciona bem o suficiente na maioria das vezes, e nas ocasiões em que ocorre, não é necessariamente necessário procurar algo melhor. Contanto que o construtor nunca jogue e não haja dependências entre singletons.
Um singleton é uma implementação para um objeto globalmente acessível (GAO a partir de agora), embora nem todos os GAOs sejam singletons.
Os próprios registradores não devem ser singletons, mas os meios para registrar devem ser idealmente acessíveis globalmente, para desacoplar onde a mensagem de log está sendo gerada, de onde ou como é registrada.
O carregamento preguiçoso / avaliação preguiçosa é um conceito diferente e o singleton geralmente também o implementa. Ele vem com muitos de seus próprios problemas, em particular segurança de threads e problemas se falhar, com exceções, de modo que o que parecia uma boa idéia na época acabou não sendo tão bom, afinal. (Um pouco como a implementação de COW em strings).
Com isso em mente, os GOAs podem ser inicializados assim:
Ele não precisa ser feito de maneira tão grosseira quanto essa, e claramente em uma biblioteca carregada que contém objetos, você provavelmente deseja outro mecanismo para gerenciar sua vida útil. (Coloque-os em um objeto que você obtém ao carregar a biblioteca).
Quanto a quando eu uso singletons? Usei-os para duas coisas - Uma tabela singleton que indica quais bibliotecas foram carregadas com dlopen - Um manipulador de mensagens no qual os loggers podem se inscrever e para os quais você pode enviar mensagens. Necessário especificamente para manipuladores de sinais.
fonte
Ainda não entendo por que um singleton precisa ser global.
Ia produzir um singleton onde ocultava um banco de dados dentro da classe como uma variável estática constante privada e criava funções de classe que utilizam o banco de dados sem nunca expor o banco de dados ao usuário.
Não vejo por que essa funcionalidade seria ruim.
fonte
Acho-os úteis quando tenho uma classe que encapsula muita memória. Por exemplo, em um jogo recente em que trabalho, tenho uma classe de mapas de influência que contém uma coleção de matrizes muito grandes de memória contígua. Quero que tudo seja alocado na inicialização, liberado no desligamento e, definitivamente, quero apenas uma cópia. Eu também tenho que acessá-lo de muitos lugares. Acho que o padrão singleton é muito útil nesse caso.
Tenho certeza de que existem outras soluções, mas acho esta muito útil e fácil de implementar.
fonte
Se você é quem criou o singleton e o utiliza, não o faça como singleton (não faz sentido, pois você pode controlar a singularidade do objeto sem torná-lo único), mas faz sentido quando você é desenvolvedor de um biblioteca e você deseja fornecer apenas um objeto para seus usuários (nesse caso, você é quem criou o singleton, mas não é o usuário).
Singletons são objetos, portanto, use-os como objetos, muitas pessoas acessam singletons diretamente chamando o método que o retorna, mas isso é prejudicial porque você está fazendo com que seu código saiba que o objeto é singleton, eu prefiro usar singletons como objetos, eu os passo através do construtor e eu os uso como objetos comuns, dessa forma, seu código não sabe se esses objetos são singletons ou não e isso torna as dependências mais claras e ajuda um pouco na refatoração ...
fonte
Em aplicativos de desktop (eu sei, apenas nós, os dinossauros, escrevemos mais isso!), Eles são essenciais para obter configurações globais de aplicativos relativamente imutáveis - o idioma do usuário, o caminho para arquivos de ajuda, as preferências do usuário etc. .
Editar - é claro que eles devem ser somente leitura!
fonte
Outra implementação
fonte
Instance()
retorne um ponteiro, não uma referência. Dentro do seu.cpp
arquivo, inicializar a instância como nulo:Singleton* Singleton::instance_ = nullptr;
. EInstance()
deve ser implementada como:if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.