Pergunta

Edit: De outra pergunta que eu forneci uma resposta que tem links para um monte de perguntas / respostas sobre singletons: mais informações sobre singletons aqui:

Então eu li o fio Singletons:? Bom design ou uma muleta
E o argumento ainda grassa.

Eu vejo Singletons como um padrão de design (boas e más).
O problema com Singleton não é o padrão, mas sim os usuários (todo mundo desculpe). Todos e seu pai acha que eles podem implementar um corretamente (e das muitas entrevistas que tenho feito, a maioria das pessoas não pode). Também porque todo mundo pensa que eles possam implementar um Singleton correta abusam o padrão e usá-lo em situações que não são apropriadas (substituindo variáveis ??globais com Singletons!).

Assim, as principais questões que precisam ser respondidas são:

  • Quando você deve usar um Singleton
  • Como você implementar um Singleton corretamente

A minha esperança para este artigo é que podemos reunir em um único lugar (em vez de ter que Google e procure vários sites) uma fonte autorizada de quando (e, em seguida, como) para usar um Singleton corretamente. Também apropriado seria uma lista de Anti-Usos e implementações ruins comuns explicando por que eles não conseguem trabalho e por boas implementações de suas fraquezas.


Assim, fazer a bola rolar:
Eu vou segurar minha mão para cima e dizer que este é o que eu uso, mas provavelmente tem problemas.
I like "Scott Myers" manipulação do assunto em seus livros "Effective C ++"

situações boas para usar Singletons (não muitos):

  • estruturas de Registro
  • Tópicos reciclagem piscinas
