Singleton: Como deve ser usado

291

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.
:-)

Loki Astari
fonte
36
E se você decidir mais tarde que deseja vários registradores? Ou vários conjuntos de encadeamentos? Se você deseja apenas um criador de logs, crie apenas uma instância e torne-a global. Singletons só são bons se você PRECISA absolutamente que sempre exista apenas um e PRECISA ser global, IMHO.
3
Quem disse que uma estrutura pode ter apenas 1 instância de logger. Um singelton representando o Framework. O Framwork pode fornecer loggers específicos.
Martin Iorque
Sim. Eu não usaria um singeltong como um threadpool. Apenas lançando idéias para gerar respostas.
Martin Iorque
@ Dan Singleton que implementa o padrão de estratégia. O comportamento é abstraído de singleton. Singleton é um único ponto de entrada. Não tenha dois registradores, tenha um que possa decidir como registrar. Você não pode produzir apenas um registro de cada vez, não é necessário ter dois.
Lee Louviere 29/07
5
Xaade: e se você quiser fazer logon em dois arquivos? Ou para um banco de dados? Ou uma tomada de rede? Ou um widget da GUI? O ponto é que não adicione restrições artificiais - não há necessidade. Quantas vezes você criou acidentalmente dois para loops em vez de apenas um? Se você deseja apenas um criador de logs, crie apenas um.

Respostas:

182

Todos vocês estão errados. Leia a pergunta. Responda:

Use um Singleton se:

  • Você precisa ter um e apenas um objeto de um tipo no sistema

Não use um Singleton se:

  • Você quer economizar memória
  • Você quer tentar algo novo
  • Você quer mostrar o quanto você sabe
  • Porque todo mundo está fazendo isso (Veja programador de cultos de carga na wikipedia)
  • Em widgets da interface do usuário
  • É suposto ser um cache
  • Em cordas
  • Em Sessões
  • Eu posso ir o dia inteiro

Como criar o melhor singleton:

  • Quanto menor, melhor. Eu sou minimalista
  • Certifique-se de que é seguro para threads
  • Verifique se nunca é nulo
  • Verifique se ele foi criado apenas uma vez
  • Inicialização lenta ou do sistema? Até suas necessidades
  • Às vezes, o sistema operacional ou a JVM cria singletons para você (por exemplo, em Java, toda definição de classe é um singleton)
  • Forneça um destruidor ou, de alguma forma, descubra como dispor de recursos
  • Use pouca memória
Javaxpert
fonte
14
Na verdade, acho que você também não está correto. Eu reformularia como: "Se você precisa ter um e apenas um objeto de um tipo no sistema E precisa ter acesso global a ele" A ênfase na necessidade é minha - não faça isso se for conveniente, apenas se você DEVE tê-lo.
91
Você também está errado. Se você precisar de um e apenas um objeto, crie um e apenas um. Se não houver uma maneira lógica de acomodar duas instâncias sem danificar irreversivelmente o aplicativo, considere torná-lo um singleton. E há o outro aspecto, acesso global: se você não precisar de acesso global à instância, não deve ser um singleton.
jalf
4
Fechado para modificação, aberto para extensão. O problema é que você não pode estender um singleton para ser um duoton ou um tripleton. Está preso como um singleton.
9788 Lee Juliet
2
@ enzom83: Um Singleton maiúsculo-S inclui código para garantir sua singularidade. Se você deseja apenas uma instância, pode perder esse código e simplesmente criar uma instância ... fornecendo a economia de memória de uma única instância, além da economia com a obviação do código de aplicação da singeleza - o que também significa não sacrificar o código. capacidade de criar uma segunda instância, se alguma vez seus requisitos mudarem.
cHao 6/01/14
4
"Se você precisar ter um e apenas um objeto de um tipo no sistema" - "... e nunca quiser zombar desse objeto em um teste de unidade."
22614 Cygon
72

Singletons permitem combinar duas características ruins em uma classe. Isso está errado em praticamente todos os aspectos.

Um singleton fornece:

  1. Acesso global a um objeto e
  2. Garantia de que não é possível criar mais de um objeto desse tipo

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

std::ostream os;
os << "hello world\n";

Quando você pretendia escrever

std::cout << "hello world\n";

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.

