quarta-feira, 3 de dezembro de 2008

Reference Counting em Forms, Datamodules, etc

O uso de interfaces é muito útil para evitar um alto acoplamento no sistema. Mas como implementar interfaces em Datamodules, Forms ou Componentes que não fazem a contagem de referência, sem deixar Memory Leaks pelo sistema todo?

É simples: basta fazer algumas pequenas alterações no código. Vamos supor que queremos fazê-lo num datamodule chamado Cliente:


type
TCliente = class(TDataModule, IInterface, ICliente)
dspDados: TDataSetProvider;
cdsDados: TClientDataSet;
qryDados: TSQLQuery;
private
FRefCount: Integer;
FOwnerIsComponent: Boolean;
protected
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure AfterConstruction; override;
class function NewInstance: TObject; override;
// Outros métodos e propriedades
end;

implementation

{$R *.dfm}

procedure TCliente.AfterConstruction;
begin
inherited;
FOwnerIsComponent := Assigned(Owner) and (Owner is TComponent);
InterlockedDecrement(FRefCount);
end;

function TCliente._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount)
end;

function TCliente._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if (Result = 0) and not FOwnerIsComponent then
Destroy;
end;

class function TCliente.NewInstance: TObject;
begin
Result := inherited NewInstance;
TCliente(Result).FRefCount := 1;
end;


Você deve estar se perguntando: "Por que cargas d'agua eu deveria fazer isso?". Bem, desde muito tempo eu trabalho com alguns sistemas de legado nos quais eu resolvi fazer um híbrido de OO/RAD. Criei datamodules cujo nome é o nome da classe (ex.: Cliente). O datamodule contém um clientdataset que faz o acesso aos dados no DB. Ao mesmo tempo, o DM implementa uma interface (digamos, ICliente) e para que ele trabalhe com reference counting eu implementei algo parecido. Assim, eu posso fazer:


var
Cliente: ICliente;
begin
// Não preciso conhecer a classe concreta. Apenas a interface precisa ser conhecida
// Isso ajuda a criar aplicativos de baixo acoplamento e modularizados
Cliente := ClienteFactory.CriarCliente;
Cliente.ConsultarPeloCodigo('0001');
ShowMessage(Cliente.Nome)
// Não preciso dar o Free!
end;


Mas quando usado como componente eu tenho que destruí-lo manualmente:


var
Cliente: TCliente;
begin
Cliente := TCliente.Create(nil, Connection)
try
Cliente.ConsultarPeloCodigo('0001');
ShowMessage(Cliente.Nome);
finally
Cliente.Free;
end;
end;


Assim, eu mantenho o comportamento padrão de um datamodule (sem ref. Counting) e ao mesmo tempo, quando desejado, posso usar o Ref. Counting.

Com aplicativos de baixo acoplamento, a reutilização de código aumenta. A mesma idéia pode ser utilizada em Forms. Mas com os forms precisa-se ter um cuidado especial: Se você usa uma variável local para criá-lo, ao final do método, adeus form!

Use sua criatividade. E eu tenho certeza que vc vai encontrar muita utilidade para essa dica.

Eu peguei essa idéia de TXmlDocument. Ele faz exatamente a mesma coisa: quando usado como componente, não usa o mecanismo de reference counting, mas quando usado através da interface IXmlDocument, usa reference counting.

2 comentários:

OgreT disse...

"é simples" é?! kkkkkkk se umeno tu botasse um UMLzim fiva mais "simples" ainda!

Marcos George disse...

kkkkkkkkkk
Eu achei... Tu num achou nao?

 

web analytics software