Pergunta

Eu estou olhando para criar um singleton em Delphi.Eu já fiz isso antes usando versões antigas do Delphi, e acabou usando variáveis globais (na seção de implementação) e, usando a inicialização e finalização para cuidar da instância.Também não havia nenhuma maneira de impedir que o usuário a criação de uma instância como você não poderia esconder o construtor padrão.Eu queria saber se qualquer uma das novas funcionalidades tais como a classe de construtores e destrutores, e variáveis de classe (ok, não tão nova), talvez genéricos, poderia ajudar na criação de um genérico classe singleton.Eu ainda não conseguiu criar algo para a minha satisfação ainda.

Foi útil?

Solução

Era possível gerenciar isso substituindo o VERDADEIRO atribuição e deallocator métodos em Delphi, NewInstance e FreeInstance.Construtores e Destrutores em Delphi apenas inicializar e finalizar respectivamente, eles não alocar ou desalocar a memória, então, tentar ocultar os construtores sempre foi um pouco equivocado.

i.e.foi possível, para permitir a utilização de qualquer e todos os construtores contanto que você derrubou NewInstance de tal forma que ele só retornou uma referência a uma única alocação de memória para a classe.

Mas a tentativa de impor um uso/padrão de comportamento em uma classe base é um erro imho.Nem TODOS os padrões são ou exigir classes específicas para encapsular o padrão.

Em casos como este, você acaba criando algo que é desnecessariamente complicado, e a complicação atrai erros na minha experiência e o objeto do exercício torna-se então tentar encontrar falhas na implementação do padrão e, em seguida, tentar implementar salvaguardas contra essas falhas, ao invés de ficar com o trabalho na prática de trabalho que a classe singleton era suposto para executar.

É muito, MUITO mais simples e mais eficaz para documentar o USO da classe.

A documentação como uma técnica para implementar este padrão tem funcionou perfeitamente durante 15 anos para o Aplicação e Tela objetos da VCL, por exemplo, para não falar de inúmeras outras gestações únicas que eu criei naqueles anos.

Outras dicas

Se você só precisa de uma simples singleton, a maneira mais simples é usar construtores de classe e métodos de classe, como sugerido por plainth.Mas os genéricos são muito úteis se você precisar de gestações únicas com a construção da demanda (por exemplo,no primeiro acesso).

O código a seguir é tirada de um dos meus utilitário de unidades;basicamente, ele oferece um genérico singleton fábrica para o Delphi 2009.

interface

type
  {$HINTS OFF}
  { TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding
    expensive initialization operations.
    If you do not require lazy creation and you target only Delphi 2010 onwards, you should
    use class constructors and class destructors instead to implement singletons. }
  TSingletonInstance<T: class, constructor> = record
  private
    FGuard: IInterface;
    FInstance: T;
    function GetInstance: T;
    function CreateInstance: TObject;
  public
    property Instance: T read GetInstance;
  end;
  {$HINTS ON}
  TSingletonFactoryFunction = function: TObject of object;

{ Private symbols (which are in the interface section because of known limitations of generics) }
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);

implementation

{ TSingleton }

var
  SingletonCriticalSection: TRTLCriticalSection;

type
  TSingletonGuard = class (TInterfacedObject)
  private
    FSingletonInstance: TObject;
  public
    constructor Create (AInstance: TObject);
    destructor Destroy; override;
  end;

  PUntypedSingletonInstance = ^TUntypedSingletonInstance;
  TUntypedSingletonInstance = record
    FGuard: IInterface;
    FInstance: TObject;
  end;

  // TODO: is a lock required for multiple threads accessing a single interface variable?
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
var
  USI: PUntypedSingletonInstance;
begin
  USI := PUntypedSingletonInstance (InstanceRecord);
  EnterCriticalSection (SingletonCriticalSection);
  if USI.FInstance = nil then
  begin
    USI.FInstance := Factory ();
    USI.FGuard := TSingletonGuard.Create (USI.FInstance);
  end;
  LeaveCriticalSection (SingletonCriticalSection);
