sexta-feira, 12 de novembro de 2010

Metaprogramação no lazarus

No meu último post eu falei sobre o componente Pascal Script da RemObjects. A melhor notícia é que a versão 3 do componente, com um belo instalador automático e uma série de melhorias, tem uma versão para lázarus muito fácil de se instalar.

Além disso tem uma versão também para o visual studio / .net, então imagina usar metaprogramação "via script" no ambiente .net que já permite metaprogramação. Vai ser show de bola, e um perigo também, se mal usado.

Esse é  o tipo de recurso que quando agente usa fala: "e porque não?".

Bom, pelo menos como aprendizado e prova de conceito darei uma estudada e desenvolverei mais alguns exemplinhos.

Por hora, fiz no lázarus o mesmíssimo exemplo feito em Delphi no último post, espero que gostem. Uma das mudanças foi colocar um SynEdit para digitar o código, em vez de um memo comum.



O Synedit é show!

Faça o download do exemplo aqui:

Have fun ;)

quarta-feira, 10 de novembro de 2010

Chamando Formulários com PascalScript + RTTI + Factory Methods

Um desenvolvedor que leu meu artigo sobre metaprogramação no Delphi me perguntou hoje como criar e abrir formulários a partir de scripts com o Pascal Script.
Não há uma maneira muito fácil de se fazer isso, e pode haver outras maneiras de se fazer.

Você não pode simplesmente registrar a form1 simplesmente porque o tipo TForm1 não existe. Ele não é nativo do Delphi, mas foi escrito por você.

O Pascal Script não conhece o tipo TForm1 e não tem como, dentro do script, você criar uma variável desse tipo.

Uma das maneiras seria você registrar a unit1, fazendo com que o pascal script a use, ou declarar a TForm1 todinha dentro do pascal script. Essas duas opções são complicadas, pois um dos meios é passar o fonte .pas junto com a aplicação e deixar  o cliente ver / manipular livremente. E se por acaso a unit1 usa ou depende de outras units você teria um enorme problema em cascata.

Além disso o pascal script é limitado. Ele se limita apenas às operações básicas do pascal.

Uma maneira elegante de resolver esse impasse seria usar uma mistura de FactoryMethod com RTTI e pascal script.

Use a função registerclass para "registrar" a classe da form que você quer que seja aberta no script. Faça isso na seção initilization como segue:

unit uFrmChamada;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmChamada = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmChamada: TfrmChamada;

implementation

{$R *.dfm}


initialization
  RegisterClass(TfrmChamada);

end.


RegisterClass exige um parâmetro do tipo TPersistentClass, que é um tipo que define um tipo (?!?!?) Sim, é um metatipo, ou metaclasse se você preferir.

Como forms são descendentes de TPersistent, qualquer tipo de classe de form é do mesmo tipo do metatipo TPersistentClass (?!?!?). Estou falando de classes, não de objetos, repare bem nisso. Você pode ter variáveis que são do TIPO CLASSE e não DO TIPO DE UMA CLASSE.

E não podemos esquecer que na unit forms existe a declaração:

  TFormClass = class of TForm;

Para facilitar nossa vida, porque TFormClass também é um TPersistentClass, mas é específico da classe TForm, ou seja, TFormClass é um descendente indireto (bastardo) de TPersistentClass.

São os truques da RTTI para se trabalhar dinamicamente com tipos sobre tipos. (variáveis de tipos).

Partindo do pressuposto que você já tem uma form com um memo para digitar o código e o componente do pascal script colado nela, crie nessa form (ou numa unit separada para não gerar acoplamentos desnecessários) uma function estática global assim:

function ConstrutorDeForms(NomeClasse: string): TForm;
var
  ClasseDaForm: TFormClass;
begin
  ClasseDaForm := TFormClass(FindClass(NomeClasse));
  Result := ClasseDaForm.Create(Application);
end;

