Olá, leitores, como vão?
Conforme prometido, hoje inicio a temporada de artigos sobre Design Patterns! Não serão sequenciais, já que eventualmente postarei artigos sobre outros assuntos. Mesmo assim, é com grande satisfação que dou este primeiro passo.
Para inaugurar, apresento-lhes o Abstract Factory! Confira o artigo e aprenda um pouco mais sobre este padrão!
Â
Pessoal, para evitar que o artigo fique extenso, vou dispensar a explicação sobre o que são Design Patterns e seus benefÃcios, ok? Acredito que a maioria (senão todos) já conhecem ou ao menos já ouviram falar destes padrões. No entanto, caso for necessário, posso elaborar um artigo abordando o conceito de Design Patterns de forma geral. Basta deixar um comentário! 🙂
Pois bem, sabemos que uma das caracterÃsticas que “poluem” o código é o excesso de estruturas condicionais, como o IF. Não digo que um projeto não deve ter essas estruturas, mas justifico que muitas delas são desnecessárias, uma vez que podem ser substituÃdas pelo padrão que vou apresentar neste artigo, mantendo o código mais limpo.
Â
André, exemplifique um cenário de desvantagens do uso de estruturas IF.
Claro! Considere um sistema de loja de eletrônicos, no qual o usuário seleciona uma marca (como Dell ou Apple) e consulta os produtos, como notebooks, desktops e servidores.
O código executado para exibir os dados dos produtos é listado abaixo:
procedure MostrarDadosProdutos;
begin
// Dados do notebook
if Opcao = 'Dell' then
begin
EditTamanhoTela.Text := 'Tela de 14 polegadas';
EditMemoriaRAM.Text := '3GB DDR3';
end
else if Opcao = 'Apple' then
begin
EditTamanhoTela.Text := '11.6 polegadas';
EditMemoriaRAM.Text := '4GB DDR3';
end;
// Dados do desktop
if Opcao = 'Dell' then
begin
EditProcessador.Text := 'Intel Core i5';
EditTamamhoHD.Text := '1 TB';
end
else if Opcao = 'Apple' then
begin
EditProcessador.Text := 'Intel Core i7';
EditTamamhoHD.Text := '500 GB';
end;
end;
Notou as estruturas condicionais? Sei que podemos aproveitar o mesmo IF para exibir os dados de todos os produtos, mas o código acima é só um exemplo. Na prática, estes IFs podem estar em métodos separados.
Imagine, agora, que essa mesma loja venderá também produtos da Lenovo. Cada método receberá um novo IF:
...
else if Marca = 'Lenovo' then
...
E vou mais além. Suponha também que servidores serão comercializados. Um novo bloco de código (ou método) terá de ser criado com todos esses IFs, um para cada marca.
Em suma, quanto mais marcas e produtos a loja trabalhar, maior será a quantidade de estrutura condicionais. O que aconteceria, por exemplo, se os desenvolvedores esquecessem de adicionar um IF em um destes blocos? Ruim, não?
Certa vez, quando eu estava participando de um treinamento sobre Design Patterns, o ministrante mencionou que cada estrutura IF deveria se “transformar” em uma nova classe. Para isso, terÃamos um mecanismo que nos retornaria os dados que precisamos naquele momento sem utilizarmos estruturas condicionais. Chamamos este mecanismo de fábrica, pois, analogicamente, é capaz de “criar” e disponibilizar um “produto”.
Bom, mas nada disso parece fazer sentido se não houver uma aplicação prática, não é?
Usando o mesmo exemplo da loja, o primeiro passo é criar 3 Interfaces: duas para cada produto (notebooks e desktops) e outra para as marcas. Ah, uma observação: Interfaces são recursos extremamente indispensáveis para a implementação de Design Patterns. Na parte 9 do artigo sobre dicas de desenvolvimento eu detalho um pouco mais sobre elas.
INotebook = interface
function BuscarTamanhoTela: string;
function BuscarMemoriaRAM: string;
end;
IDesktop = interface
function BuscarNomeProcessador: string;
function BuscarTamanhoHD: string;
end;
IFactoryMarca = interface
function ConsultarNotebook: INotebook;
function ConsultarDesktop: IDesktop;
end;
Agora, codificaremos a classe concreta de duas marcas que comercializam notebooks e desktops:
– Dell, que possui o Vostro e Inspiron;
– Apple, que possui o MacBook e iMac.
Vale lembrar que, como elas implementam IFactoryMarca, devem obrigatoriamente declarar os métodos assinados nessa Interface.
{TDell}
TDell = class(TInterfacedObject, IFactoryMarca)
function ConsultarNotebook: INotebook;
function ConsultarDesktop: IDesktop;
end;
function TDell.ConsultarNotebook: INotebook;
begin
result := TVostro.Create;
end;
function TDell.ConsultarDesktop: IDesktop;
begin
result := TInspiron.Create;
end;
{TApple}
TApple = class(TInterfacedObject, IFactoryMarca)
function ConsultarNotebook: INotebook;
function ConsultarDesktop: IDesktop;
end;
function TApple.ConsultarNotebook: INotebook;
begin
result := TMacBook.Create;
end;
function TApple.ConsultarDesktop: IDesktop;
begin
result := TIMac.Create;
end;
Em seguida, vamos criar as classes referentes aos notebooks:
{TVostro}
TVostro = class(TInterfacedObject, INotebook)
private
function BuscarTamanhoTela: string;
function BuscarMemoriaRAM: string;
end;
function TVostro.BuscarTamanhoTela: string;
begin
result := '15 polegadas';
end;
function TVostro.BuscarMemoriaRAM: string;
begin
result := '3GB DDR3';
end;
{TMacBook}
TMacBook = class(TInterfacedObject, INotebook)
private
function BuscarTamanhoTela: string;
function BuscarMemoriaRAM: string;
end;
function TMacBook.BuscarTamanhoTela: string;
begin
result := '11.6 polegadas';
end;
function TMacBook.BuscarMemoriaRAM: string;
begin
result := '4GB DDR3';
end;
E, finalmente, as classes relacionadas aos desktops:
{TInspiron}
TInspiron = class(TInterfacedObject, IDesktop)
private
function BuscarNomeProcessador: string;
function BuscarTamanhoHD: string;
end;
function TInspiron.BuscarNomeProcessador: string;
begin
result := 'Intel Core i5';
end;
function TInspiron.BuscarTamanhoHD: string;
begin
result := '1 TB';
end;
{TIMac}
TIMac = class(TInterfacedObject, IDesktop)
private
function BuscarNomeProcessador: string;
function BuscarTamanhoHD: string;
end;
function TIMac.BuscarNomeProcessador: string;
begin
result := 'Intel Core i7';
end;
function TIMac.BuscarTamanhoHD: string;
begin
result := '500 GB';
end;
Â
Nossa, André, quanto código!!!
Sim, foi a mesma coisa que pensei enquanto estudava Design Patterns, porém, acredite: se você continuar insistindo na forma como apresentei no inÃcio do artigo, em pouco tempo o seu código ficará bem maior do que a implementação apresentada acima. O nÃvel de abstração que alcançamos com esse padrão (criando Interfaces e classes com responsabilidade única), permitirá com o que a nossa arquitetura se torne bastante desacoplada, reduzindo as linhas de código a longo prazo.
Bom, pessoal, com tudo já pronto, é hora de conferirmos toda a mágica do padrão. Veja abaixo como ficou o método “MostrarDadosProdutos”. O único IF aparece somente nas primeiras linhas para instanciar a marca selecionada.
procedure MostrarProdutos;
var
Marca: IFactoryMarca;
Notebook: INotebook;
Desktop: IDesktop;
begin
// instancia a marca -> único IF da aplicação
if Opcao = 'Dell' then
Marca := TDell.Create
else if Opcao = 'Apple' then
Marca := TApple.Create;
// consulta (constrói) os objetos
Notebook := Marca.ConsultarNotebook;
Desktop := Marca.ConsultarDesktop;
// exibe os dados
EditTamanhoTela.Text := Notebook.BuscarTamanhoTela;
EditMemoriaRAM.Text := Notebook.BuscarMemoriaRAM;
EditProcessador.Text := Desktop.BuscarNomeProcessador;
EditTamamhoHD.Text := Desktop.BuscarTamanhoHD;
end;
Â
Ué, onde estão os  outros IFs? 🙂
Com um pouco mais de implementação, é possÃvel remover até o IF no inÃcio do método, mas isso é assunto para outro artigo.
Com o Abstract Factory, reduzimos as estruturas condicionais, facilitamos a manutenção do código e, acima de tudo, mantemos a escalabilidade da arquitetura, ou seja, se a loja começar a vender uma nova marca e/ou um novo produto, basta criar apenas novas classes e alterar a nossa fábrica. O Abstract Factory se vira com o resto.
Agora, olhando o código novamente, tente imaginar o quão fácil seria se precisássemos adicionar a marca “Lenovo”. Esse é o objetivo! 🙂
Caso você tenha ficado com alguma dúvida, baixe um exemplo no link abaixo (com alguns aprimoramentos) ou deixe um comentário!
Exemplo de Abstract Factory com Delphi
Â
Abraço, pessoal!
Excelente artigo André, meus sistemas possui centenas de if, else kkkkk. Vou estudar melhor esse artigo e tentar adptar para meu uso. E obrigado por apresentar isso pois eu não tinha o menor conhecimento sobre isso. Gostaria de saber se vc poderia escrever um artigo sobre NF-e pois eu tenho muitas dúvidas com respeito a integração, e acredito que outras pessoas tbm tenham dúvidas a respeito. Obrigado e continue esse excelente trabalho.
Fala, André, tudo certo?
Muito obrigado pelo feedback sobre o artigo! Espero que este padrão realmente possa ajudá-lo em seus projetos.
André, infelizmente não trabalhei com NF-e, então não tenho uma bagagem de conhecimento satisfatória para elaborar um artigo.
Mesmo assim, recomendo os componentes do projeto ACBr, bastante conhecido por programadores Delphi que já trabalharam com este segmento.
http://www.projetoacbr.com.br/forum/
Abraço!
fantastico celestino! 😀
sempre q vc faz esse recesso de fim de ano volta melhor, kkk
vc falou q é possÃvel remover esse IF do inicio mas q seria abordado em outro artigo, entao aborde pf, pq ate agora n cosnegui imaginar como tirar eles… 😛
abs
E aÃ, Conde, tudo bem?
Pode deixar que logo logo vou abordar como remover esse último IF! 🙂
Obrigado pelo elogio, Conde!
Abração!
Bom dia, Parabéns pelo trabalho, uma iniciativa como essa não tem preço, perincipalmente a iniciantes leigos como eu.
Tive uma dúvida. Se por acaso eu quiser escolher o processador “Intel Core i3”, “Intel Core i5”, “Intel Core i7”, teria que fazer um If na função “TInspiron.BuscarNomeProcessador”. Seria isso?
Olá, Wandelei, como vai?
No exemplo deste artigo, os dados são apenas consultados, e não inseridos ou alterados. No caso de uma inserção, a classe
TInspiron
(assim como as outras classes que implementamIDesktop
) teria uma property referente ao nome do processador. Dessa forma, o usuário poderia escolher o processador durante o cadastro do produto, atribuindo-o à essa property, como no exemplo abaixo:Obrigado pelo feedback. Abraço!
Simples, objetivo e prático. Meus parabéns e obrigado por me ajudar a entender esse padrão.
Obrigado pelo comentário, Igor!
Grande abraço!
Cara, muito bom seu artigo, foi bem didático, de fácil entendimento. Muito obrigado por compartilhar seu conhecimento.
Eu que agradeço pelo feedback, Warley!
Fico feliz por saber que gostou do artigo.
Grande abraço!
Meu querido, esse [Delphi] Design Patterns GoF – Retrospectiva tem em video? algum curso especifico para leigos como eu?
Boa noite, Germano!
Infelizmente não gravei vÃdeos para essa série de artigos. Quem sabe futuramente!
Abraço!