jalf
fonte
3
ou você pode torná-lo global e obter apenas uma das desvantagens de um singleton. Com o singleton, você se limita simultaneamente a uma instância dessa classe de banco de dados. Por que fazer isso? Ou você pode ver por que você tem tantas dependências que a lista de instanciações se torna "realmente longa". Eles são todos necessários? Alguns deles devem ser delegados a outros componentes? Talvez alguns deles possam ser agrupados em uma estrutura para que possamos transmiti-los como um único argumento. Existem muitas soluções, todas elas melhores do que singletons.
jalf
6
Sim, um singleton pode ser justificado lá. Mas acho que você acabou de provar meu argumento de que isso só é necessário em casos bastante exóticos. A maioria dos softwares não lida com o arado de neve. Mas ainda não estou convencido. Concordo que, na sua inscrição atual, você deseja apenas uma delas. Mas e os testes de unidade? Cada um deles deve ser executado isoladamente; portanto, idealmente, eles devem criar seu próprio SpreaderController - o que é difícil de fazer com um singleton. Por fim, por que seus colegas de trabalho criariam várias instâncias em primeiro lugar? Esse é um cenário realista para se proteger?
jalf
3
E um ponto que você perdeu é que, embora seus dois últimos exemplos justifiquem a limitação da "única instância", eles não fazem nada para justificar a limitação "acessível globalmente". Por que diabos toda a base de código deve ser capaz de acessar a unidade de administração da central telefônica? O ponto em um singleton é fornecer as duas características. Se você só precisa de um ou de outro, não deve usar um singleton.
jalf
2
@ jalf - Meu objetivo era apenas dar um exemplo de onde Singleton é útil na natureza, já que você não pode imaginar; Eu acho que você não vê muitas vezes aplicá-lo à sua linha de trabalho atual. Eu mudei para a programação de arado de neve a partir de aplicativos de negócios apenas porque isso me permitiria usar o Singleton. :) j / k Eu concordo com sua premissa de que existem maneiras melhores de fazer essas coisas, você me deu muito em que pensar. Obrigado pela discussão!
1911 J. Polfer
2
Usar o "padrão" singleton ( AHEM! ) Para impedir as pessoas de instanciar mais instâncias é simplesmente estúpido, apenas para impedir que as pessoas acedam a isso. Quando eu tenho uma variável local foo1 do tipo Foo na minha pequena função e quero apenas uma na função, não estou preocupado que alguém crie um segundo foo2 foo2 e use-o em vez do original.
Thomas Eding
36

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.

DrPizza
fonte
6
Acordado. Dois erros podem dar certo, de acordo com alguns, no mundo real. Mas na programação, misturar duas idéias ruins não resulta em uma boa.
jalf
27

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.

Paweł Hajdan
fonte
8
Não, um singleton é muito mais que uma variável global disfarçada. Isso é o que a torna especialmente ruim. Combina a globalidade (que geralmente é ruim) com outro conceito que também é ruim (o de não deixar o programador instanciar uma classe se ele decidir que precisa de uma instância). Eles são frequentemente usados como variáveis ​​globais, sim. E então eles arrastam o outro efeito colateral desagradável e prejudicam a base de código.
jalf
7
Também deve ser observado que os singletons não precisam ter acessibilidade pública. Um singleton pode muito bem ser interno à biblioteca e nunca exposto ao usuário. Portanto, eles não são necessariamente "globais" nesse sentido.
9119 Steven Evers
1
+1 por apontar a quantidade de dor que os Singletons estão testando.
#
1
@ jalf Não permitir que alguém crie mais de uma instância de uma classe não é uma coisa ruim. Se realmente houver apenas uma instância da classe instanciada que imponha o requisito. Se alguém decidir mais tarde que precisa criar outra instância, deve refatorá-la, porque nunca deveria ter sido um singleton em primeiro lugar.
William
2
@ William: e eu tive que ter vários registradores de vez em quando. Você não está defendendo um singleton, mas um local simples e antigo. Você quer saber que um criador de logs está sempre disponível. É para isso que serve um global. Você não precisa saber que nenhum outro agente de log pode ser instanciado , o que um singleton impõe. (tente escrever testes de unidade para o seu logger - que é muito mais fácil se você pode criar e destruí-lo, conforme necessário, e que não é possível com um singleton)
jalf
13

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 comimport 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.