Usei Application como owner aqui porque não estamos dentro de um objeto ou classe (form) estamos direto na unit. Poderia ser nil, mas assegure-se de que o objeto seja destruído para não criar leaks.

E registre a função assim, no evento OnCompile do pascal script:

 psExecutar.AddFunction(@ConstrutorDeForms, 'function ConstrutorDeForms(NomeClasse: string):TForm;');


Não esqueça de registrar outras funções e variáveis que vá utilizar, como foi explicado no artigo.

unit uFrmEditor;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, dateutils, DB, ADODB, uPSComponent, uPSCompiler,
  uPSRuntime, uPSComponent_DB, uPSComponent_Default;

type
  TfrmEditor = class(TForm)
    btExecutar: TBitBtn;
    psExecutar: TPSScript;
    mMensagens: TMemo;
    mSaida: TMemo;
    mFonte: TMemo;
    PSImport_DB1: TPSImport_DB;
    PSImport_Classes1: TPSImport_Classes;
    procedure btExecutarClick(Sender: TObject);
    procedure psExecutarCompile(Sender: TPSScript);
    procedure psExecutarExecute(Sender: TPSScript);
    procedure psExecutarCompImport(Sender: TObject; x: TPSPascalCompiler);
    procedure psExecutarExecImport(Sender: TObject; se: TPSExec;
      x: TPSRuntimeClassImporter);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


procedure NossoWriteLn(const s: string);
procedure NossoReadLn(var s: string);
//function para simular factory method com as classes de forms registradas
//ela cria a form e devolve a quem a chamar
function ConstrutorDeForms(NomeClasse: string): TForm;


var
  frmEditor: TfrmEditor;

implementation
uses
  uPSR_std,
  uPSC_std,
  uPSR_stdctrls,
  uPSC_stdctrls,
  uPSR_forms,
  uPSC_forms,
  uPSC_graphics,
  uPSC_controls,
  uPSC_classes,
  uPSR_graphics,
  uPSR_controls,
  uPSR_classes, uFrmChamada;

{$R *.dfm}


procedure NossoWriteLn(const s: string);
begin
  frmEditor.mSaida.Lines.Add(s);
end;

procedure NossoReadLn(var s: string);
begin
  s := InputBox('Digite um valor:', 'Digite um valor:', '');
end;

function ConstrutorDeForms(NomeClasse: string): TForm;
var
  ClasseDaForm: TFormClass;
begin
  ClasseDaForm := TFormClass(FindClass(NomeClasse));
  Result := ClasseDaForm.Create(Application);
end;



procedure TfrmEditor.btExecutarClick(Sender: TObject);
var
  Compilou, Executou: boolean;
  i: integer;
begin
  mMensagens.Clear;
  mSaida.Clear;


  psExecutar.Script.Text := mFonte.Text;
  Compilou := psExecutar.Compile;

  if Compilou then
  begin
    mMensagens.Lines.Add('Programa compilado com sucesso!');
    Executou := psExecutar.Execute;

    if Executou then
    begin
      mMensagens.Lines.Add('Programa executado com sucesso!');
    end
    else
    begin
      mMensagens.Lines.Add('Ocorreu o erro de execução: ' +
        psExecutar.ExecErrorToString +' onde? '+
        Inttostr(psExecutar.ExecErrorProcNo)+'.'+
        Inttostr(psExecutar.ExecErrorByteCodePosition));
    end;

  end
  else
     mMensagens.Lines.Add('Erro de compilação:');

  for i := 0 to psExecutar.CompilerMessageCount - 1 do
  begin
    mMensagens.Lines.Add('Compilador: '+ psExecutar.CompilerErrorToStr(i));
  end;

end;

