A variável global glorificada - se torna uma classe global glorificada. Alguns dizem que quebram o design orientado a objetos.
Dê-me cenários, além do bom e antigo logger, onde faz sentido usar o singleton.
A variável global glorificada - se torna uma classe global glorificada. Alguns dizem que quebram o design orientado a objetos.
Dê-me cenários, além do bom e antigo logger, onde faz sentido usar o singleton.
Respostas:
Na minha busca pela verdade, descobri que existem realmente muito poucas razões "aceitáveis" para usar um Singleton.
Um motivo que tende a aparecer repetidamente nas internets é o de uma classe de "registro" (que você mencionou). Nesse caso, um Singleton pode ser usado em vez de uma única instância de uma classe, porque uma classe de log geralmente precisa ser usada repetidamente e repetidamente por todas as classes de um projeto. Se toda classe usa essa classe de log, a injeção de dependência se torna complicada.
O log é um exemplo específico de um Singleton "aceitável" porque não afeta a execução do seu código. Desabilitar o log, a execução do código permanece a mesma. Habilite o mesmo. Misko coloca da seguinte maneira em Causa raiz dos singletons : "As informações aqui fluem de uma maneira: do seu aplicativo para o criador de logs. Mesmo que os registradores sejam um estado global, já que nenhuma informação flui dos criadores de logs para o seu aplicativo, os registradores são aceitáveis".
Tenho certeza de que existem outros motivos válidos também. Alex Miller, em " Patterns I Hate ", fala sobre localizadores de serviços e interfaces de usuário do lado do cliente, sendo possivelmente opções "aceitáveis".
Leia mais em Singleton, eu te amo, mas você está me derrubando.
Um candidato Singleton deve atender a três requisitos:
Se o seu Singleton proposto tiver apenas um ou dois desses requisitos, um novo design é quase sempre a opção correta.
Por exemplo, é improvável que um spooler de impressora seja chamado de mais de um local (o menu Imprimir), para que você possa usar mutexes para resolver o problema de acesso simultâneo.
Um logger simples é o exemplo mais óbvio de um Singleton possivelmente válido, mas isso pode mudar com esquemas de log mais complexos.
Lendo arquivos de configuração que devem ser lidos apenas no momento da inicialização e encapsulando-os em um Singleton.
Properties.Settings.Default
.NET.
Você usa um singleton quando precisa gerenciar um recurso compartilhado. Por exemplo, um spooler de impressora. Seu aplicativo deve ter apenas uma instância do spooler para evitar solicitações conflitantes para o mesmo recurso.
Ou uma conexão com o banco de dados ou um gerenciador de arquivos etc.
Os singletons somente leitura que armazenam algum estado global (idioma do usuário, caminho do arquivo de ajuda, caminho do aplicativo) são razoáveis. Tenha cuidado ao usar singletons para controlar a lógica de negócios - o single quase sempre acaba sendo múltiplo
Gerenciando uma conexão (ou um conjunto de conexões) com um banco de dados.
Eu o usaria também para recuperar e armazenar informações em arquivos de configuração externos.
Uma das maneiras de usar um singleton é cobrir uma instância em que deve haver um único "intermediário" controlando o acesso a um recurso. Singletons são bons em madeireiros, porque intermediam o acesso a, digamos, um arquivo, no qual só pode ser gravado exclusivamente. Para algo como log, eles fornecem uma maneira de abstrair as gravações para algo como um arquivo de log - você pode agrupar um mecanismo de cache no seu singleton, etc ...
Pense também em uma situação em que você tem um aplicativo com muitas janelas / threads / etc, mas que precisa de um único ponto de comunicação. Uma vez eu usei um para controlar trabalhos que eu queria que meu aplicativo iniciasse. O singleton era responsável por serializar os trabalhos e exibir seu status para qualquer outra parte do programa que estivesse interessada. Nesse tipo de cenário, você pode ver um singleton como uma classe de "servidor" em execução no aplicativo ... HTH
Um singleton deve ser usado ao gerenciar o acesso a um recurso compartilhado por todo o aplicativo, e seria destrutivo potencialmente ter várias instâncias da mesma classe. Garantir que o acesso a recursos compartilhados seja seguro é um exemplo muito bom de onde esse tipo de padrão pode ser vital.
Ao usar Singletons, verifique se não oculta acidentalmente dependências. Idealmente, os singletons (como a maioria das variáveis estáticas em um aplicativo) são configurados durante a execução do código de inicialização do aplicativo (static void Main () para executáveis em C #, static void main () para executáveis em java) e depois transmitidos para todas as outras classes instanciadas que requerem. Isso ajuda a manter a testabilidade.
Um exemplo prático de um singleton pode ser encontrado em Test :: Builder , a classe que suporta quase todos os módulos de teste Perl modernos. O singleton Test :: Builder armazena e intermedia o estado e o histórico do processo de teste (resultados históricos do teste, conta o número de testes executados), bem como coisas como para onde está indo a saída do teste. Tudo isso é necessário para coordenar vários módulos de teste, escritos por autores diferentes, para trabalharem juntos em um único script de teste.
A história do singleton do Test :: Builder é educacional. Ligar new()
sempre oferece o mesmo objeto. Primeiro, todos os dados foram armazenados como variáveis de classe sem nada no próprio objeto. Isso funcionou até que eu quisesse testar o Test :: Builder por conta própria. Então, eu precisava de dois objetos Test :: Builder, um configurado como fictício, para capturar e testar seu comportamento e saída, e outro para ser o objeto de teste real. Nesse ponto, o Test :: Builder foi refatorado para um objeto real. O objeto singleton era armazenado como dados de classe e new()
sempre o retornava. create()
foi adicionado para criar um objeto novo e permitir o teste.
Atualmente, os usuários desejam alterar alguns comportamentos do Test :: Builder em seu próprio módulo, mas deixam outros em paz, enquanto o histórico do teste permanece em comum em todos os módulos de teste. O que está acontecendo agora é que o objeto monolítico Test :: Builder está sendo dividido em partes menores (histórico, saída, formato ...) com uma instância Test :: Builder reunindo-os. Agora o Test :: Builder não precisa mais ser um singleton. Seus componentes, como a história, podem ser. Isso empurra a necessidade inflexível de um singleton para um nível abaixo. Dá mais flexibilidade ao usuário para misturar e combinar peças. Os objetos singleton menores agora podem apenas armazenar dados, com seus objetos contidos decidindo como usá-los. Ele ainda permite que uma classe que não seja Test :: Builder seja reproduzida usando o histórico Test :: Builder e os singletons de saída.
Parece haver uma pressão entre a coordenação de dados e a flexibilidade de comportamento, que pode ser atenuada colocando o singleton em torno de apenas dados compartilhados com a menor quantidade de comportamento possível para garantir a integridade dos dados.
Quando você carrega um objeto Propriedades de configuração, do banco de dados ou de um arquivo, ajuda a tê-lo como um singleton; não há razão para continuar relendo os dados estáticos que não serão alterados enquanto o servidor estiver em execução.
Você pode usar Singleton ao implementar o padrão State (da maneira mostrada no livro GoF). Isso ocorre porque as classes State concretas não têm um estado próprio e executam suas ações em termos de uma classe de contexto.
Você também pode fazer da Abstract Factory um singleton.
setState()
responsabilizar-se por decidir a política de criação do estado. Ajuda se a sua linguagem de programação suportar modelos ou genéricos. Em vez de Singleton, você poderia usar o padrão Monostate , em que instanciar um objeto de estado acaba reutilizando o mesmo objeto de estado global / estático. A sintaxe para alterar o estado pode permanecer inalterada, pois seus usuários não precisam estar cientes de que o estado instanciado é um Monoestado.
Recursos compartilhados. Especialmente em PHP, uma classe de banco de dados, uma classe de modelo e uma classe de depósito de variável global. Todos precisam ser compartilhados por todos os módulos / classes que estão sendo usados em todo o código.
É um verdadeiro uso de objeto -> a classe de modelo contém o modelo de página que está sendo construído e é modelado, adicionado e alterado pelos módulos que estão sendo adicionados à saída da página. Ele deve ser mantido como uma instância única para que isso possa acontecer, e o mesmo vale para os bancos de dados. Com um singleton de banco de dados compartilhado, todas as classes dos módulos podem ter acesso a consultas e obtê-las sem ter que executá-las novamente.
Um singleton de depósito de variável global fornece um depósito de variável global, confiável e facilmente utilizável. Ele organiza bastante o seu código. Imagine ter todos os valores de configuração em uma matriz em um singleton como:
$gb->config['hostname']
ou ter todos os valores de idioma em uma matriz como:
$gb->lang['ENTER_USER']
No final da execução do código da página, você obtém, digamos, um agora maduro:
$template
Singleton, um $gb
singleton que possui a matriz lang para sua substituição e toda a saída carregada e pronta. Você apenas substitui-os pelas chaves que agora estão presentes no valor da página do objeto de modelo maduro e, em seguida, envia ao usuário.
A grande vantagem disso é que você pode fazer QUALQUER pós-processamento que desejar em qualquer coisa. Você pode canalizar todos os valores de idioma para o google translate ou outro serviço de tradução, recuperá-los e substituí-los em seus lugares, traduzidos, por exemplo. ou, você pode substituir nas estruturas da página ou nas cadeias de conteúdo conforme desejar.
Pode ser muito pragmático configurar preocupações específicas de infraestrutura como singletons ou variáveis globais. Meu exemplo favorito disso são as estruturas de injeção de dependência que usam singletons para atuar como um ponto de conexão com a estrutura.
Nesse caso, você está usando uma dependência da infraestrutura para simplificar o uso da biblioteca e evitar a complexidade desnecessária.
Eu o uso para um objeto que encapsula parâmetros de linha de comando ao lidar com módulos conectáveis. O programa principal não sabe quais são os parâmetros da linha de comando para os módulos que são carregados (e nem sempre sabe quais módulos estão sendo carregados). por exemplo, cargas principais A, que não precisam de nenhum parâmetro em si (então, por que ele deve levar um ponteiro / referência extra / o que seja, não tenho certeza - parece poluição), carrega os módulos X, Y e Z. Dois destes, digamos X e Z, precisam (ou aceitam) parâmetros; portanto, eles retornam ao singleton da linha de comando para informar quais parâmetros devem ser aceitos e, no tempo de execução, eles retornam para descobrir se o usuário realmente especificou algum parâmetro. deles.
De várias maneiras, um singleton para manipular parâmetros CGI funcionaria da mesma forma se você estivesse usando apenas um processo por consulta (outros métodos mod_ * não fazem isso, então seria ruim lá - portanto, o argumento que diz que você não deve ' Não use singletons no mundo mod_cgi para o port_modl ou qualquer outro mundo.
Um exemplo com código, talvez.
Aqui, o ConcreteRegistry é um singleton em um jogo de pôquer que permite que os comportamentos de toda a árvore de pacotes acessem as poucas interfaces principais do jogo (ou seja, as fachadas do modelo, visualização, controlador, ambiente etc.):
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
Ed.
1 - Um comentário sobre a primeira resposta:
Não concordo com uma classe estática de logger. isso pode ser prático para uma implementação, mas não pode ser substituível para testes de unidade. Uma classe estática não pode ser substituída por um teste duplo. Se você não fizer o teste de unidade, não verá o problema aqui.
2 - Tento não criar um singleton manualmente. Acabei de criar um objeto simples com construtores que me permitem injetar colaboradores no objeto. Se eu precisasse de um singleton, usaria uma estrutura de inyection de dependência (Spring.NET, Unity para .NET, Spring para Java) ou alguma outra.
ILogger logger = Logger.SingleInstance();
quando esse método é estático e retorna uma instância armazenada estaticamente de um ILogger. Você usou o exemplo de "uma estrutura de injeção de dependência". Quase todos os contêineres DI são singletons; suas configurações são definidas estaticamente e, finalmente, acessadas de / armazenadas em uma única interface de provedor de serviços.