Eli Courtwright
fonte
Quando alguma vez uma variável global é "boa"? Às vezes, eles são a melhor solução alternativa para um problema, mas nunca são "bons".
#
1
A variável global é boa quando usada em qualquer lugar e tudo pode ter acesso a ela. Uma implementação de uma máquina de turing de estado único pode fazer uso de um singleton.
9788 Lee Juliet
Gosto da camada de indireção nesta resposta: "quando seria bom / ruim usar um global". Tanto o DevSolar quanto o Lee Louviere obtêm o valor com o qual concordam, embora na hora da resposta não se soubesse quem iria comentar.
Praxeolitic
6

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.

tenpn
fonte
6
  • Como você implementa um Singleton corretamente

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.

Mark Ransom
fonte
10
Bem, ouvi dizer que você gostava de Singletons, então fiz um Singleton para o seu Singleton, para que você possa fazer um teste padrão enquanto faz o teste padrão.
Eva
@ Eva, sim, algo assim. Não criei o problema, apenas tive que fazê-lo funcionar de alguma forma.
Mark Ransom
5

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.

Roubar
fonte
Yep que é observado nos comentários abaixo: * Limitação: Single Threaded Projeto * Veja: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Para problemas associados com bloqueio em aplicações multi-threaded
Martin Iorque
O singleton clássico com apenas getInstance como método estático e métodos de instância para outras operações nunca pode tornar o thread seguro. (bem menos que você torná-lo um armazenamento local de thread single-per-thread-tonelada usando ...)
Tobi
mesmo em c ++ 11 ou posterior?
Hg_git 5/09
5

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:

  1. instanciação preguiçosa ou inicial
  2. útil para um objeto que requer configuração e / ou estado

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.

lexh
fonte
3

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.

Brian
fonte
O Prototype Pattern também faz isso e é mais flexível. Você também pode usá-lo quando seu cliente criar muitas instâncias de sua classe cara, mas apenas um número limitado delas realmente possui um estado diferente. Por exemplo, tetronimos em Tetris.
Eva
3

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.

ZebZiggle
fonte
Os próprios madeireiros não devem ser singletons. O sistema geral de mensagens "broadcast" deve ser. Os próprios registradores são assinantes das mensagens de difusão.
CashCow
Pools de threads também não devem ser singletons. A questão geral é: você gostaria de mais de um deles? Sim. Quando os usei pela última vez, tínhamos 3 pools de encadeamentos diferentes em um aplicativo.
precisa saber é o seguinte
3

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.

Brad Barker
fonte
3

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:

gogole
fonte
2

Mas quando preciso de algo como um Singleton, muitas vezes acabo usando um contador Schwarz para instancia-lo.

Matt Cruikshank
fonte
2

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:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

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.

A. Gupta
fonte
2
Definitivamente não é melhor. 1: Você não está definindo a semântica de propriedade usando o ponteiro. Você nunca deve usar ponteiros em C ++, a menos que esteja preparado para gerenciá-los. 2: Seu uso do bloqueio com verificação dupla é antiquado e existem maneiras muito melhores de fazer isso. 3: Seus comentários sobre destruição são ingênuos. A recuperação da memória não é o ponto de uso do destruidor, é a limpeza. Sugestões para uma versão melhor: Veja a pergunta. A versão apresentada já é muito melhor.
Martin York
1

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.

Matt Cruikshank
fonte
45
Regras rígidas e rápidas sobre a contratação farão com que você perca uma grande diversidade de funcionários em potencial.
194 Karl Karl
13
Existe uma grande diversidade de idiotas. Isso não significa que eles devam ser considerados para contratação. Se alguém puder mencionar nenhum padrão de design, acho que seria preferível a alguém que conhece o singleton e nenhum outro padrão.
jalf
3
Para o livro de registros - minha resposta foi explícita. No meu processo real de entrevistas, tento avaliar se precisaremos orientar alguém em C ++ e quão difícil será. Alguns dos meus candidatos favoritos são pessoas que NÃO conhecem C ++ por dentro e por fora, mas pude ter uma ótima conversa com eles sobre isso.
Matt Cruikshank
4
Down vote. Da minha experiência pessoal - o programador pode não ser capaz de nomear outros padrões além de Singleton, mas isso não significa que ele use Singletons. Pessoalmente, eu estava usando singletons no meu código ANTES de ouvi-los (os chamei de "globais mais inteligentes" - sabia o que é o global). Quando aprendi sobre eles, quando aprendi sobre seus prós e contras - parei de usá-los. De repente, o teste de unidade se tornou muito mais interessante para mim quando parei ... Isso me tornou um programador pior? Pfff ...
Paulius
3
Também estou votando pela questão sem sentido "nomeie alguns padrões de design". Projetar é entender como aplicar padrões de design, não apenas ser capaz de identificar seus nomes. Ok, isso pode não justificar um voto negativo, mas esta resposta é troll-ish.
CashCow
0

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.