end;

constructor TSingletonGuard.Create (AInstance: TObject);
begin
  FSingletonInstance := AInstance;
end;

destructor TSingletonGuard.Destroy;
begin
  FSingletonInstance.Free;
  inherited;
end;

function TSingletonInstance<T>.GetInstance: T;
var
  Factory: TSingletonFactoryFunction;
begin
  if FInstance = nil then
  begin
    Factory := Self.CreateInstance; // TODO: associate QC report
    _AllocateSingletonInstance (@Self, Factory);
  end;
  Result := FInstance;
end;

function TSingletonInstance<T>.CreateInstance: TObject;
begin
  Result := T.Create;
end;

initialization
  InitializeCriticalSection (SingletonCriticalSection);
finalization
  DeleteCriticalSection (SingletonCriticalSection);

O uso da seguinte forma:

type
  TMySingleton = class
  public
    constructor Create;
    class function Get: TMySingleton; static;
  end;

var
  MySingletonInstance: TSingletonInstance<TMySingleton>;

class function TMySingleton.Get: TMySingleton;
begin
  Result := MySingletonInstance.Instance;
end;

No Delphi 2010, de longe, a melhor e mais segura maneira é usar construtores de classe.Ver aqui - leia especialmente o parágrafo chamado Melhorado o encapsulamento.

HTH.

Eu prefiro usar interfaces quando eu preciso de gestações únicas e ocultar a implementação da interface na seção de implementação.

benefícios

  • Destruição automática quando o programa termina.
  • De jeito nenhum acidentalmente a criar um TMySingleton.

desvantagens

  • Alguém pode decidir implementar IMySingleton no seu próprio.

Nota:Eu acredito que o uso de Singletons deve ser mantido a um mínimo absoluto.Tudo em tudo, Singletons são pouco mais do que glorificaram a variáveis globais.Se e quando você começa a unidade de testar o seu código, eles se tornam um incômodo.

unit uSingleton;

interface

type
  ISingleton = interface
    ['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}']
  end;

function Singleton: ISingleton;

implementation

uses
  SyncObjs;

type
  TSingleton = class(TInterfacedObject, ISingleton);

var
  Lock: TCriticalSection;

function Singleton: ISingleton;
const
  _singleton: ISingleton = nil;
begin
  if not Assigned(_singleton) then
  begin
    Lock.Acquire;
    try
      if not Assigned(_singleton) then
        _singleton := TSingleton.Create();
    finally
      Lock.Release;
    end;
  end;
  Result := _singleton;
end;

initialization
  Lock := TCriticalSection.Create;
finalization
  Lock.Free;

end.

Há uma forma de ocultar a herança "Criar" construtor de TObject.Embora não seja possível alterar o nível de acesso, ele pode ser escondido com outro público sem parâmetros método com o mesmo nome:"Criar".Isso simplifica a implementação da classe Singleton tremendamente.Veja a simplicidade do código:

unit Singleton;

interface

type
  TSingleton = class
  private
     class var _instance: TSingleton;
  public
    //Global point of access to the unique instance
    class function Create: TSingleton;

    destructor Destroy; override;
  end;

implementation

{ TSingleton }

class function TSingleton.Create: TSingleton;
begin
  if (_instance = nil) then
    _instance:= inherited Create as Self;

  result:= _instance;
end;

destructor TSingleton.Destroy;
begin
  _instance:= nil;
  inherited;
end;

end.

Eu adicionei os detalhes para o meu post original: http://www.yanniel.info/2010/10/singleton-pattern-delphi.html

Para um singleton, você pode substituir o método NewInstance.E usar uma variável de classe.Você cria a variável em primeira chamada e retornar o ponteiro para a classe de cada chamada.

Você só precisa encontrar someting para destruí-lo no final (provavelmente usando a finalizar).

Eu prefiro criar classe singleton usando um gerador de código.O problema com o genérico é a de que, todo o código é gerado na memória, não no arquivo de origem.Ele irá aumentar a dificuldade de depuração.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top