Design Patterns – Singleton – Parte 3

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?

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:

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"); } }
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

Artigos relacionados