Singleton é quando uma classe possui apenas uma instância e não se deseja que em uma mesma aplicação haja mais de uma.
Exemplos de classes desejadamente singleton são pools e carregadores.
Em C++ a saída para criar uma classe singleton é tornar protegido seu método construtor e criar um método
getInstance
para retornar a instância única:class Loader {
protected:
Loader();
public:
~Loader();
static Loader& getInstance();
}
O método
getInstance
deve guardar uma referência estática para a instância:static Loader& Loader::getInstance() {
static Loader *instance = 0;
if (!instance)
instance = new Loader();
return *instance;
}
Java utiliza a mesma abordagem, já outras linguagens, como Perl, Python e Ruby têm uma abordagem bem mais elegante.
Singleton em Python
A ideia de singleton em Python – Perl e Ruby – é usar o próprio construtor da classe para obter a instância.
Por exemplo, em vez de (Java):
Image character = Loader.getInstance().getImage("char.png");
Teremos (Python):
character = Loader().getImage("char.png")
Há três formas de implementar singleton em Python: 1implementando diretamente na classe, 2herdando um classe pai ou 3usando metaclasse (o mais divertido!).
Implementação direta na classe
Você pode implementar uma classe singleton diretamente, usando os metamétodos
__new__
(construtor real) e __init__
(construtor de inicialização).A classe ainda precisa ter um atributo de classe para armazenar a instância.
A ideia mais simples é implementar
__new__
para devolver a instância em vez de uma nova a cada vez:class Loader:
__instance = None
__initialized = False
def __new__(cls, *args, **keyw):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance
Repare no atributo
__initialized
… isso é porque ainda temos um problema: o construtor de inicialização (__init__
) será chamado novamente a cada tentativa de obter a instância.Então precisamos verificar se a inicialização já foi executada:
def __init__(self, *args, **keyw):
if not self.__initialized:
self.__initialized = True
# Restante do método...
Pronto! Nossa classe já é singleton.
Herança
Podemos usar uma classe pai que implemente unicidade e estendê-la:
class Singleton:
__instance = None
__initialized = False
def __new__(cls, *args, **keyw):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, *args, **keyw):
if not self.__initialized:
self.__initialized = True
self._init(*args, **keyw)
Assim, para implementar uma classe singleton:
class Loader(Singleton):
def _init(self, *args, **keyw):
# Este será o construtor...
A vantagem desta abordagem é sua capacidade de reaproveitamento: é possível reutilizar a classe
Singleton
como classe pai de cada nova classe singleton.No entanto traz dois inconvenientes: primeiro os métodos podem conflitar com métodos de outras classes pai em herança múltipla; segundo é preciso prestar atenção à implementação estranha (uso de
_init
em vez de __init__
) e ter cuidado com a sobrescrita de métodos.A solução para tornar isso mais transparente é usar uma metaclasse.
Metaclasse
Metaclasse é um classe cujas instâncias são classes. Vamos criar uma metaclasse cujas classes sejam singleton:
class singleton(type):
def __init__(cls, name, base, dict):
super(singleton, cls).__init__(name, base, dict)
cls.__instance = None
cls.__copy__ = lambda self: self
cls.__deepcopy__ = lambda self, memo=None: self
def __call__(cls, *args, **keyw):
if cls.__instance is None:
cls.__instance = \
super(singleton, cls).__call__(*args, **keyw)
return cls.__instance
Agora vem a beleza da metaclasse: para criar uma classe singleton basta fazer:
class Loader:
__metaclass__ = singleton
Nada mais! Todo o resto da classe pode ser implementado sem preocupações.
A única dúvida que pode ocorrer é: e se a classe tiver outra metaclasse?
É fácil resolver! Por exemplo: imagine uma classe singleton que implemente autopropriedades:
class Loader:
class __metaclass__(autoprop, singleton):
pass
**
Está aqui outra dica! Mais sobre singleton pode ser encontrado nas Reflexões de Monte Gasppa e Giulia C..
[]'s
Cacilhas, La Batalema