DEV Community

Cover image for Design Pattern - Singleton
Nicolas de Souza
Nicolas de Souza

Posted on • Originally published at nicolasdesouza.com

Design Pattern - Singleton

O objetivo deste padrão é bem simples, garantir que apenas uma instância de uma determinada classe seja criada.

Simples não?! Após padrões um tanto quanto confusos e complexos, parece até pegadinha mas é fato, realmente é simples. Mas quando isso pode ser aplicado?! Um exemplo comum são as situações de conexões com banco, normalmente não queremos ter N fontes de verdades sobre os dados, o que poderia ocasionar situações de conflito entre essas instâncias.

Não vou me extender neste padrão, ele realmente é simples, deixarei algumas recomendações no final caso queira saber mais!

Ótimo, você já entendeu que o objetivo é criar apenas uma instância, mas como podemos fazer isso?! Algumas linguagens como C# permitem que "marquemos" determinados serviços como singleton quando realizamos a injeção de dependências, então talvez você já tenha visto algo como isso:


.AddSingleton<Interface, Class>()

Enter fullscreen mode Exit fullscreen mode

Mas aqui quero falar um pouco mais sobre a ótica de criarmos uma classe que só possa ter uma instância e não sobre um serviço em si. Pensemos no seguinte, você está construindo um jogo de futebol, neste jogo teremos vários objetos que teremos um ou mais, como jogadores de linha, goleiros, bandeirinhas, mas teremos apenas uma objeto bola, apenas um objeto juíz...

Primeiro vamos criar nossa classe de interesse

public class Ball
{
    private static Ball instance;
    public string ImagePath { get; set; }
    public string Weight { get; set; }
    public double ElasticCoefficient { get; set; }

    private Ball() { }

    public static Ball GetInstance()
    {
        instance ??= new Ball(); // só é instanciado se instace é nula
        return instance;
    }
}

Enter fullscreen mode Exit fullscreen mode

Agora vamos verificar se tudo ocorreu bem

using SingletonFootball;

var b1 = Ball.GetInstance();
var b2 = Ball.GetInstance();

if (b1 == b2)
    Console.WriteLine("A mesma instância foi atribuída aos dois objetos");
else
    Console.WriteLine("Instância diferentes foram atribuídas");
Enter fullscreen mode Exit fullscreen mode

No console veremos:

A mesma instância foi atribuída aos dois objetos
Enter fullscreen mode Exit fullscreen mode

Ótimo, funcionou!!! Mas aqui mora uma pegadinha, se por algum motivo estivermos trabalhando com Threads, teremos a possibilidade de várias rotinas acessarem simultaneamente obtendo várias instância, para corrigir isso precisamos realizar pequenas modificações em nosso código.

Para isso vamos implementar a classe Referee utilizando uma instrução chamada lock. A ideia é que a primeira instância a chave, irá bloquear que outras possíveis instâncias sejam iniciadas.

public class Referee
{
    private static Referee instance;
    public string Name { get; set; }

    private static readonly object _lock = new object();

    private Referee(){ }

    public static Referee GetInstance(string value)
    {
        if (instance == null)
        {
            lock (_lock)
            {
                if (instance == null)
                {
                    instance = new Referee();
                    instance.Name = value;
                }
            }
        }
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora vamos testar o que implementamos

Thread process1 = new Thread(() => {
    TestSingleton("George");
});

Thread process2 = new Thread(() => {
    TestSingleton("Hamilton");
});

process1.Start();
process2.Start();

process1.Join();
process2.Join();

void TestSingleton(string value)
{
    Referee Referee = Referee.GetInstance(value);
    Console.WriteLine(Referee.Name);
}
Enter fullscreen mode Exit fullscreen mode

Na saída teremos então:

George
George
Enter fullscreen mode Exit fullscreen mode

Desse modo garantiremos que apenas uma instância será criada, mesmo quando utilizarmos threads para execução de tarefas.

Repositório GIT

Design Patterns

Materiais de Estudo

Estamos chegando ao fim do nosso artigo e queria deixar alguns materiais que usei para estudar esse padrão de software:

Refactoring Guru

Dofactory

Higor Diego - Padrão - Singleton

Balta - AddTransit, AddScopped, AddSingleton

Um pouco sobre a instrução lock

Top comments (0)