procedure TfrmEditor.psExecutarCompile(Sender: TPSScript);
begin
  psExecutar.AddFunction(@NossoWriteLn, 'procedure Writeln(s: string);');
  psExecutar.AddFunction(@NossoReadLn, 'procedure ReadLn(var s: string);');
  psExecutar.AddFunction(@ShowMessage, 'procedure ShowMessage(s: string);');

  psExecutar.AddFunction(@ConstrutorDeForms, 'function ConstrutorDeForms(NomeClasse: string):TForm;');

  //adicionamos isso para existir a variável
  psExecutar.AddRegisteredVariable('Application', 'TApplication');
  psExecutar.AddRegisteredVariable('Self', 'TForm');
end;



procedure TfrmEditor.psExecutarCompImport(Sender: TObject;
  x: TPSPascalCompiler);
begin
  SIRegister_Std(x);
  SIRegister_Classes(x, true);
  SIRegister_Graphics(x, true);
  SIRegister_Controls(x);
  SIRegister_stdctrls(x);
  SIRegister_Forms(x);
end;

procedure TfrmEditor.psExecutarExecImport(Sender: TObject; se: TPSExec;
  x: TPSRuntimeClassImporter);
begin
  RIRegister_Std(x);
  RIRegister_Classes(x, True);
  RIRegister_Graphics(x, True);
  RIRegister_Controls(x);
  RIRegister_stdctrls(x);
  RIRegister_Forms(x);
end;

procedure TfrmEditor.psExecutarExecute(Sender: TPSScript);
begin
  psExecutar.SetVarToInstance('Application', Application);
  psExecutar.SetVarToInstance('Self', Self);
end;

end.


E para chamar a dita cuja segunda form faça assim:

program DoUsuario;

var frmChamada: Tform;


begin

frmChamada := ConstrutorDeForms('TFrmChamada');
frmChamada.ShowModal;
frmChamada.free;

end.


O legal é que a função ConstrutorDeForms pode ser usada também fora do pascal script para chamar qualquer form que esteja registrada com RegisterClass.

Para acessar componentes internos da form criada use os métodos FindComponent, exemplo:

program DoUsuario;

var frmChamada: Tform;
lbl1: TLabel;


begin

frmChamada := ConstrutorDeForms('TFrmChamada');
lbl1:= TLabel(frmChamada.FindComponent('Label1'));
lbl1.Caption := 'Uia, dá pra mudar propiedades dos componentes!';

frmChamada.ShowModal;
frmChamada.free;

end.


O pascal script, pelo menos nesta versão e do modo como eu o uso, não tem os operadores de RTTI is e as, por isso usamos a conversão de tipo "forçada" e "não segura" TLabel(frmChamada.FindComponent('Label1')); que não é verificada pelo compilador.

Baixe o fonte desse exemplo, juntamente com o componente.

Espero ter ajudado ^^

sexta-feira, 5 de novembro de 2010

Faça backup do seu blog

Se você tem um blog no blogspot você pode fazer backup de seus posts através de XML, para poder armazenar o conteúdo e até portar para outras plataformas.

Backup é a única medida de segurança barata/viável em um monte de situações, e no caso do blogger é a medida mais segura que você pode tomar.

Uma das ferramentas que pode te ajudar com isso é o blogger backup utility que você pode baixar no codeplex.

Ele é open-source, então você pode baixar sem medo, e alterar a seu gosto.


Na caixa de seleção logo acima escolha "add/edit / remove blogs" para adicionar seu blog na lista. (você pode ter múltiplos).
Depois de colocar seu login e senha simplesmente diga se você quer fazer backup de tudo ou apenas a partir de uma data, e se quer tudo em um arquivo ou um arquivo por post. Simples assim.

Have fun ;)

Existem 1001 maneiras de preparar SINGLETON, invente uma!

