28 de setembro de 2010

Design Patterns com Delphi - Iterator - Parte II

Tomando como o base a situação descrita no post anterior - sobre o Design Pattern comportamental Iterator - eu mostro aqui como estruturar classes em Delphi para implementar aquele exemplo.

Apenas para manter a referência, reproduzo abaixo novamente o Diagrama de Classes usado naquele post que representa a solução proposta.
Diagrama UML para o padrão Iterator

O ponto focal da solução é a classe tida como Client, isto é, aquela que fará uso do Iterator, pois ela determina algumas características básicas que afetarão o desenho do sistema como um todo. Por exemplo, espera-se que a classe TWBrowseForm do diagrama acima seja capaz de recuperar estruturas de cada lista e alimentar com elas um Grid na tela - talvez até mesmo definindo novas classes com adoção de um padrão MVC (Model-View-Controller). Veja no quadro abaixo uma proposta para a classe TWBrowseForm.
TWBrowseForm = class (TForm)
_Grid : TStringGrid;
{ ... }
protected
_Dados: TWCollection;

public { ... }
procedure SetCollection (ADados: TWCollection);
procedure ExibirRegistros;
end;

{ ... }
procedure TWBrowseForm.SetCollection (ADados: TWCollection);
begin
_Dados := ADados;
end;

procedure TWBrowseForm.ExibirRegistros;
var lIterator : TWIterator;
lBObj : TWBusinessObj;
begin
{ ... }
lIterator := _Dados.CreateIteratorObj;
lIterator.Primeiro;

while (not lIterator.FimColl) do
begin
lBObj := lIterator.CurReg;
lBObj.ExibeNoGrid(_Grid);

lIterator.Proximo;
end;
{ ... }
end;

A classe é uma herança de um Form na qual foi incluido um Grid para exibir os registros recuperados. Os registros que serão exibidos, por sua vez, estão armazenados num coleção nomeada como _Dados e cujo tipo é a classe genérica TWCollection. Tudo o que o nosso Form precisa saber é que essa coleção consegue construir um iterator para nos permitir navegar pelos registros nela contidos. Por isso, a classe de coleção é definida como abstrata, forçando todas as coleções reais a disponibilizarem sua própria versão de Iterator.
TWCollection=class
public
function CreateIteratorObj:TWIterator;virtual;abstract;
end;

TWColCarrinhoCompras=class(TWCollection)
protected
_Lista : TList;
public
procedure AddItem (AItem: TWBusinessObj);
function CreateIteratorObj:TWIterator;override;
end;


TWColMovtosEstoque=class(TWCollection)
protected
_Query: TAdoQuery;
public
function CreateIteratorObj:TWIterator;override;
end;

Note que cada coleção real neste exemplo está preparada para usar uma forma diferente de organização das informações - uma usará diretamente uma lista interna enquanto a outra deverá ler as informações no banco de dados em algum momento. Veja também que o método que cria o iterator não é abstrato nelas - apenas na base. Isto significa que elas terão que providenciar codifição para esse método, cada uma respeitando sua própria organização interna.

A construção dos iterators também segue uma regra bem definida, estabelecida através de uma classe base abstrata que introduz os métodos para navegação. Esses métodos deverão ser obrigatoriamente implementados pelos iterators reais, em associação com as respectivas coleções.
TWIterator=class
public
procedure Primeiro;virtual;abstract;
procedure Proximo;virtual;abstract;
function FimColl : Boolean;virtual;abstract;
function CurReg : TWBusinessObj;virtual;abstract;
end;

TWItCarrinho=class(TWIterator)
protected
_CurIdx : Integer;
_Col: TWColCarrinhoCompras;
public
Constructor Create (ACol: TWColCarrinhoCompras);

procedure Primeiro;override;
procedure Proximo;override;
function FimColl : Boolean;override;
function CurReg : TWBusinessObj;override;
end;

TWItMovtosPorData=class(TWIterator)
protected
_Col: TWColMovtosEstoque;
public
Constructor Create (ACol: TWColMovtosEstoque);

procedure Primeiro;override;
procedure Proximo;override;
function FimColl : Boolean;override;
function CurReg : TWBusinessObj;override;
end;

A classe TWBusinessObj que aparece no código acima é o tipo de dado que esse Iterator em particular pode manipular e utilizá-la é uma decisão tomada durante a fase de projeto. Dependendo do uso que se pretente, poderíamos ter optado pela classe mais básica do Delphi - o TObject -, propiciando que qualquer classe seja utilizada na solução. O TWBusinessObj não foi incluído no diagrama UML do Iterator porque não é parte da solução do Pattern em si. Ele aqui representa objetos de negócio que têm a capacidade de se desenhar como uma linha num grid. O quadro acima também reforça a ideia de que os iterators reais estão intimamente conectados à coleção na qual eles estão associados. Isso ocorre porque eles precisam conhecer a forma com que a coleção está estruturada para poder extrair-lhe os registros. Veja a implementação para o carrinho de compras:
procedure TWItCarrinho.Primeiro;
begin
_CurIdx := 0;
end;

procedure TWItCarrinho.Proximo;
begin
Inc (_CurIdx);
end;

function TWItCarrinho.FimColl : Boolean;
begin
Result := (_CurIdx >= _Col.GetLista.Count);
end;

function TWItCarrinho.CurReg : TWBusinessObj;
begin
if (_CurIdx >= 0) And (_CurIdx < _Col.GetLista.Count) then
Result := TWBusinessObj(_Col.GetLista.Items[_CurIdx])
else
Result := Nil;
end;

Para ligar isso tudo, é preciso um contexto onde se tenha uma coleção que deva ser exibida numa tela com grid - ainda que isso não faça parte do design pattern em si.
var lForm : TWBrowseForm;
begin
lForm := TWBrowseForm.Create (Self);
lForm.SetCollection (_Colecao);
lForm.ExibirRegistros;
{ ... }

No exemplo acima, a variável _Colecao representa uma coleção qualquer instanciada em outro ponto, conforme as necessidades específicas do sistema. Ela também teve a oportunidade de ser alimentada diretamente com dados (no caso do carrinho de compras) ou de ter seus filtros estabelecidos (para as queries nas coleções de movimentos de estoque ou de cadastro de clientes).

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.