Eu li opiniões diferentes sobre o padrão singleton. Alguns sustentam que deve ser evitado a todo custo e outros que podem ser úteis em determinadas situações.
Uma situação em que eu uso singletons é quando eu preciso de uma factory (digamos, um objeto f do tipo F) para criar objetos de uma determinada classe A. A factory é criada uma vez usando alguns parâmetros de configuração e é usada sempre que um objeto de o tipo A é instanciado. Portanto, toda parte do código que deseja instanciar A busca o singleton f e cria a nova instância, por exemplo
F& f = F::instance();
boost::shared_ptr<A> a = f.createA();
Então, o meu cenário geral é que
- Preciso de apenas uma instância de uma classe por motivos de otimização (não preciso de vários objetos de fábrica) ou para compartilhar o estado comum (por exemplo, a fábrica sabe quantas instâncias de A ainda podem ser criadas)
- Eu preciso de uma maneira de ter acesso a esta instância f de F em diferentes locais do código.
Não estou interessado em discutir se esse padrão é bom ou ruim, mas supondo que eu queira evitar o uso de um singleton, que outro padrão posso usar?
As idéias que tive foram: (1) obter o objeto de fábrica de um registro ou (2) criar a fábrica em algum momento durante a inicialização do programa e depois passar a fábrica como um parâmetro.
Na solução (1), o próprio registro é um singleton, então acabei de mudar o problema de não usar um singleton da fábrica para o registro.
No caso (2), preciso de alguma fonte inicial (objeto) da qual o objeto factory vem, por isso tenho medo de voltar a usar outro singleton (o objeto que fornece minha instância de fábrica). Seguindo essa cadeia de singletons, talvez eu possa reduzir o problema a um singleton (o aplicativo inteiro) pelo qual todos os outros singletons são gerenciados direta ou indiretamente.
Essa última opção (usando um singleton inicial que cria todos os outros objetos exclusivos e injeta todos os outros singletons nos lugares certos) seria uma solução aceitável? Essa é a solução sugerida implicitamente quando alguém aconselha não usar singletons ou quais são outras soluções, por exemplo, no exemplo ilustrado acima?
EDITAR
Como acho que o ponto da minha pergunta foi mal interpretado por alguns, aqui estão mais algumas informações. Conforme explicado aqui , por exemplo , a palavra singleton pode indicar (a) uma classe com um objeto de instância única e (b) um padrão de design usado para criar e acessar esse objeto.
Para tornar as coisas mais claras, vamos usar o termo objeto único para (a) e padrão singleton para (b). Então, eu sei o que são o padrão singleton e a injeção de dependência (BTW, ultimamente tenho usado DI intensamente para remover instâncias do padrão singleton de algum código no qual estou trabalhando).
O que quero dizer é que, a menos que o gráfico inteiro de objetos seja instanciado a partir de um único objeto vivendo na pilha do método principal, sempre haverá a necessidade de acessar alguns objetos exclusivos através do padrão singleton.
Minha pergunta é se a criação e a fiação completas de gráficos de objetos dependem do método principal (por exemplo, através de uma poderosa estrutura DI que não usa o próprio padrão) é a única solução livre de padrão singleton .
Respostas:
Sua segunda opção é um bom caminho a percorrer - é um tipo de injeção de dependência, que é o padrão usado para compartilhar o estado em seu programa quando você deseja evitar singletons e variáveis globais.
Você não pode contornar o fato de que algo precisa criar sua fábrica. Se esse é o aplicativo, que assim seja. O ponto importante é que sua fábrica não deve se importar com o objeto que o criou e os objetos que recebem a fábrica não devem depender de onde a fábrica veio. Não faça com que seus objetos obtenham um ponteiro para o aplicativo singleton e solicitem a fábrica; faça com que seu aplicativo crie a fábrica e dê-o aos objetos que precisarão dele.
fonte
O objetivo de um singleton é impor que apenas uma instância possa existir em um determinado domínio. Isso significa que um singleton é útil se você tiver motivos fortes para impor o comportamento de singleton; na prática, esse raramente é o caso, e o multiprocessamento e o multiencadeamento certamente obscurecem o significado de 'exclusivo' - é uma instância por máquina, por processo, por encadeamento, por solicitação? E sua implementação de singleton cuida das condições da corrida?
Em vez de singleton, prefiro usar um dos seguintes:
A razão é que um singleton é um estado global disfarçado, o que significa que ele apresenta um alto grau de acoplamento em todo o aplicativo - qualquer parte do seu código pode pegar a instância singleton de qualquer lugar com o mínimo esforço. Se você usar objetos locais ou passar instâncias, terá muito mais controle e poderá manter seus escopos pequenos e suas dependências estreitas.
fonte
A maioria das pessoas (incluindo você) não entende completamente qual é o padrão Singleton. O padrão Singleton significa apenas que existe uma instância de uma classe e existe algum mecanismo para o código em todo o aplicativo obter uma referência a essa instância.
No livro do GoF, o método estático de fábrica que retorna uma referência a um campo estático era apenas um exemplo de como esse mecanismo seria e um com graves inconvenientes. Infelizmente, todos e seus cães se prenderam a esse mecanismo e pensaram que era disso que Singleton se tratava.
As alternativas que você cita também são singletons, apenas com um mecanismo diferente para obter a referência. (2) resulta claramente em muita repercussão, a menos que você precise da referência em apenas alguns lugares próximos à raiz da pilha de chamadas. (1) soa como uma estrutura de injeção de dependência bruta - então por que não usar uma?
A vantagem é que as estruturas DI existentes são flexíveis, poderosas e bem testadas. Eles podem fazer muito mais do que apenas gerenciar Singletons. No entanto, para usar plenamente seus recursos, eles funcionam melhor se você estruturar seu aplicativo de uma certa maneira, o que nem sempre é possível: idealmente, existem objetos centrais que são adquiridos através da estrutura DI e têm todas as suas dependências preenchidas transitivamente e são então executado.
Edit: Em última análise, tudo depende do método principal de qualquer maneira. Mas você está certo: a única maneira de evitar o uso de global / estático completamente é configurando tudo a partir do método principal. Observe que o DI é mais popular em ambientes de servidor em que o método principal é opaco e configura o servidor e o código do aplicativo consiste basicamente em retornos de chamada que são instanciados e chamados pelo código do servidor. Mas a maioria das estruturas de DI também permite acesso direto ao registro central, por exemplo, ApplicationContext da Spring, para "casos especiais".
Então, basicamente, a melhor coisa que as pessoas surgiram até agora é uma combinação inteligente das duas alternativas que você mencionou.
fonte
Existem algumas alternativas:
Injeção de dependência
Todo objeto tem suas dependências transmitidas a ele quando foi criado. Normalmente, uma estrutura ou um pequeno número de classes de fábrica são responsáveis por criar e conectar os objetos
Registro de Serviço
Cada objeto recebe um objeto de registro de serviço. Ele fornece métodos para solicitar vários objetos diferentes que fornecem serviços diferentes. A estrutura do Android usa esse padrão
Root Manager
Há um único objeto que é a raiz do gráfico de objetos. Seguindo os links desse objeto, qualquer outro objeto pode ser encontrado. O código tende a se parecer com:
fonte
Singleton::instance
em qualquer lugar no código do cliente.Se suas necessidades do singleton podem ser reduzidas a uma única função, por que não usar apenas uma simples função de fábrica? Funções globais (provavelmente um método estático da classe F no seu exemplo) são inerentemente singletons, com exclusividade imposta pelo compilador e vinculador.
É certo que isso ocorre quando a operação do singleton não pode ser reduzida a uma única chamada de função, embora talvez um pequeno conjunto de funções relacionadas possa ajudá-lo.
Tudo isso dito, os itens 1 e 2 da sua pergunta deixam claro que você realmente quer apenas algo. Dependendo da sua definição do padrão singleton, você já o está usando ou está muito próximo. Eu não acho que você possa ter um de algo sem que seja um singleton ou pelo menos muito próximo de um. É muito perto do significado de singleton.
Como você sugere, em algum momento você precisa ter um de algo, talvez o problema não esteja em ter uma única instância de algo, mas em tomar medidas para evitar (ou pelo menos desencorajar ou minimizar) o abuso. Mover o máximo de estado para fora do "singleton" e para os parâmetros possível é um bom começo. A restrição da interface ao singleton também ajuda, pois há menos oportunidades de abuso dessa maneira. Às vezes, você apenas precisa absorver e tornar o singleton muito robusto, como o heap ou o sistema de arquivos.
fonte
Se você estiver usando uma linguagem multiparadigmática como C ++ ou Python, uma alternativa a uma classe singleton é um conjunto de funções / variáveis envolvidas em um espaço para nome.
Conceitualmente, um arquivo C ++ com variáveis globais livres, funções globais livres e variáveis estáticas usadas para ocultar informações, todas agrupadas em um espaço para nome, oferece quase o mesmo efeito que uma "classe" singleton.
Ele só quebra se você deseja herança. Eu já vi muitos singletons que seriam melhores assim.
fonte
Encapsulamento de singletons
Cenário do caso. Seu aplicativo é orientado a objetos e requer 3 ou 4 singletons específicos, mesmo se você tiver mais aulas.
Antes do exemplo (C ++ como pseudocódigo):
Uma maneira é remover parcialmente alguns singletons (globais), encapsulando todos eles em um singleton único, que tem como membros os outros singletons.
Dessa forma, em vez de "vários singletons, abordagem versus versus nenhum singleton, abordagem", temos o "encapsular todos os singletons em uma abordagem única".
Após o exemplo (C ++ como pseudocódigo):
Observe que os exemplos são mais parecidos com pseudocódigo e ignora pequenos bugs ou erros de sintaxe e considere a solução para a pergunta.
Também há outra coisa a considerar, porque a linguagem de programação usada pode afetar como implementar sua implementação singleton ou não singleton.
Felicidades.
fonte
Seus requisitos originais:
Não alinhe com a definição de um singleton (e com o que você se refere posteriormente). Do GoF (minha versão é 1995) página 127:
Se você precisar apenas de uma instância, isso não impede você de fazer mais.
Se você deseja uma única instância globalmente acessível, crie uma única instância globalmente acessível . Não há necessidade de ter um padrão nomeado para tudo o que você faz. E sim, instâncias únicas acessíveis globalmente geralmente são ruins. Dar-lhes um nome não os torna menos ruins .
Para evitar a acessibilidade global, as abordagens comuns "criar uma instância e transmiti-la" ou "fazer com que o proprietário de dois objetos os cole" são bem-vindas.
fonte
Que tal usar o contêiner IoC? Com algum pensamento, você pode acabar com algo nas linhas de:
Você precisaria especificar qual implementação
IFoo
usar em uma inicialização, mas essa é uma configuração única que pode ser substituída facilmente mais tarde. A vida útil de uma instância resolvida pode ser normalmente controlada por um contêiner de IoC.O método estático de singleton void pode ser substituído por:
O método estático getter singleton pode ser substituído por:
O exemplo é relevante para o .NET, mas tenho certeza de que algo semelhante pode ser alcançado em outros idiomas.
fonte