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>()
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;
}
}
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");
No console veremos:
A mesma instância foi atribuída aos dois objetos
Ó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;
}
}
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);
}
Na saída teremos então:
George
George
Desse modo garantiremos que apenas uma instância será criada, mesmo quando utilizarmos threads para execução de tarefas.
Repositório GIT
Materiais de Estudo
Estamos chegando ao fim do nosso artigo e queria deixar alguns materiais que usei para estudar esse padrão de software:
Higor Diego - Padrão - Singleton
Top comments (0)