/*
 * 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;
        }
};

OK. Permite obter algumas críticas e outras implementações juntos.
: -)

Foi útil?

Solução

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

Use um Singleton se:

  • Você precisa ter um e somente um objeto de um tipo de sistema

Não use um Singleton se:

Como criar o melhor Singleton:

  • Quanto menor, melhor. Eu sou um minimalista
  • Certifique-se de que é seguro para threads
  • Certifique-se que nunca é nulo
  • Certifique-se de que ele é criado apenas uma vez
  • preguiçoso ou sistema de inicialização? -Se com suas necessidades
  • Às vezes o sistema operacional ou a JVM cria singletons para você (por exemplo, em Java cada definição de classe é um singleton)
  • Fornecer um processo de destruição ou de alguma forma descobrir como eliminar recursos
  • Use pouca memória

Outras dicas

Singletons dar-lhe a capacidade de combinar duas características ruins em uma classe. Isso está errado em praticamente todos os sentidos.

Um singleton dá-lhe:

  1. O acesso global a um objeto, e
  2. A garantia de que não mais do que um objeto deste tipo pode jamais ser criado

O número um é simples. Globals são geralmente ruim. Nunca devemos fazer objetos globalmente acessível a menos que realmente precisa.

O número dois pode parecer que faz sentido, mas vamos pensar sobre isso. Quando foi a última vez que você ** acidentalmente * criado um novo objeto, em vez de fazer referência a um já existente? Uma vez que este é marcado C ++, vamos usar um exemplo de que a linguagem. Você costuma escrever acidentalmente

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

Quando você pretendia escrever

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

Claro que não. Nós não precisamos de proteção contra esse erro, porque esse tipo de erro só não acontece. Se isso acontecer, a resposta correta é ir para casa e dormir por 12-20 horas e espero que você se sentir melhor.

Se apenas um objeto é necessário, basta criar uma instância. Se um objeto deve ser globalmente acessível, torná-lo um global. Mas isso não significa que ele deve ser impossível criar outras instâncias do mesmo.

O "apenas uma instância é possível" restrição realmente não nos proteger contra bugs prováveis. Mas faz tornar nosso código muito difícil refatorar e manter. Porque muitas vezes descobrimos depois que foi preciso mais de uma instância. Nós do ter mais de um banco de dados nós não ter mais de um objeto de configuração, queremos vários madeireiros. Nossos testes de unidade pode querer ser capaz de criar e recriar esses objetos 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 nós necessidade acesso global (o que é raro, porque globals são geralmente desanimado) e que necessidade para impedir que alguém sempre a criação de mais de uma instância de uma classe (que soa para mim como um problema de design). A única razão que eu posso ver por isso é se a criação de duas instâncias iria corromper o nosso estado do aplicativo - provavelmente porque a classe contém um número de membros estáticos ou bobagem semelhante. Caso em que a resposta óbvia é para corrigir essa classe. Ele não deve depender de ser a única instância.

Se você precisar de acesso global a um objeto, torná-lo um global, como std::cout. Mas não restringir o número de instâncias que podem ser criados.

Se você absolutamente, precisa positivamente para limitar o número de instâncias de uma classe para apenas um, e não há nenhuma maneira que a criação de uma segunda instância pode sempre ser manuseados com segurança, em seguida, fazer valer isso. Mas não torná-lo globalmente acessível também.

Se você precisa fazer ambas as características, então 1) torná-lo um singleton, e 2) deixe-me saber o que você precisa que para, porque eu estou tendo um tempo difícil imaginar tal caso.

O problema com singletons não é a sua implementação. É que eles confundem dois conceitos diferentes, nenhuma das quais é obviamente desejável.

1) Singletons fornecer um mecanismo de acesso global a um objeto. Embora possam ser marginalmente mais threadsafe ou marginalmente mais confiável em línguas sem uma ordem de inicialização bem definida, esse uso ainda é o equivalente moral de uma variável global. É uma variável global vestidos em alguma sintaxe estranha (foo :: get_instance () em vez de g_foo, digamos), mas serve o mesmo propósito exato (um único objeto acessível em todo o programa) e tem exatamente as mesmas desvantagens.

2) Singletons evitar que várias instâncias de uma classe. É raro, IME, que este tipo de recurso deve ser cozido em uma classe. É normalmente uma coisa muito mais contextual; um monte de coisas que são considerados como um-e-somente um são realmente só acontece-a-ser-somente um. IMO uma solução mais adequada é apenas para criar apenas um exemplo -. Até você perceber que você precisa de mais do que uma instância

Uma coisa com padrões: Não generalize . Eles têm todos os casos em que eles são úteis, e quando eles falham.

Singleton pode ser desagradável quando você tem que test o código. Você está geralmente preso com uma instância da classe, e pode escolher entre abrir uma porta no construtor ou algum método para repor o estado e assim por diante.

Outro problema é que o Singleton na verdade não é nada mais do que um variável global disfarçada. Quando você tem estado compartilhado demais global durante seu programa, as coisas tendem a voltar, todos nós conhecemos.

Pode fazer rastreamento de dependência mais difícil. Quando tudo depende do seu Singleton, é mais difícil mudá-lo, dividida para dois, etc. Você está geralmente preso com ele. Isso também dificulta a flexibilidade. Investigar alguns Dependency Injection quadro para tentar aliviar esta questão.

Singletons basicamente deixar de ter estado global complexo em línguas que de outra forma tornar difícil ou impossível ter variáveis ??globais complexos.

Java em particular utiliza singletons como um substituto para variáveis ??globais, já que tudo deve ser contido dentro de uma classe. O mais próximo se trata de variáveis ??globais são variáveis ??estáticas públicas, que podem ser utilizados como se fossem global com import static

C ++ tem variáveis ??globais, mas a ordem em que construtores de variáveis ??de classe globais são invocados é indefinido. Como tal, a exclusivo permite adiar a criação de uma variável global até que a primeira vez que a variável é necessária.

linguagens como Python e Ruby singletons uso muito pouco, porque você pode usar variáveis ??globais dentro de um módulo em seu lugar.

Assim, quando é bom / mau usar um singleton? Muito bonito exatamente quando seria bom / ruim para usar uma variável global.

Projeto ++ Modern C por Alexandrescu tem um singleton genérico inheritable thread-safe.

Para o meu 2p-estima, eu acho que é importante ter vidas definidas para seus singletons (quando é absolutamente necessário para usá-los). Eu normalmente não deixar o get() estática função instanciar nada, e licença de set-up e destruição para alguma seção dedicada do aplicativo principal. Isso ajuda dependências de destaque entre singletons -. Mas, como salientou acima, é melhor apenas evitá-los se possível

  • Como você implementar um Singleton corretamente

Há uma questão que eu nunca vi mencionado, algo que eu corri para em um trabalho anterior. Tivemos singletons C ++ que foram compartilhadas entre DLLs, e os mecanismos habituais de garantir uma única instância de uma classe simplesmente não funcionam. O problema é que cada DLL obtém seu próprio conjunto de variáveis ??estáticas, juntamente com o EXE. Se a sua função get_instance está em linha ou parte de uma biblioteca estática, cada DLL vai acabar com a sua própria cópia do "singleton".

A solução é certificar-se o código singleton só é definido em um DLL ou EXE, ou criar um gerenciador singleton com essas propriedades para parcelar casos.

O primeiro exemplo não é thread-safe - se dois segmentos chamar getInstance, ao mesmo tempo, que estático vai ser um PITA. Alguma forma de exclusão mútua ajudaria.

Como já foi notado, as principais desvantagens para únicos incluem a incapacidade para estender-los, e a perda do poder de instanciar uma instância mais do que, por exemplo, Para fins de teste.

Alguns aspectos úteis de singletons:

  1. preguiçoso ou adiantado instanciação
  2. útil para um objeto que requer instalação e / ou estado

No entanto, você não tem que usar um singleton para obter esses benefícios. Você pode escrever um objeto normal que faz o trabalho, e, em seguida, ter pessoas acessá-lo através de uma fábrica (um objeto separado). A fábrica pode se preocupar com única instanciar um, e reutilizá-lo, etc., se for necessário. Além disso, se você programar para uma interface em vez de uma classe concreta, a fábrica pode usar estratégias, ou seja, você pode alternar dentro e fora várias implementações da interface.

Finalmente, uma fábrica presta-se a tecnologias de injeção de dependência como Spring etc.

Singletons são úteis quando você tem um código de lote ser execução quando você inicializar e objeto. Por exemplo, quando você está usando iBatis quando você configurar um objeto persistência que tem que ler todas as configurações, analisar os mapas, certifique-se todos os seus correta, etc .. antes de chegar ao seu código.

Se você fez isso o tempo todo, o desempenho seria muito degradado. Usá-lo em um singleton, você tomar essa batida uma vez e, em seguida, todas as chamadas subsequentes não tem que fazê-lo.

A verdadeira queda de Singletons é que eles quebram herança. Você não pode derivar uma nova classe para lhe dar funcionalidade estendida menos que você tenha acesso ao código onde o Singleton é referenciado. Assim, além do fato de o o Singleton fará seu código fortemente acoplados (solucionáveis ??por um padrão de estratégia ... ou Dependency Injection) que também irá impedi-lo de fechar seções do código de revisão (bibliotecas compartilhadas).

Assim, até mesmo os exemplos de madeireiros ou pools de threads são inválidos e deve ser substituído por Strategies.

A maioria das pessoas usar singletons quando eles estão tentando fazer-se sentir bem sobre o uso de uma variável global. Há usos legítimos, mas na maioria das vezes, quando as pessoas usá-los, o fato de que só pode haver uma instância é apenas um fato trivial em comparação com o fato de que é globalmente acessível.

Como um Singleton permite apenas uma instância a ser criada de forma eficaz a replicação exemplo controlos. por exemplo, você não precisaria de várias instâncias de uma pesquisa - um mapa morse pesquisa por exemplo, envolvendo-a, assim, em uma única classe é apt. E só porque você tem uma única instância da classe não significa que você também são limitados no número de referências a essa instância. Você pode fila chamadas (para evitar problemas de threading) para a instância e mudanças necessárias efeito. Sim, a forma geral de um singleton é uma globalmente público, certamente você pode modificar o design para criar um acesso restrito singleton mais. Eu não tenho cansado isso antes, mas eu com certeza sei que é possível. E a todos aqueles que comentou dizendo que o padrão Singleton é totalmente mal você deve saber o seguinte: sim, é o mal se você não usá-lo corretamente ou dentro limita a funcionalidade eficaz e comportamento previsível:. Não generalize

Mas quando eu preciso de algo como um Singleton, muitas vezes acabam usando um Schwarz Contador para instanciá-lo.

Eu uso Singletons como um teste entrevista.

Quando eu pedir a um desenvolvedor para citar alguns padrões de design, se todos eles podem nomear é Singleton, eles não contratado.

A seguir é a melhor abordagem para a implementação de um padrão singleton thread-safe com desalocá a memória em si destructor. Mas eu acho que o destruidor deve ser um opcional porque instância singleton será destruído automaticamente quando o termina do programa:

#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 únicas pode ser- Se quisermos manter o estado da instância ao longo da execução do programa Se estamos envolvidos em escrever para o log de execução de uma aplicação onde apenas uma instância da necessidade arquivo a ser usado .... e assim por diante. Será apreciável se alguém pode sugerir otimização no meu código acima.

Anti-Uso:

Um grande problema com o uso excessivo Singleton é que o padrão evita que a extensão fácil e troca de implementações alternativas. O nome da classe é codificado duro onde quer que o singleton é usado.

Eu acho que esta é a versão mais robusto 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á o versão .NET otimizado :

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 este padrão em dotfactory.com .

Os Meyers únicas padrão funciona bem o suficiente na maioria das vezes, e nas ocasiões em que isso não significa necessariamente pagar a olhar para uma melhor nada. Enquanto o construtor nunca vai jogar e não há dependências entre singletons.

Um singleton é uma implementação de um objeto globalmente acessível (GAO de agora em diante), embora nem todos os gaos são singletons.

Os madeireiros próprios não deve ser singletons mas os meios para log deve idealmente ser globalmente acessível, para desacoplar onde a mensagem log está sendo gerado a partir de onde ou como ele é registrado.

preguiçoso-loading / avaliação preguiçosa é um conceito diferente e singleton geralmente implementa isso também. Ele vem com um monte de seus próprios problemas, em particular thread-segurança e problemas se ele falhar com exceções, que o que parecia ser uma boa idéia no momento acaba por não ser tão grande, afinal. (Um pouco como a implementação vaca em cordas).

Com isso em mente, GOAS pode ser inicializado como este:

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 como forma grosseira como aquela, e claramente em uma biblioteca carregada que contém objetos que você provavelmente vai querer algum outro mecanismo para gerir a sua vida. (Coloque-as em um objeto que você começa quando você carregar a biblioteca).

Como para quando eu uso singletons? Eu usei-os para 2 coisas - Um singleton tabela que indica que as bibliotecas tenham sido carregados com dlopen - Um manipulador mensagem que loggers pode assinar e que você pode enviar mensagens para. Necessário especificamente para os manipuladores de sinal.

Eu ainda não entendo por que um singleton tem que ser global.

Eu estava indo para produzir um singleton, onde eu escondi um banco de dados dentro da classe como uma variável constante estática privada e aproveite funções de classe que utilizam o banco de dados, sem nunca expor o banco de dados para o usuário.

Eu não vejo por que essa funcionalidade seria ruim.

Eu encontrá-los úteis quando eu tenho uma classe que encapsula uma grande quantidade de memória. Por exemplo, em um jogo recente que eu tenho trabalhado em Eu tenho uma classe mapa influência que contém uma coleção de matrizes muito grandes de memória contígua. Eu quero que todos alocados na inicialização, todos libertados no desligamento e eu definitivamente quero apenas uma cópia do mesmo. Eu também tenho que acessá-lo a partir de muitos lugares. I encontrar o padrão Singleton para ser muito útil neste caso.

Estou certo de que há outras soluções, mas eu encontrar um presente muito útil e fácil de implementar.

Se você é a pessoa que criou o singleton e que usa-lo, não faça isso como Singleton (que não tem sentido, porque você pode controlar a singularidade do objeto sem torná-lo Singleton), mas faz sentido quando um desenvolvedor de uma biblioteca e que pretende fornecer apenas um objeto para seus usuários (neste caso, você é o que criou o singleton, mas você não é o usuário).

Singletons são objetos para usá-los como objetos, muitas pessoas acessa a singletons diretamente através de chamar o método que retorna-lo, mas isso é prejudicial, porque você está fazendo o seu código sabe que objeto é Singleton, eu prefiro usar singletons como objetos, Eu passá-los através do construtor e eu usá-los como objetos comuns, por essa forma, seu código não sei se esses objetos são singletons ou não, e que faz com que as dependências mais clara e isso ajuda um pouco para refatoração ...

Em aplicativos de desktop (eu sei, só nos dinossauros escrever estas mais!) Que são essenciais para a obtenção de configurações relativamente imutáveis ??globais de aplicação - o idioma do usuário, caminho para arquivos de ajuda, preferências do usuário etc., que de outra forma teria para propagar em todas as classes e cada diálogo.

Editar - é claro que essas devem ser somente leitura

Outra implementação

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

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top