Singleton Pattern no C#

De acordo como Wikipedia O Singleton Pattern é um padrão de design de software que restringe a instanciação de uma classe a uma instância “única”. Isso é útil quando exatamente um objeto é necessário para coordenar ações em todo o sistema.

Uma curiosidade é que em matemática, um Singleton, também conhecido como conjunto de unidades, é um conjunto com exatamente um elemento. Por exemplo, o conjunto {null} é um Singleton contendo o elemento null.

Usabilidade

Em C#, você não pode definir variáveis globais verdadeiras (no sentido de que elas não pertencem a nenhuma classe).

Dito isso, a abordagem mais simples para imitar esse recurso consiste em usar uma classe estática, da seguinte maneira

public static class Globals
{
    public const Int32 BUFFER_SIZE = 512; // Unmodifiable
    public static String FILE_NAME = "Output.txt"; // Modifiable
    public static readonly String CODE_PREFIX = "US-"; // Unmodifiable
}

Mas se estamos falando instâncias de objetos temos que tomar cuidado para garantir que teremos somente uma, por todo o ciclo de vida da aplicação. Uma maneira de garantir a unidade é, depois de instanciar uma classe, você faz uma nota mental para você mesmo não para criar outra instância, e o problema é resolvido certo? Errado.

Isso seria verdade se existisse somente você como desenvolvedor, mantendo uma pequena aplicação. Mas não é, definitivamente, uma solução viável em projetos maiores, ou projetos com vários desenvolvedores. Neste caso o Singleton é uma ótima opção.

Todas as implementações do Singleton têm estas duas etapas em comum

– Torne o construtor padrão privado, para evitar que outros objetos usem o novo operador com a classe Singleton.

– Crie um método de criação estático que atue como um construtor. Nos bastidores, esse método chama o construtor privado para criar um objeto e o salva em um campo estático. Todas as chamadas a seguir para este método retorna o objeto em cache.

Como funciona

Vamos imaginar que existe uma classe com as informações de navegação do usuário pela aplicação. Esta classe é usada em diversas partes do sistema e contem informações que são alteradas enquanto o usuário está usando a aplicação. Esta classe tem o nome de UserNavigationInformation.

Para garantir que teremos somente uma instância, vamos criar um método que gera uma nova instância caso ainda não exista uma criada.

using System;
namespace SingletonPattern
{
    class UserNavigationInformation
    {
        private static UserNavigationInformation _uniqueInstace = null;

        public static UserNavigationInformation GetInstance()
        {
            if(_uniqueInstace is null)
            {
                _uniqueInstace = new UserNavigationInformation();
            }
            return _uniqueInstace;
        }
    }
}

Agora todas as vezes que chamamos o GetInstance() teremos sempre a mesma instancia. Porem ainda existe um problema com esta classe, não existe um bloqueio para criação de novas instâncias.

Para evitar que algum desenvolvedor chame “new UserNavigationInformation()” temos que garantir que o construtor da classe seja privado, ou seja, só pode ser chamado de dentro da própria classe.

using System;

namespace SingletonPattern
{
    class UserNavigationInformation
    {
        private UserNavigationInformation() { }

        private static UserNavigationInformation _uniqueInstace = null;

        public static UserNavigationInformation GetInstance()
        {
            if(_uniqueInstace is null)
            {
                _uniqueInstace = new UserNavigationInformation();
            }
            return _uniqueInstace;
        }
    }
}

Com esta correção garantimos que não existem duas ou mais maneiras de gerar instâncias de UserNavigationInformation além da chamada estática do método GetInstance();.

Agora se tentarmos gerar uma nova instância teremos o seguinte erro. "error CS0122: "UserNavigationInformation.UserNavigationInformation()" é inacessível devido ao seu nível de proteção"

Outra maneira de implementação

// Reference: Wikipedia
public sealed class Singleton
{
    public static Singleton Instance { get; } = new Singleton();

    private Singleton() { }
}

Conclusão

Então, você pode ver variações de como Singleton é realizado, mas seu objetivo é sempre o mesmo, fornecer acesso global a uma classe restrita a uma instância. Em geral, isso é conseguido por ter um construtor privado com um método público que instância a classe, se ela ainda não estiver instanciada.

Qualquer dúvida ou dicas, entre em contato: leandrolt@gmail.com

Principais referências

– https://refactoring.guru/design-patterns/singleton
– https://stackoverflow.com/questions/14368129/how-to-use-global-variables-in-c
– https://www.arclab.com/en/kb/csharp/global-variables-fields-functions-static-class.html
– https://en.wikipedia.org/wiki/Singleton_pattern

Leave a Reply

Your email address will not be published. Required fields are marked *