Continuando com a série Design Patterns vamos falar sobre Singleton.

Antes de começar a despejar um monte de informação, vamos criar um cenário...

Você é um programador solo, que cria o projeto, trabalha sozinho em todos os passos do mesmo? Ou você trabalha com uma equipe com vários programadores e cada um cria uma parte do projeto visando um todo para chegar a um único resultado? Em ambos os caso o singleton será útil, no primeiro vai resolver os casos dos programadores que esquecem até mesmo os próprios conceitos utilizados na implementação de uma classe, e no segundo vai garantir que ninguém vai criar mais de um objeto da mesma classe, vamos ver por quê?

Para alegria geral na nação, o padrão singleton é o mais simples em termos de seu diagrama de classe, na verdade o diagrama só contém uma classe.

Digamos que temos uma classe qualquer que chamaremos de Singleton e queremos que em toda a aplicação só exista uma única instância dessa mesma classe, e que você tenha que garantir que isso aconteça, no caso de ser um programador solo, de que você não se esqueça disso e no caso de uma equipe, que ninguém viole essa regra, bem com o padrão de projeto Singleton garantiremos uma única instância para a classe em questão.

Vamos analisar uma criação de uma classe singleton clássica não thread safe.


    public class Singleton
    {
    private static Singleton _unicaInstancia;
    public int _valor { get; set; }
    // Outras variáveis e propriedade aqui
    // Contrutor privado
    private Singleton() { }
    public static Singleton getInstance()
    {
    if (_unicaInstancia == null)
    {
    _unicaInstancia = new Singleton();
    }
    return _unicaInstancia;
    }
    public void ExibirValor()
    {
    Console.WriteLine(_valor);
    }
    // Outros metodos aqui
    }
    

Observe que o construtor é privado não permitindo a criação da classe de fora dela, e que temos um método getInstance que tem é estática e é esse mesmo método que verifica se o objeto _unicaInstancia que também deve ser estática já foi instanciado ou se é necessario criar a instancia e retorna o mesmo para a invocação do método, vamos ver um exemplo utilizando essa classe.


    using System;
    class SingletonClient
    {
    static void Main(string[] args)
    {
    Singleton singCliente1 = Singleton.getInstance();
    singCliente1._valor = 10;
    Singleton singCliente2 = Singleton.getInstance();
    singCliente2.ExibirValor();
    singCliente2._valor = 20;
    singCliente1.ExibirValor();
    Console.ReadKey();
    }
    }
    

Vamos observar as linhas das criações dos objetos singCliente1 e singCliente2, veja que não utilizamos o new para criações das classes e sim solicitamos ao método getInstance da classe a única instancia dela. Setamos um valor para a propriedade _valor do singCliente1 e somente depois criamos o objeto singCliente2 e invocamos o método ExibirValor do objeto singCliente2, em seguida setamos um valor para a propriedade _valor do singCliente2 e invocamos o método ExibirValor do objeto singCliente1.

O resultado na tela do console vai ser:

Imagem com Resultado
Resultado.

Por que isso ocorreu se setamos o valor 10 no objeto singCliente1 e não setamos nada no singCliente2 e ele exibiu o valor do objeto singCliente1?

Bem, ocorre que ambos os objetos é uma referência o objeto estático que está dentro da classe, esse objeto é o único objeto instanciado em todo o projeto, o que foi feito, foi criar um ponto global de acesso a esse objeto.

Como foi dito a solução acima não vai ter o resultado esperado quando utilizado em MultiThreading, mais não se preocupe, vamos ver uma outra solução bem parecida com essa, na verdade algumas alterações feitas somente e já teremos uma classe singletam thread safe que poderá ser usada em varias threads.


    using System;
    using System.Threading;
    public class Singleton
    {
    // Responsável pela criação do objeto singleton
    class CriaInstancia
    {
    internal static readonly Singleton instancia = new Singleton();
    }
    public int _valor { get; set; }
    private Singleton() { }
    public static Singleton getInstance()
    {
    return CriaInstancia.instancia;
    }
    public void ExibirValor()
    {
    Console.WriteLine(_valor);
    }
    }
    class Program
    {
    static void Main(string[] args)
    {
    Singleton singCliente1 = Singleton.getInstance();
    singCliente1._valor = 10;
    //Nova thread
    Thread teste = new Thread(TesteThread);
    teste.Start();
    teste.Join();
    //Final thread
    Singleton singCliente2 = Singleton.getInstance();
    singCliente2.ExibirValor();
    singCliente2._valor = 20;
    singCliente1.ExibirValor();
    Console.ReadKey();
    }
    private static void TesteThread()
    {
    Console.WriteLine("Inicio Thread");
    Singleton singCliente3 = Singleton.getInstance();
    singCliente3.ExibirValor();
    singCliente3._valor = 100;
    Console.WriteLine("Fim Thread");
    }
    }
    
Resultado
Close do código

Foi adicionado na classe Singleton a classe CriaInstancia que agora contém a única instancia da classe Singleton com os modificadores internal, static e readonly permitindo assim que em qualquer thread seja utilizado o mesmo objeto.


    / Responsável pela criação do objeto singleton
    class CriaInstancia
    {
    internal static readonly Singleton instancia = new Singleton();
    }
    

A classe Singleton não tem mais o objeto statico da sua classe que servia para instanciar o objeto caso fosse a primeira solicitação. Com estas simples alterações garantiremos que em todas as threds criadas no sistema vamos ter o resultado esperado, uma única instância do objeto.

Caso você queira um construtor para o CriaInstancia você deverá cria-lo com o modificador static ficando desta forma:


    class CriaInstancia
    {
    internal static readonly Singleton instancia = new Singleton();
    static CriaInstancia()
    {
    instancia._valor = 200;
    }
    }
    

Finalizamos mais este artigo, espero ter alcançado meus objetivos que erá passar um pouco do conceito do design pattern singleton de forma clara e com um facil entendimento.

Estarei falando sobre outros padrões nos proximos artigos.

Fontes:

FREEMAN, ERIC & FREEMAN, ELISABETH – Use a Cabeça! Padrões de Projetos (Design Patterns), 2ª Edição