Falando-se de POO e Padrões de projeto, não podemos deixar de falar em Singleton. Singleton é um tipo de classe que só pode ser instanciada uma e apenas uma vez. Esse tipo de classe é ideal para objetos que carregam configurações do sistema, objetos que manipulam o horario do sistema, objetos que usamo recursos compartilhados, e por isso necessitam ser serializados ou sincronizados pelas threads, por não poderem executar ao mesmo tempo.
Concrete Factories e Builders também são ótimos exemplos de padrões de projeto que podem ser criados a partir de um singleton. Afinal, se você precisa de um objeto que crie, ou que crie e configure outros objetos para você, seria ideal que todos os objetos criados e montados fossem fabricados do mesmo jeito. Logo, você não deveria ter duas instancias diferentes de um mesmo factory ou de um mesmo buider.
A maneira mais simples de se criar um singleton no Delphi é comona listagem abaixo.
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.


    

Estou criando um objeto, e no momento da criação estou gravando em um atributo privado o horário de criação. Repare que um objeto normal permite que se crie várias instâncias, e cada nova instância vai sendo gravada com o novo horário, mas o objeto singleton permanece o mesmo.
Podemos também alterar o método create do objeto para que grave mensagens únicas em um listbox, num formulário. Se em cada linha do listbox adicionássemos um objeto diferente, o singleton seria adicionado uma única vez.
Veja também que para saber se o objeto já está criado usamos uma variavel estática. Mas ela não é Pública global. Ela não está na seção interface da unit, mas sim na seção implementation. E seu nome começa com "_". Com isso garantimos que ela será invisível às outras units e na própria unit ela não será usada "sem querer" por um programador desavisado.
Seria interessante se fosse possível criar um constructor privado. Só que existe um outro problema: Criar um constructor privado não impede de se chamar o método create da classe. Só que quando isso acontecer será executado o constructor create público da classe ancestral. Isso fará com que a string FDataHora interna da classe, e qualquer outro objeto a ser carregado no constructor não sejam carregados, setados e construidos corretamente. Isso criaria varios erros de access violations.
Construtores privados apenas no Prism e no C#.net :)
Podemos contornar esse problema mantendo o constructor público, mas implementando um contador de objetos. Se ele passar de 1, disparamos uma exception.
O grande problema de se criar uma classe singleton assim é que ela não pode ser facilmente herdada, a não ser que existam outras variáveis para controlar o contador e o conteiner de instancias da classe filha. Para isso todos os métodos devem ser sobrecarregados, ou seja, o singleton deve ser refeito.
Outra desvantagem é que é estritamente necessário criar esse objeto pela class function (método estático) CreateUnico.

É interessante salientar que o CreateUnico é um método de classe estático (construtores também são métodos estáticos) que poderia ser substituido por uma function estática global tradicional, mas resolvemos usar uma class function para usar o "namespace" (nome da classe) e não se distanciar muito da POO. O método CreateUnico também pode ser caracterizado como um factory method se você desejar, pois a função dele é criar um objeto. (construtores também são fábricas degeneradas)






Podemos usar o seguinte exemplo, na listagem abaixo, para testar nosso singleton. Repare que temos dois botões no formulário. (crie um formulário como o da figura)

Um vai criar uma instância de uma classe normal (TObjetoNormal) e o outro vai criar uma instância do singleton (TObjetoUnico) pelo método CreateUnico.
Estamos mostrando, para cada objeto criado, sua data de criação, seu endereço na memória (um integer, deve ser único) e o nome da classe. Monitore memory leaks no seu projeto.
Você verá que a cada instância do objeto normal, mesmo que gravado em uma mesmoa variável, é uma nova instância, com endereço de memória e data diferentes. Mas ao criar o singleton, mesmo que crie 200 vezes, o endereço será sempre o mesmo, e a data também, indicando que a instância é a mesma. Ao fechar o programa as várias instâncias da classe normal serão reportadas como leaks, mas a classe singleton será dstruida no finalization.
O controle de instâncias é feito por uma variável estática privada e por um contador de referências. Caso passar de 1 ou a variável do tipo singleton não for nil é disparada uma excessão (no método create). O método CreateUnico verifica se já está criada (variável _ObjetoUnico <> de nil e contador de referencias exatamente igual a 1). Se já estiver criada retorna a mesma, caso contrário cria armazena na hora de retornar e retorna (lazy binding).



