unit uSingleton;

interface

uses
  DateUtils, SysUtils, Windows, dialogs;

type



  TObjetoUnico = class
  private
    FDataHora: string;
    class var  FObjetoUnico: TObjetoUnico;
    class var  FContador: Integer;
    class function GetObjetoUnico: TObjetoUnico; static;
    class function GetContador: integer; static;


  public
    constructor Create;
    destructor Destroy; override;
    function GetDataHora: string; virtual;

    class property ObjetoUnico:  TObjetoUnico read GetObjetoUnico;
    class property Contador: integer read GetContador;
    property DataHora: string read GetDataHora write FDataHora;
  end;

procedure VerificaObjetoUnicoCriado;

implementation


procedure VerificaObjetoUnicoCriado;
begin
  //Uso do FObjetoUnico aqui porque se usasse ObjetoUnico o método acessor
  //GetObjetoUnico seria automaticamente executado, criando o objeto
  if (TObjetoUnico.FContador >= 1) and (TObjetoUnico.FObjetoUnico <> nil) then
    ShowMessage('=========== verificação ===========' + #13#10 +
    'Nome da classe: ' + TObjetoUnico.FObjetoUnico.ClassName+ #13#10 +
    'Hora de Criação pelo método: ' + TObjetoUnico.FObjetoUnico.GetDataHora + #13#10 +
    'Hora de Criação pela propriedade: ' + TObjetoUnico.FObjetoUnico.DataHora + #13#10 +
    'Endereço na memória: ' + IntToStr(Integer(TObjetoUnico.FObjetoUnico))
  );
end;


{ TObjetoUnico }

constructor TObjetoUnico.Create;
begin

    //essa verificação impede uma segunda chamada a create
    //se não verificar, ou se deixar crar para se disparar a excessão depois
    //da verificação corre-se o risco de ter criado mais um antes da excessão
    //e aí não será possível destruir, causando um leak
    if (FContador = 0) and (FObjetoUnico = nil) then
    begin
      inherited Create;  //aqui tudo bem usar o inherited create e destroy porque a classe base não faz nada de mais
      InterlockedIncrement(FContador);
      DataHora := FormatDateTime('yyyy-mm-dd hh:nn:ss', now);
    end
    else
      raise Exception.Create('Ei! Não use esse constructor, use o CreateUnico!');
    //o inherited fica dentro do if assim o objeto não será criado caso ja esteja o contador > 0

end;



destructor TObjetoUnico.Destroy;
begin

  FObjetoUnico := nil;
  InterlockedDecrement(FContador);
  inherited;

end;

class function TObjetoUnico.GetContador: integer;
begin
  Result := FContador;
end;

function TObjetoUnico.GetDataHora: string;
begin
  Result := FDataHora;
end;



//como a propriedade  ObjetoUnico executa esse get e esse get le o class var e
//o instancia caso seja nil a propriedade ObjetoUnico tem uma proteção
//natural contra nil e sempre será instanciada, nunca será igual a nil
class function TObjetoUnico.GetObjetoUnico: TObjetoUnico;
begin
  //aqui a mesma verificação é feita para não se executar o create duas vezes
  //mas devolver o objeto existente ou devolvê-lo assim que criado
  if (FContador = 0) and (FObjetoUnico = nil) then
    FObjetoUnico := TObjetoUnico.Create;
  Result := FObjetoUnico;
end;

initialization
  //a initilization não é necessária

finalization
  if (TObjetoUnico.FObjetoUnico <> nil) then
  try
    //leia o comentário abaixo para saber porque o campo privado FObjetoUnico
    //é usado aqui em vez da propriedade ObjetoUnico
    TObjetoUnico.FObjetoUnico.Free;
  except
    //tratamento de excessão
  end;

  //por causa da proteção "natural" contra nil você pode destruir
  //o singleton com
  //TObjetoUnico.ObjetoUnico.Free;
  //mas isso causa um overhead porque se o FObjetoUnico não estiver criado (nil)
  //ele será criado só para ser destruído.
  //É bonito de se ver, mas feio do ponto de vista do algoritmo

end.