Adam Franco
fonte
Votado por 2 razões: 1. O Singleton pode usar internamente instâncias polimórficas (por exemplo, o Logger global usa estratégias polimórficas de segmentação) 2. Pode haver typedef para o nome singleton, portanto, o código depende facultativamente do typedef.
gamedev topright
Acabei construindo minha versão de um singleton para ser extensível usando o padrão curiosamente recorrente do modelo.
Zachary Kraus
0

Eu acho que esta é a versão mais robusta para c #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Aqui está a versão otimizada para .NET :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Você pode encontrar esse padrão em dotfactory.com .

artur02
fonte
3
Você pode retirar as partes que não são especificamente relevantes para os Singletons para facilitar a leitura do código.
Martin York
Além disso, sua primeira versão não é segura para threads devido a uma possível reordenação de leitura / gravação. Veja stackoverflow.com/questions/9666/…
Thomas Danecker
5
Linguagem errada? A questão está obviamente marcada com C ++ .
#
0

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:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

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.

CashCow
fonte
0

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.

Zachary Kraus
fonte
Não entendo por que você acha que tem que ser global.
Martin York
de acordo com esta discussão, todo mundo estava dizendo a exclusivo tem de ser global
Zachary Kraus
1
Não. O encadeamento indica que um singelton tem estado global. Não que seja uma variável global. A solução que você propõe possui um estado global. A solução que você propõe também está usando uma variável global; um membro estático de uma classe é um objeto de "Duração estática do armazenamento" e uma variável global é um objeto de "Duração estática do armazenamento". Assim, os dois são basicamente a mesma coisa com semânticas / escopos ligeiramente diferentes.
Martin York
Portanto, uma variável estática privada ainda é global devido à "Duração do armazenamento estático"?
Zachary Kraus
1
Nota: Você perdeu minha parte deliberadamente inexistente. Seu design de usar um membro estático "privado" não é ruim da mesma maneira que um singelton. Porque não introduz "estado mutável global". Mas também não é um singleton. Um singleton é uma classe projetada para que apenas uma instância do objeto possa existir. O que você está sugerindo é um único estado compartilhado para todos os objetos de uma classe. Conceito diferente
Martin York
0

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.

Michael Avraamides
fonte
0

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 ...

La VloZ Merrill
fonte
-1

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!

Martin Beckett
fonte
Mas isso está implorando a pergunta; por que o idioma do usuário e caminho para o arquivo de ajuda tem que ser método casos em tudo ?
DrPizza 18/09/08
2
Temos globais para isso. Não há nenhuma necessidade de fazê-los singletons
jalf
Variáveis ​​globais - então como você as serializa no registro / banco de dados? Classe Gobal - então como você garante que exista apenas um deles?
22330 Martin Beckett
@mgb: você os serializa lendo valores do registro / banco de dados e armazenando-os nas variáveis ​​globais (isso provavelmente deve ser feito na parte superior da sua função principal). você garante que haja apenas um objeto de uma classe, criando apenas um objeto de classe ... realmente ... é difícil 'grep -rn "novo \ + global_class_name".' ? realmente?
Paulius
7
@mgb: Por que diabos eu garantiria que haja apenas um? Eu só preciso saber que uma instância sempre representa as configurações atuais . mas não há razão para que eu não tenha permissão para ter outros objetos de configurações por todo o local. Talvez um para "as configurações que o usuário está definindo no momento, mas ainda não tenha aplicado", por exemplo. Ou um para "a configuração que o usuário salvou anteriormente para que ele possa retornar posteriormente". Ou um para cada um dos seus testes de unidade.
jalf
-1

Outra implementação

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};
user1135926
fonte
3
Isso é realmente horrível. Consulte: stackoverflow.com/questions/1008019/c-singleton-design-pattern/… para obter uma melhor inicialização lenta e uma destruição determinística mais importante garantida.
Martin York
Se você usar ponteiros, Instance()retorne um ponteiro, não uma referência. Dentro do seu .cpparquivo, inicializar a instância como nulo: Singleton* Singleton::instance_ = nullptr;. E Instance()deve ser implementada como: if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis