Singleton
July 12th, 2009
Analogia
Você está no mercado fazendo compras. Passa por uma prateleira e coloca os produtos no carrinho. Anda, anda, olha a sua volta, vê outros produtos, coloca-os em seu carrinho e continua fazendo suas compras. Depois de muito “passear”, chega ao caixa, retira os produtos do carrinho, paga e vai embora. Do momento em que entrou no mercado até o momento que saiu, quantos carrinhos você utilizou? Um. Tão somente um.
Conceito
O singleton consiste em retornar à sua aplicação apenas e tão somente UMA INSTÂNCIA DO OBJETO, que será compartilhada e terá ponto de acesso global. Por ser tão simples, talvez seja um pouco confuso de entender no começo. Este é um design pattern criacional.
Diagrama de Classe: Singleton Design Pattern

Diagrama de Classes: Singleton Pattern
Exemplo
Vamos fazer o Singleton de nosso carrinho de compras.
- Crie um novo arquivo Singleton1.fla
- Crie uma classe para ser compilada ao nosso .fla. Singleton1.as ou Main.as
- Crie uma classe representando o carrinho. Segue o diagrama de classe:

Diagrama de Classe: Carrinho Singleton
Veja que existe uma dependência da classe Carrinho com CarrinhoEnforcer. Isso por que não temos construtores privados no ActionScript 3.0, então temos que “contornar”, criando uma classe interna em Carrinho, onde somente esta terá visibilidade, forçando então a dependência (validando nosso Singleton).
Código
Carrinho.as
package { /** * ... * @author André Vendramini */ public class Carrinho { private var _items:Array; private static var instance:Carrinho; public function Carrinho(enforcer:CarrinhoEnforcer=null) { if (enforcer == null) { throw new Error("Apenas uma instância deve ser criada."); } _items = new Array(); } public static function getInstance():Carrinho { if (instance == null) { //Preste atenção nesta linha: instance = new Carrinho(new CarrinhoEnforcer()); /* * Neste momento, estamos instanciando nosso carrinho, * passando como parâmetro apenas uma referência da classe * CarrinhoEnforcer. Como essa classe é interna, apenas * a classe Carrinho tem visibilidade, com isso, esta é a única * maneira de instanciarmos nossa classe Carrinho. */ } return instance; } public function addItem(item:String):void { _items.push(item); } /* GETTERS AND SETTERS */ public function get items():Array { return _items; } } } /* * Classe interna, apenas a classe Carrinho tem visibilidade desta. * Com isso, temos a certeza de que apenas a classe Carrinho irá passá-la como parâmetro no construtor. * Essa é uma das formas de "burlar" o ActionScript, já que não temos construtores privados. */ class CarrinhoEnforcer { public function CarrinhoEnforcer() { } }
Singleton da classe Carrinho criado. Agora vamos usá-lo. Na classe a ser compilada, entre com o seguinte código:
package { import flash.display.Sprite; /** * ... * @author André Vendramini */ public class Singleton1 extends Sprite { Carrinho.getInstance().addItem("item 1"); Carrinho.getInstance().addItem("item 2"); Carrinho.getInstance().addItem("item 3"); Carrinho.getInstance().addItem("item 4"); Carrinho.getInstance().addItem("item 5"); trace(Carrinho.getInstance().items); } }
O output deverá ser: item 1,item 2,item 3,item 4,item 5. Como nossa função getInstance() é publica e estática, temos ponto de acesso global a instância da classe Carrinho de qualquer escopo. Agora, vamos forçar um erro. Tente instanciar a classe Carrinho normalmente. Ainda na classe a ser compilada:
package { import flash.display.Sprite; /** * ... * @author André Vendramini */ public class Singleton1 extends Sprite { var carrinho:Carrinho = new Carrinho(); } }
O output:
Error: Apenas uma instância deve ser criada.
at Carrinho()
at Singleton1()
Perceba que a única forma da instância da classe Carrinho ser criada é passando um parâmetro: uma instância da classe CarrinhoEnforcer. Vamos tentar passar essa instância como parâmetro e burlar nosso Singleton? Simples, não?
package { import flash.display.Sprite; /** * ... * @author André Vendramini */ public class Singleton1 extends Sprite { var carrinho:Carrinho = new Carrinho(new CarrinhoEnforcer()); } }
Output: 1180: Call to a possibly undefined method CarrinhoEnforcer. A classe foi definida DENTRO da própria classe Carrinho.as, tendo apenas esta a visibilidade da mesma. Voilà, nosso Singleton está apto a ser posto em serviço.
Baixe os arquivos aqui.
Aplicabilidade
Este é um pattern extremamente útil se aplicado de forma correta. Por exemplo, em jogos. Para contar pontuação de um jogo, não importa em qual fase esteja, quantos pontos ganhe nem quantas vezes tente. O Singleton se encarrega de cuidar direitinho de tudo isso. Com a certeza de existir apenas 1 ponto de acesso a instância.
Cuidados
Este é um pattern que pode ludibriar no início por sua simplicidade e poder. Não o use abusivamente para corrigir problemas nem contornar situações de orientação a objetos. Por trabalhar com uma função estática e de acesso global, você pode ficar “preguiçoso” de fazer da forma correta e começar a usá-lo de forma errada, apenas para poupar tempo. Os princípios da orientação a objetos não devem ser substituídos, jamais.
Ele quebra uma das leis da orientação a objetos, que diz para cada objeto ter uma responsabilidade. O Singleton possui 2: da acesso global a variável e têm a certeza de única instância.
Farei um post sobre o assunto mais pra frente. Leiam esses materiais:
- http://www.as3dp.com/2008/11/26/we-don%E2%80%99t-need-no-stinkin%E2%80%99-singletons-why-to-avoid-the-singleton-pattern-in-actionscript-30-programming/
- http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
Curiosidades
Singleton é uma alusão a jogo de cartas. Nos EUA, quando o jogador fica apenas com uma carta na mão, esta é chamada “singleton”.
Este é um padrão GoF.
Filed under: criacionais, design patterns | 1 Comment »