Este post utiliza em seu contexto e exemplos a linguagem de programação C# (CSharp).
O padrão Singleton com certeza é um dos mais conhecidos entre os desenvolvedores. Talvez seja por que ele é relativamente simples, ou, então, por que é muito amado e muito odiado por seus defensores e opositores, respectivamente. Seja como for, é um padrão que tem seu valor e sua utilização dependerá do cenário que você, Arquiteto ou Desenvolvedor, precisa resolver, por isso, não devemos desprezar seu valor.
Singleton, em Inglês, pode ser traduzido como Solteirão, Filho ou Coisa Única. A explicação do pattern começa exatamente por aí.. Em Engenharia de Software, Singleton nada mais é do que uma classe que permite apenas uma única instância.
Deduzo que, por estar lendo este post, você deve ter familiaridade com Programação. Caso nunca tenha visto a implementação de um Singleton, você deve estar se perguntando como é possível para uma Classe garantir que apenas uma instância dela seja criada, uma vez que podemos criar objetos utilizando a palavra reservada new
a qualquer momento em nosso código? Esta é uma excelente pergunta para relembrarmos um pouco dos conceitos de Programação Orientada a Objetos. Vejamos:
Existe um método que é invocado automaticamente pelo operador new
logo após a memória necessária para armazenar o objeto ser alocada. Esse método é o que chamaos de construtor. É nos construtores que fazemos toda a inicialização das dependências daquele objeto e o preparamo para ser utilizado em nosso sistema. Os construtores se diferenciam de métodos tradicionais por algumas características:
- Construtores têm obrigatiriamente o mesmo nome da classe (inclusive respeitando a mesma grafia com maiúsculas e minúsculas)
- Construtores não possuem tipo de retorno
Vejamos um exemplo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Dragon | |
{ | |
int age; | |
public Dragon(int dragonAge) | |
{ | |
age = dragonAge; | |
} | |
public bool Dracarys() | |
{ | |
// Burn all the enemies | |
return true; //return true because they are all dead | |
} | |
} |
Esse exemplo tem pouca aplicação na vida real (uma pena, por que seria incrível ter um Dragão, rs!) mas serve para o propósito de ilustrar nosso exemplo.
A classe Dragon
, do exemplo, e possui um método público com mesmo nome. Assim, sempre que uma instância dessa classe é criada por meio do operador new, o método Dragon(int)
será imediatamente chamado após a alocação de memótria.
É possível que você já tenha visto algumas classes que não possuem construtores. Mas como é possível existir uma classe que não possui, se toda vez que instanciamos um objeto o método construtor é invocado automaticamente?
Vamos começar a responder esta perguntanta entendendo que nem toda classe necessariamente precisa ter processamento realizado dentro do construtor. Se no cenário hopotético do exemplo anterior a idade do Dragão não fosse importante, poderíamos elimitar o construtor da classe e ela ficaria parecida com algo assim:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Dragon | |
{ | |
public bool Dracarys() | |
{ | |
// Burn all the enemies | |
return true; //return true because they are all dead | |
} | |
} |
Nesse caso, podemos claramente ver que a classe não possui construtor, isto é, um método homônimo (com mesmo nome).
Nesse caso, o compilador entende que não é necessário efetuar nenhum processamento no construtor dessa classe e (grosseiramente falando) “adiciona automaticamente” um construtor padrão por você, isto é, um método sem tipo de retorno e com o mesmo nome da classe.
Ou seja, mesmo que você não tenha especificado, o construtor estará lá. Por isso é possível que classes sem um construtor explícito sejam instanciadas.
Agora voltemos a pergunta inicial, que tentava explicar como uma classe pode garantir que apenas uma instância dela seja criada em todo o ciclo de vida do sistema. A resposta está exatamente nos construtores. Ora, os construtores dos quais falamos até agora são todos públicos, ou seja, eles estão acessíveis para serem invocados pelo operador new.
O que acontece, então, se o construtor for privado?
Se o construtor de uma classe for privado, apenas métodos pertencentes aquela classe poderão acessá-lo, ou seja, o operador new
não poderá invocá-lo durante a criação do objeto e, dessa forma, você não poderá instanciar objetos utilizando a sintaxe Dragon viserion = new Dragon()
, por exemplo.
Assim, podemos concluir que a criação de instâncias de uma classe que possui apenas construtores privados deve ser feita por ela mesma. E, dessa forma, a classe pode controlar quantas instâncias serão criadas.
[continua]
Encontrou algum erro ou quer sugerir uma melhoria para este post? Entre em contato comigo pelo formulário a seguir.. Críticas construtivas nos ajudam a fazer cade vez melhor o nosso trabalho.