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:

Layout do form principal
Figura 1. Layout do form principal

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;