10 de agosto de 2009

Design Patterns com Delphi : Builder - parte 2

Neste post, vou mostrar um exemplo de como mapear classes em Delphi para implementar o Design Pattern Builder. No post anterior, dei um overview a respeito do funcionamento desse padrão e as situações onde ele se aplica. Citei como exemplo a geração do documento para o SPED Fiscal, cuja criação deve ser feita em etapas bem conhecidas, representadas por tipos de Registros, o que torna tal criação apropriada para uso do padrão Builder.

A criação dos registros neste caso consiste, de uma forma simplificada, em incluir um cabeçalho, uma série de registros comuns, alguns registros específicos para o ramo de atividade da empresa emissora do documento de SPED e, por fim, um registro de encerramento. Veja uma parte do diagrama de classes para essa situação:

Diagrama UML para Builder - SPED
Neste diagrama, a classe TWAbstractBuilderSPED é o Builder abstrato, isto é, ele apenas diz quais são os métodos que um Builder para registros do SEPD deve ter. Em Delphi, este tipo de classe é codificada usando a palavra chave abstract depois do nome de cada função tida como abstrata.
type TWAbstractBuilderSPED=class
public
function BuildHeader: TWRegistro;virtual;abstract;
function BuildRegistrosComuns: TWRegistro;virtual;abstract;
function BuildRegistrosExtras: TWRegistro;virtual;abstract;
function BuildTail: TWRegistro;virtual;abstract;
end;
Por ser abstrata, esta classe não deve ser instanciada. É preciso criar as heranças concretas que implementarão as funções introduzidas acima. Essas classes que herdam do AbstractBuilder são denominadas Concrete Builder. No diagrama eu só representei 2: uma para empresas que não tenham registros especiais (TWBuilderSPEDComum) e outra para indústrias Farmacêuticas (TWBuilderSPEDMedicam) mas deve haver uma dessas classes para cada ramo que tiver registros do SPED específicos. Exemplificando com um trecho do TWBuilderSPEDMedicam:
TWBuilderSPEDMedicam=class (TWAbstractBuilderSPED)
public
function BuildHeader: TWRegistro;override;
function BuildRegistrosExtras: TWRegistro;override;
end;

implementation

function TWAbstractBuilderSPED.BuildHeader: TWRegistro;
begin
Result := TWRecHeader.Create;
end;

function TWAbstractBuilderSPED.BuildRegistrosExtras: TWRegistro;
begin
Result := TWRecMedicamentos.Create;
end;
Cada classe concreta dessas é responsável por criar as "etapas" corretas para construir o objeto ao qual dizem respeito. Como poderão existir diversas dessas classes, um sistema normalmente usa o Design Pattern Factory Method ou Abstract Factory para decidir qual delas instanciar.

Depois de ter o Builder correto criado, é responsabilidade do TWDiretorSPED montar o objeto final - o Product, segundo a nomenclatura do Design Pattern - na ordem correta.
function TWDiretorSPED.Construct : TWDoctoSPED;
var builder : TWAbstractBuilderSPED;
Registro : TWRegistro;
begin
builder := GetBuilderSPEDFromFactory (_RamoAtividade);

Result := TWDoctoSPED.Create;

Registro := builder.BuildHeader;
Result.Add (Registro);
Registro := builder.BuildRegistrosComuns;
Result.Add (Registro);
Registro := builder.BuildRegistrosExtras;
Result.Add (Registro);
Registro := builder.BuildTail;
Result.Add (Registro);
end;
A função Construct é o resumo do padrão Builder. Nela podemos ver que uma instância concreta do Builder é criada por uma Factory e lançada numa variável do tipo do Abstract Builder de modo que podemos criar as partes separadamente através de polimorfismo. Montar uma Factory para obtenção da instância correta do Builder não é obrigatório mas é bastante comum. Por fim, o Builder obtido é usado para instanciar cada uma das etapas (os registros) que compõem o produto final. O produto final aqui é uma instância da classe TWDoctoSPED, que é montada registro a registro e retornada pela função Construct. O sistema, então, pode usar o documento SPED do jeito que for necessário.

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.