codigo:

procedure TfrmUmaInstancia.btNormalClick(Sender: TObject);
var FObjetoNormal: TObjetoNormal;
begin

  //cuidado, você está prestes a criar varias instancias de
  //um objeto colocando na mesma variável, perdendo
  //posteriormente a referencia aos objetosanteriores
  //você não poderá destruir os objetos anteriores, causando
  //um memory leak
  FObjetoNormal := TObjetoNormal.Create;

  //mostrando a classe do objeto, hora de criação e endereço do objeto
  //veja que é sempre diferente
  ShowMessage('Nome da classe: ' + FObjetoNormal.ClassName+ #13#10 +
    'Hora de Criação: ' + FObjetoNormal.GetDataHora + #13#10 +
    'Endereço na memória: ' + IntToStr(Integer(FObjetoNormal))
  );
end;

procedure TfrmUmaInstancia.btUnicoClick(Sender: TObject);
var FObjetoUnico: TObjetoUnico;
begin

  //veja que é possivel executar o create
  //Mas dessa forma não é garantido que o objeto seja unico
  //para garantir que seja único é imprescindível o uso do método  CreateUnico

  FObjetoUnico := TObjetoUnico.CreateUnico;

  //mostrando a classe do objeto, hora de criação e endereço do objeto
  //veja que é sempre igual
  ShowMessage('Nome da classe: ' + FObjetoUnico.ClassName+ #13#10 +
    'Hora de Criação: ' + FObjetoUnico.GetDataHora + #13#10 +
    'Endereço na memória: ' + IntToStr(Integer(FObjetoUnico))
  );


end;

    




Repare que esse singleton que fizemos pode ser refeito em Lazarus sem grandes problemas. Mas seria difícil usar herança com ele. Para que uma classe derivada funcione corretamente sem misturar instâncias com a classe ancestral, sem gravar uma instância nas variáveis estáticas locais da unit ancestral e sem usar métodos indevidos da classe ancestral, como o método que incrementa a contagem, obrigatoriamente deve-se reimplementar todos os métodos da classe filha sem invocar o inherited.
Criar um descendente para este singleton não é impossível, e pode ser exemplificado pelo código abaixo:


unit uSingletonDerivado;

interface

uses
  uSingleton, SysUtils, DateUtils, Windows, dialogs;

type

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

procedure VerificaObjetoUnicoDerivadoCriado;

implementation

var
  _ObjetoUnico: TObjetoUnicoDerivado;
  _Contador: Integer = 0;

procedure VerificaObjetoUnicoDerivadoCriado;
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;

{ TObjetoUnicoDerivado }

constructor TObjetoUnicoDerivado.Create;
begin

    if (_Contador = 0) then
    begin
      //aqui há o perigo de criar mais um objeto _ObjetoUnico que perderá sua referência e causará leak
      //ou, no mínimo, incrementar o seu contador
      //experimente descomentar o inherited para ver o que acontece
      //inherited Create;
      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 TObjetoUnicoDerivado.CreateUnico: TObjetoUnicoDerivado;
begin
  //aqui não se pode usar inherited porque senão trará uma instância do objeto ancestral
  if _ObjetoUnico = nil then
    _ObjetoUnico := TObjetoUnicoDerivado.Create;
  Result := _ObjetoUnico;
end;

destructor TObjetoUnicoDerivado.Destroy;
begin
  _ObjetoUnico := nil;
  InterlockedDecrement(_Contador);
  //aqui há o perigo de destruir um objeto _ObjetoUnico que pode estar em uso na unit do ancestral
  //expedrimente descomentar o inherited para ver o que acontece
  //inherited;
end;

