Dec 01 2006

Tentativa de Singleton usando Delphi

Autor: Marcos Dell Antonio - Categorias: Delphi

O design pattern Singleton garante que somente uma instância de determinada classe estará disponível durante todo o ciclo de vida da aplicação.

Hoje precisei implementá-lo em uma aplicação servidora. Tenho uma estrutura assim:

  • Arquivo de configuração XML (Configuracoes.xml);
  • Classe que faz read/write neste arquivo (TXMLCfgClass);
  • Classes que utilizam a TXMLCfgClass para acessarem as suas configurações (TServidorCfgClass, TBackupCfgClass, TBancoDadosCfgClass, etc.).

O mesmo arquivo XML será compartilhado pelas três classes de acesso às configurações, ou seja, uma única instância do objeto TXMLCfgClass deverá estar disponível para as três classes. Aqui é que entra o Singleton: ele garante que somente uma instância da classe TXMLCfgClass existirá durante todo o ciclo de vida da aplicação.

Tudo muito bonito e interessante na teoria. Na prática a coisa é um pouquinho diferente.

Tentei criar uma propriedade (ou field) privado e estático na classe TXMLCfgClass, mas não consegui. Uma solução semelhante é utilizar uma variável na seção implementation, desta forma:

Declaração da variável
1 implementation 2 3 var 4 xmlCfg: TXMLCfgClass;

Implementação do Singleton (método estático e público):
class function TXMLCfgClass.GetInstance: TXMLCfgClass; begin // GetInstance if (xmlCfg = nil) then xmlCfg := TXMLCfgClass.Create; Result := xmlCfg; end;

A abordagem é muito interessante, pois se bem utilizada (chamando somente o método GetInstance) funcionará. No entanto, ainda é possível instanciar um objeto TXMLCfgClass utilizando o constructor Create.

Outra forma de implementar o Singleton é fazendo um override no método NewInstance da classe TObject. Encontrei este exemplo que é específico para o Delphi 5, no entanto, fiz os testes com o Delphi 6 e funcionou também.

Aqui também não são só flores. De acordo com o help do Delphi 6, ao fazer o override do NewInstance não devemos chamar inherited NewInstance no final, mas sim InitInstance. No exemplo que fiz, utilizei o inherited NewInstance e funcionou perfeitamente. O único detalhe é que não deve-se chamar o FreeAndNil após utilizar o objeto.

Veja o exemplo:

Declaração pública:
class function NewInstance: TObject; override;

Implementação:
class function TCliente.NewInstance: TObject; begin if (cliente = nil) then cliente := TCliente(inherited NewInstance); Result := cliente; end;

No fim das contas, acabei optando pela primeira opção. Faça os testes e escolha uma delas. ;)

É isso aí! T+

Adicione ao del.icio.us del.icio.us | Adicione ao Rec6 Rec6

Faça um comentário