Um design pattern bastante conhecido na engenharia de software é singleton.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.__instanceRepare 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.__instanceAgora vem a beleza da metaclasse: para criar uma classe singleton basta fazer:
class Loader:
__metaclass__ = singletonNada 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

6 comentários:
Cara, mandou bem! Mandou mesmo!
Excelente artigo e interessante por ser o tema bastante recorrente nas listas de email.
"Java utiliza a mesma abordagem, já outras linguagens, como Perl, Python e Ruby têm uma abordagem bem mais elegante."
Eu não sei o que você quis dizer com elegante, mas todas as formas de implementação em Python que você apresentou tem muita cara de que o programador tá forçando a linguagem, utilizando-se de recursos de um nível mais baixo do que o normalmente utilizado na linguagem. Gosto de Python, mas em Java e C++ o Singleton nasce muito mais naturalmente. Esse não é o caso de Python.
Salve André!
Quando usei a palavra «elegante» não quis referir-me à implementação em si, mas ao uso depois de implementado.
Se você usa metaclasse, por exemplo, pode colocá-la em um módulo e importá-la, apenas ajustando o atributo __metaclass__ das classes singleton.
Depois toda vez que precisar obter a instância da classe é só usar o construtor normalmente, instanciando a classe – a única diferença é que você receberá sempre a mesma instância.
Em Java e C++ você precisa sempre se lembrar que não pode usar o construtor e precisa saber qual o nome do método que retorna a instância – é costume usar getInstance, mas não há um padrão real.
Gosto muito de C++, até mais do que de Python, no entanto, mesmo trabalhando em um nível um pouco mais baixo, o código Python é mais elegante.
Tenta fazer C++ ou Java retornar sempre a mesma instância no construtor! Não é justo comparar um recurso superficial de C++ e Java – que pode ser usado em Python, caso você prefira – com códigos que alteram o comportamento da linguagem em Python para facilitar a vida do programador.
É comparar como laranjas e torradeiras.
Eduardo, valeu o elogio!
[]'s
Cacilhas, La Batalema
Laranjas e torradeiras... hm... isso me dá uma idéia...
O problema do pattern Singleton é que muitas vezes vc não precisa de um Singleton.
Em Java, por exemplo, vc pode ter uma instância de Singleton por classloader e muitas vezes vc tem mais de uma JVM para atender uma aplicação, como varios servidores atras de um balanceador de carga e, nesses casos, vc tem varios Singletons e comportamentos inesperados. Sem falar que a testabilidade de um Singleton não é algo obvio, tanto que é considerado um anti-pattern por parte da galera de TDD. Sem falar nos problemas de sincronização entre threads que vc pode ter se implementar este padrão de qq jeito.
Eu pensaria duas vezes antes de implementar um Singleton: talvez existam outras formas de resolver o meu problema.
Agora, Singleton é um caso especial de patterns que limitam o numero de instâncias de um objeto (nesse caso o número é 1), mas eu posso querer ter no maximo X instâncias (como um pool de conexões com um banco de dados, por exemplo). Seria interessante falar sobre isso.
Outra coisa interessante é pq usar um objeto singleton e não propriedades e métodos estáticos diretamente na classe, pois eu posso obter os mesmos resultados, mas tem algumas diferenças :)
Salve Tiago!
> O problema do pattern Singleton é que muitas vezes vc não precisa de um Singleton.
Você poderia destacar isso se não pudesse dizer o mesmo dos demais design patterns.
> Em Java, por exemplo, vc pode ter uma instância de Singleton por classloader e muitas vezes vc tem mais de uma JVM para atender uma aplicação, como varios servidores atras de um balanceador de carga e, nesses casos, vc tem varios Singletons e comportamentos inesperados.
Isso não signfica nada que eu já não tenha dito: são casos e casos e você precisa escolher a melhor ferramenta para cada caso.
Ninguém em sã consciência vai usar uma marreta para enfiar um parafuso na parede!
> Sem falar que a testabilidade de um Singleton não é algo obvio, tanto que é considerado um anti-pattern por parte da galera de TDD.
Testabilidade é um conceito subjetivo, usado por evangelizadores que não tem o que dizer. Seria quase o equivalente a falar em mojo ou em cocoricó, a diferença é que existe um conceito pessoal e subjetivo por trás.
O que traz credibilidade ao conceito de testabilidade é que integrantes de grupos precisam interagir para efetuar testes – e empresas aproveitam isso para lucrar vendendo seus cursinhos.
Quanto a galera, eu quero mais que qualquer galera se exploda! Eles não vão fazer meu trabalho por mim.
> Eu pensaria duas vezes antes de implementar um Singleton: talvez existam outras formas de resolver o meu problema.
Essa afirmação cai no mesmo caso da primeira: você pode dizer isso de qualquer pattern.
É sempre importante considerar formas diferentes de se resolver um problema e eventualmente você vai acabar usando singleton, assim como vai acabar não usando.
> Agora, Singleton é um caso especial de patterns que limitam o numero de instâncias de um objeto (nesse caso o número é 1), mas eu posso querer ter no maximo X instâncias (como um pool de conexões com um banco de dados, por exemplo). Seria interessante falar sobre isso.
Adorei a sugestão!
Quando citei pool no artigo quis dizer que um determinado pool – de conexões, de threads, etc. – pode ser singleton, mas esse objeto único gerencia um número X de conexões, threads, o que for.
Vou seguir seu conselho e escrever um artigo sobre pools.
> Outra coisa interessante é pq usar um objeto singleton e não propriedades e métodos estáticos diretamente na classe, pois eu posso obter os mesmos resultados, mas tem algumas diferenças :)
Não exatamente.
Por exemplo, usei recentemente singleton para carregar recursos de disco – aliás foi o que me deu a ideia para o artigo. Essa carga é efetuada por um procedimento.
Como você executaria esse procedimento? static { … }? Com um método de carga?
É possível, mas fica bem mais simples e claro com singleton.
Em suma, há casos e casos. Para alguns há soluções melhores, para outros singleton é uma boa saída, portanto é bom considerar ter essa ferramenta em sua maleta.
[]'s
Cacilhas, La Batalema
Postar um comentário