Neste exemplo utilizamos a classe TObjectList para lidar com uma coleção de objetos. Essa é uma classe genérica e funciona de forma semelhante à TList, porém ela se encarrega de liberar seus itens da memória quando é destruída. Isso evita o vazamento de memória (memory leak) em nossas aplicações, que ocorre quando itens adicionados à coleção não são liberados.
Para demonstrar o uso desse tipo de coleção criamos uma estrutura orientada a objetos na qual temos duas classes: TVenda e TVendaItem. Além disso temos um formulário para fazer a manipulação dos objetos dentro da nossa coleção. Esses elementos estão organizados em units, da seguinte forma:
- uVenda: unit contendo a classe TVenda;
- uVendaItem: unit contendo a classe TVendaItem;
- uFrmListaObjetos: unit contendo o formulário principal.
Cada uma dessas classes será explicada mais adiante.
Classe TVenda
Nesta classe temos três propriedades: IDVenda (Integer), Data (TDateTime) e ListaVendaItem(TObjectList<TVendaItem>). Além disso, implementamos seu construtor, destrutor e o método AdicionarVendaItem.
Na unit uVenda, o primeiro ponto a ser observado é que referenciamos na seção uses a unit System.Generics.Collections, pois é nela que está declarada a classe TObjectList:
uses
System.Generics.Collections, System.SysUtils, uVendaItem;
Note que também estamos referenciando a unit uVendaItem, na qual declaramos a classe TVendaItem, e a System.SysUtils, na qual encontra-se a função EncodeDate que será usada mais adiante.
Na sequência temos a definição da classe TVenda, como vemos abaixo:
01 TVenda = class
02 private
03 FIDVenda: Integer;
04 FData: TDateTime;
05 FListaVendaItem: TObjectList<TVendaItem>;
06 { private declarations }
07 protected
08 { protected declarations }
09 public
10 { public declarations }
11 property IDVenda: Integer read FIDVenda write FIDVenda;
12 property Data : TDateTime read FData write FData;
13 property ListaVendaItem: TObjectList<TVendaItem> read FListaVendaItem
14 write FListaVendaItem;
15 constructor Create;
16 destructor Destroy; override;
17 procedure AdicionarVendaItem(pProduto: String);
18 published
19 { published declarations }
20 end;
Linhas 3 a 5: Atributos privados que representam o id, a data e os itens da venda, respectivamente;
Linhas 11 a 14: Propriedades públicas que encapsulam o acesso aos atributos privados. Note que cada propriedade é responsável por ler e escrever valores em cada um dos atributos privados;
Linhas 15 e 16: Declaração do construtor e do destrutor da classe, respectivamente;
Linha 17: Declaração do método responsável por adicionar um novo item a essa venda.
O método AdicionarVendaItem é responsável por instanciar um objeto do tipo TVendaItem dentro da propriedade FListaVendaItem e preencher suas propriedades de forma estática, da seguinte forma:
01 procedure TVenda.AdicionarVendaItem(pProduto: String);
02 var
03 I: Integer;
04 begin
05 FListaVendaItem.Add(TVendaItem.Create);
06 I := FListaVendaItem.Count -1;
07 FListaVendaItem[I].IDVendaItem := I;
08 FListaVendaItem[I].IDVenda := FIDVenda;
09 FListaVendaItem[I].Produto := pProduto;
10 end;
Linha 3: Aqui declaramos uma variável que será responsável por controlar o índice do item que está sendo adicionado a coleção;
Linha 5: Utilizamos o método Add da coleção, passando para ele um novo objeto do tipo TVendaItem;
Linha 6: Nesta linha obtemos o índice do último item da coleção, ou seja, aquele que acabou de ser adicionado. Precisamos desse índice para referenciar esse objeto nas próximas linhas;
Linhas 7 a 9: Preenchemos o item recém-criado.
Mais abaixo, na implementação do método Create da classe TVenda, estamos atribuindo valores iniciais aos fields e também instanciando a nossa coleção de itens:
01 constructor TVenda.Create;
02 begin
03 inherited;
04 FIDVenda := 0;
05 FData := EncodeDate(1900,1,1);
06 FListaVendaItem := TObjectList<TVendaItem>.Create;
07 end;
Linha 3: Nesta linha garantimos que será executada uma chamada ao método Create da classe ancestral de TVenda, nesse caso TObject;
Linha 4: Atribuímos o valor 0 ao field FIDVenda;
Linha 5: Utilizando a função EncodeDate setamos um valor padrão para a data da nossa venda;
Linha 6: Neste momento estamos instanciando a nossa coleção de itens, para adicionarmos itens a ela posteriormente.
Por fim, na implementação do método Destroy da classe TVenda precisamos liberar a nossa coleção da memória e ela se encarrega de liberar todos os itens que contém:
01 destructor TVenda.Destroy;
02 begin
03 FreeAndNil(FListaVendaItem);
04 inherited;
05 end;
Linha 3: Nesta linha liberamos a nossa coleção da memória e consequentemente todos os seus itens;
Linha 4: Aqui garantimos que será executada uma chamada ao Destroy da classe ancestral de TVenda, ou seja, TObject.
Classe TVendaItem
A classe TVendaItem possui apenas três propriedades, a fim de simular de forma simplificada os itens e uma venda. Sua definição pode ser vista abaixo:
01 TVendaItem = class
02 private
03 FIDVendaItem: Integer;
04 FIDVenda: Integer;
05 FProduto: String;
06 { private declarations }
07 protected
08 { protected declarations }
09 public
10 { public declarations }
11 property IDVendaItem: Integer read FIDVendaItem write FIDVendaItem;
12 property IDVenda: Integer read FIDVenda write FIDVenda;
13 property Produto: string read FProduto write FProduto;
14 published
15 { published declarations }
16 end;
Linhas 3 a 5: atributos privados que representam o id do item, o id da venda e o nome do produto, respectivamente;
Linhas 11 a 13: propriedades públicas que encapsulam o acesso aos atributos privados. Note que cada propriedade é responsável por ler e escrever valores em cada um dos atributos privados.
Form TFrmListaObjetos
Neste formulário, cuja interface pode ser vista na Figura 1, usamos as classes definidas anteriormente para simular a criação de uma venda, a adição de itens e sua posterior listagem em um controle do tipo TMemo:
Na seção private desse form declaramos uma variável do tipo TVenda, da seguinte forma:
private
Venda: TVenda;
Em seguida é necessário instanciar esse objeto, o que foi feito no construtor do form, para garantir que após o form ser criado já poderemos usar esse objeto normalmente:
procedure TFrmListaObjetos.FormCreate(Sender: TObject);
begin
Venda := TVenda.Create;
end;
A partir daí já podemos preencher essa venda e inserir itens. Para isso utilizamos o evento OnClick do botão “Cadastrar Venda”:
procedure TFrmListaObjetos.BtnCadastrarVendaClick(Sender: TObject);
01 begin
02 Venda.IDVenda := 1;
03 Venda.Data := Now;
04
05 Venda.ListaVendaItem.Clear;
06 MmDadosVenda.Clear;
07
08 Venda.AdicionarVendaItem('Sony Vaio XR8472');
09 Venda.AdicionarVendaItem('Dell Vostro');
10 Venda.AdicionarVendaItem('HP I7');
11
12 BtnListarVenda.Enabled := True;
13 BtnCadastrarVenda.Enabled := False;
14 end;
Linhas 2 e 3: Preenchemos as propriedades do objeto Venda;
Linhas 5 e 6: Limpamos a coleção de itens da venda e o memo;
Linhas 8 a 10: Adicionamos três itens à venda;
Linhas 12 e 13: Habilitamos o botão de listar dados da venda e desabilitamos o botão de cadastrar nova venda.
Na sequência utilizamos o botão “Listar Venda” para imprimir no memo os dados da venda e seus itens. Para isso alteramos seu evento OnClick, da seguinte forma:
01 procedure TFrmListaObjetos.BtnListarVendaClick(Sender: TObject);
02 var
03 VendaItem: TVendaItem;
04 begin
05 MmDadosVenda.Lines.Add('IDVenda: ' + IntToStr(Venda.IDVenda));
06 MmDadosVenda.Lines.Add('Data: ' + FormatDateTime('dd/mm/yyyy', Venda.Data));
07
08 MmDadosVenda.Lines.Add('');
09 MmDadosVenda.Lines.Add('<<Lista de produtos>>');
10
11 for VendaItem in Venda.ListaVendaItem do
12 MmDadosVenda.Lines.Add('Produto : ' + VendaItem.Produto);
13
14 BtnListarVenda.Enabled := False;
15 BtnCadastrarVenda.Enabled := True;
16 end;
Linhas 5 e 6: Adicionamos ao memo os dados do id e data da venda;
Linhas 8 e 9: Adicionamos ao memo duas linhas para separar os dados da venda dos seus itens;
Linhas 11 e 12: Percorremos os itens da venda usando um laço for e imprimimos no memo os dados de cada um;
Linhas 14 e 15: Desabilitamos o botão de listagem e habilitamos o botão de cadastrar venda.
Por fim, no método Destroy do form liberamos o objeto Venda da memória:
procedure TFrmListaObjetos.FormDestroy(Sender: TObject);
begin
FreeAndNil(Venda);
end;