unit uSingleton;

interface

uses
  DateUtils, SysUtils, Windows, dialogs;

type

  TObjetoNormal = class
  private
    FDataHora: string;
  public
    constructor Create; 
    function GetDataHora: string;
  end;


  TObjetoUnico = class
  private
    FDataHora: string;
  public
    constructor Create;
    class function CreateUnico: TObjetoUnico;
    destructor Destroy; override;
    function GetDataHora: string;
  end;

procedure VerificaObjetoUnicoCriado;

implementation




var
  _ObjetoUnico: TObjetoUnico;
  _Contador: Integer = 0;

procedure VerificaObjetoUnicoCriado;
begin
  if (_Contador >= 1) and (_ObjetoUnico <> nil) then
    ShowMessage('=========== verificação ===========' + #13#10 +
    'Nome da classe: ' + _ObjetoUnico.ClassName+ #13#10 +
    'Hora de Criação: ' + _ObjetoUnico.GetDataHora + #13#10 +
    'Endereço na memória: ' + IntToStr(Integer(_ObjetoUnico))
  );
end;

{ TObjetoNormal }

constructor TObjetoNormal.Create;
begin
  inherited Create;
  FDataHora := FormatDateTime('yyyy-mm-dd hh:nn:ss', now);
end;

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

{ TObjetoUnico }

constructor TObjetoUnico.Create;
begin

    if (_Contador = 0) then
    begin
      inherited Create;  //aqui tudo bem usar o inherited create e destroy porque a classe base não faz nada de mais
      InterlockedIncrement(_Contador);
      FDataHora := 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;

class function TObjetoUnico.CreateUnico: TObjetoUnico;
begin
  if _ObjetoUnico = nil then
    _ObjetoUnico := TObjetoUnico.Create;
  Result := _ObjetoUnico;
end;

destructor TObjetoUnico.Destroy;
begin

  //aqui permitimos que o objeto seja destruido.
  //logico que isso dará problemas para quem tentar usar o objeto
  //mas caso necessário ele pode ser recriado automaticamente
  //desde que seja recriado uma unica vez.

  //por isso é importante não guardar referencias ao singleton em
  //variáveis, mas chama-lo apenas através de um método

  _ObjetoUnico := nil;
  InterlockedDecrement(_Contador);
  inherited;

end;

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



initialization
  _ObjetoUnico := nil;
  _Contador := 0;

finalization
  if (_ObjetoUnico <> nil) then
  try
    _ObjetoUnico.Free;
  except
    //tratamento de excessão
  end;

end.
