sexta-feira, 3 de outubro de 2008

Factory Method

Suponhamos que temos uma aplicação que é utilizada por vários clientes, mas cada um quer personalizar a "sua" aplicação. Vamos dizer que o objeto da personalização é uma consulta: A consulta de produtos.

O uso do padrão Factory Method poderia nos ajudar bastante a ter uma aplicação limpa e bem estruturada. Vejamos como.

Sem o Factory Method

Sem o padrão Factory Method, nossa aplicação muito provavelmente ficaria lotada de desvios no fluxo para atender a necessidade de cada cliente:



unit Form1;

type
TForm1 = class(TForm)
// ...
end;

implementation

uses fmConProdutos1, fmConProdutos2, fmConProdutos3;

procedure TForm1.mnuConProdutosClick(Sender: TObject);
var
f: TForm;
begin
if Cod_Cli_Sistema = CS_CLIENTE1 then
Application.CreateForm(TfrmConProdutos1, f)
else if Cod_Cli_Sistema = CS_CLIENTE2 then
Application.CreateForm(TfrmConProdutos2, f)
else if Cod_Cli_Sistema = CS_CLIENTE3 then
Application.CreateForm(TfrmConProdutos3, f);
end;


Noutra parte do sistema, também precisamos chamar o mesmo form da consulta de produtos. E agora, como vamos proceder?



unit Form2;

type
TForm2 = class(TForm)
// ...
end;

implementation

uses fmConProdutos1, fmConProdutos2, fmConProdutos3;

procedure TForm2.AbrirConsultaProdutos;
var
f: TForm;
begin
if Cod_Cli_Sistema = CS_CLIENTE1 then
begin
Application.CreateForm(TfrmConProdutos1, f);
TfrmConProdutos1(f).AlgumMetodo();
end
else if Cod_Cli_Sistema = CS_CLIENTE2 then
begin
Application.CreateForm(TfrmConProdutos2, f);
TfrmConProdutos2(f).AlgumMetodo();
end
else if Cod_Cli_Sistema = CS_CLIENTE3 then
begin
Application.CreateForm(TfrmConProdutos3, f);
TfrmConProdutos3(f).AlgumMetodo();
end;
end;



Como se pode perceber, o código fica difícil de entender (talvez não neste caso, porque se trata de um pequeno exemplo, mas numa aplicação grande e complexa, com certeza ficaria) e difícil de manter, visto que pra cada customização precisaríamos procurar por toda a aplicação os lugares onde eles são criados pra adicionar a nova classe. O risco de introduzir bugs é grande. Ademais, criamos um forte acoplamento entre as classes tornando dificil o reaproveitamento dos módulos e aumentando a possibilidade que a alteração em uma ocasione problemas em outras. É bem provável que o seu cliente diga algo parecido com isto, após a implantação da versão nova: "Antes isto aqui funcionava, agora não está mais funcionando".

Por outro lado, o uso dos padrões de projeto podem reduzir significativamente os problemas apresentados. São soluções robustas elaboradas por desenvolvedores experientes e que já foram testadas no mundo todo. Então por que não utilizá-los?

Com o Factory Method

O que precisamos fazer é uma fabrica de objetos que instancie a classe desejada mas que a minha aplicação não precise saber nada sobre a classe específica instanciada. Como conseguir isto?

Precisamos criar uma classe abstrata da qual todas as consultas de produto fossem herdadas e ela seria a única classe que a minha aplicação como um todo conheceria. As outras, herdadas dela, só seriam conhecidas pela fábrica.


unit fmConProdutosAbs;

uses SysUtils, Windows, Classes, Forms, Variants;

type
TfrmConProdutosAbs = class(TForm)
private

public
{alguns metodos aqui}
end;

implementation



Agora, iremos criar a nossa fabrica:


unit uConProdutosFactory;

uses SysUtils, Windows, Classes, Forms, Variants, fmConProdutosAbs;

type
TConProdutosFactory = class(TInterfacedObject, IConProdutosFactory)
private

public
function CriarConProdutos(Cod_Cliente_Sistema: Integer): TfrmConProdutosAbs;
end;

implementation

uses fmConProdutos1, fmConProdutos2, fmConProdutos3;

function TConProdutosFactory.CriarConProdutos(Cod_Cliente_Sistema: Integer): TfrmConProdutosAbs;
begin
if Cod_Cli_Sistema = CS_CLIENTE1 then
Application.CreateForm(TfrmConProdutos1, Result)
else if Cod_Cli_Sistema = CS_CLIENTE2 then
Application.CreateForm(TfrmConProdutos2, Result)
else if Cod_Cli_Sistema = CS_CLIENTE3 then
Application.CreateForm(TfrmConProdutos3, Result)
else
raise Exception.Create('Consulta de Produto não implementada para o cliente especificado');
end;

initilization
ConProdutosFactory := TConProdutosFactory.Create;
finalization
ConProdutosFactory := nil;
end.



Agora pense um pouco: Se colocarmos uConProdutosFactory no uses de Form1 ou Form2, estarei criando um vínculo indireto com fmConProdutos1, fmConProdutos2, fmConProdutos3, além de fmConProdutosAbs, aumentadando o acoplamento. Isto não seria legal.

Então vamos fazer o seguinte: modificamos a unit fmConProdutosAbs para acomodar a variável global ConProdutosFactory. Fazemos isto nela porque ela já vai ter que ser referenciada mesmo pelas outras units! A unit modificada fica assim:


unit fmConProdutosAbs;

uses SysUtils, Windows, Classes, Forms, Variants;

type
TfrmConProdutosAbs = class;

IConProdutosFactory = interface
['{C8E329B0-BCDD-43A3-A5D6-8366055901CA}']
function CriarConProdutos(Cod_Cliente_Sistema): TfrmConProdutosAbs;
end;

TfrmConProdutosAbs = class(TForm)
private

public
{alguns metodos aqui}
// as classes filhas terão de sobrescrever este método
procedure AlgumMetodo(); virtual; abstract;
end;

var
ConProdutosFactory: IConProdutosFactory;

implementation



O Resultado final

Agora veja como fica as unit Form1 e Form2 usando a fábrica recém-criada:


unit Form1;

type
TForm1 = class(TForm)
// ...
end;

implementation

uses fmConProdutosAbs;

procedure TForm1.mnuConProdutosClick(Sender: TObject);
var
f: TfrmConProdutosAbs;
begin
f := ConProdutosFactory.CriarConProdutos(Cod_Cli_Sistema);
end;



unit Form2;

type
TForm2 = class(TForm)
// ...
end;

implementation

uses fmConProdutosAbs;

procedure TForm2.AbrirConsultaProdutos;
var
f: TfrmConProdutosAbs;
begin
f := ConProdutosFactory.CriarConProdutos(Cod_Cli_Sistema);
f.AlgumMetodo();
end;


Veja que agora, o acoplamento foi bastante reduzido: só colocamos no uses fmConProdutosAbs! o código ficou mais limpo e fácil de manter. Se quisermos adicionar uma nova classe herdada de TfrmConProdutosAbs só precisamos criar a classe e modificar a fábrica.

Imagine outra aplicação: Você poderia colocar os seus forms em Packages e fazer a sua aplicação modularizada.

Por enquanto é só. Espero que tenham gostado.
 

web analytics software