function TObjetoUnicoDerivado.GetDataHora: string;
begin
  //aqui não haveria perigo de chamar inherited
  //mas se você não chamar o inherited do create então  o campo privado
  //FDataHora ficará vazio (seria usado o do ancestral)
  //por isso é melhor refazer
  //isso é polimorfismo :)
  Result := 'Data desta classe nova: ' +  FDataHora;;
end;

initialization
  _ObjetoUnico := nil;
  _Contador := 0;

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


end.

    


E testado assim:

procedure TfrmUmaInstancia.btDerivadoClick(Sender: TObject);
var FObjetoUnico: TObjetoUnicoDerivado;
begin
  //veja que é possivel executar o create
  //Mas dessa forma não é garantido que o objeto seja unico
  //para garantir que seja único é imprescindível o uso do método  CreateUnico

  FObjetoUnico := TObjetoUnicoDerivado.CreateUnico;

  //mostrando a classe do objeto, hora de criação e endereço do objeto
  //veja que é sempre igual
  ShowMessage('Nome da classe: ' + FObjetoUnico.ClassName+ #13#10 +
    'Hora de Criação: ' + FObjetoUnico.GetDataHora + #13#10 +
    'Endereço na memória: ' + IntToStr(Integer(FObjetoUnico))
  );

end;
    


Uma outra maneira muito elegante de criar singletons em Delphi muito mais facilmente "herdáveis" seria cria-los usando class properties e class vars estáticas (e privadas) assim a classe filha poderia herdar essas caracteristicas já adaptadas para a nova classe, sem misturar instâncias.
A desvantagem é que dessa maneira ele não funcionaria com o lazarus, pelo menos não enquando o lazarus não suportar propriedades e campos de classe.
Na parte 2 desse post veremos como implementar com class vars no Delphi XE.

Você pode fazer download desse exemplo aqui. O uso da FastMM4 vai depender de você estar usando Delphi 7 ou não. Este exemplo foi compilado em Delphi XE.

Até + :)



Links úteis, leia todos ;)



Existem 1001 maneiras de preparar SINGLETON, invente uma! - Parte 1

http://blog.vitorrubio.com.br/2010/11/existem-1001-maneiras-de-preparar.html

Existem 1001 maneiras de preparar SINGLETON, invente uma! - Parte 2

http://blog.vitorrubio.com.br/2011/01/existem-1001-maneiras-de-preparar.html

Existem 1001 maneiras de preparar SINGLETON, invente uma! - Parte 3

http://blog.vitorrubio.com.br/2011/02/existem-1001-maneiras-de-preparar.html

Existem 1001 maneiras de preparar SINGLETON, invente uma! - Parte 4
http://blog.vitorrubio.com.br/2011/02/existem-1001-maneiras-de-preparar_08.html

Criando uma classe singleton verdadeira em delphi

http://www.comofazertudo.com.br/computadores-e-internet/criando-uma-classe-singleton-verdadeira-em-delphi

Creating a real singleton class in Delphi 5

http://edn.embarcadero.com/article/22576

Introdução: Singleton - Design Pattern Delphi - Parte 1

http://www.devmedia.com.br/post-17889-Introducao--Singleton-Design-Pattern-Delphi-Parte-1.html

Tentativa de Singleton usando Delphi

http://www.marcosdellantonio.net/2006/12/01/tentativa-de-singleton-usando-delphi/

Implementing the Singleton pattern in delphi

http://www.delphi3000.com/articles/article_1736.asp?SK=

Essa é uma abordagem nova que eu nunca imaginei:

http://stackoverflow.com/questions/1409593/creating-a-singleton-in-delphi-using-the-new-features-of-d2009-and-d2010

Class (, Static, or Shared) Constructors (and Destructors)

http://blogs.embarcadero.com/abauer/2009/09/03/38898

Design Patterns in Delphi

http://delphi.about.com/od/oopindelphi/a/aa010201a.htm

No forum antigo:

Tópico no forum devmedia sobre singleton

no forum novo:

http://www.devmedia.com.br/forum/viewtopic.asp?id=374670

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)