[Delphi] Design Patterns GoF – Factory Method

Boa noite, leitores!
Peço desculpas pela ausência. Depois de um mês bastante agitado com mudança de apartamento, estou de volta!
O tema dessa vez é o padrão de projeto Factory Method, também da família de padrões criacionais. Confira o artigo para compreender este padrão e identificar as principais diferenças quando comparado ao Abstract Factory.

Introdução

Quando comecei a ler sobre o Factory Method, fiquei perplexo. Quanto mais pesquisava sobre o padrão, mais eu pensava: “Caramba, este padrão é idêntico ao Abstract Factory”. Porém, eu sabia que havia alguma diferença e que deveria identificá-la para produzir o novo artigo do blog, então continuei insistindo. Eis que, depois de algum tempo, encontrei um tópico bem interessante no StackOverflow (em inglês) que explica a diferença entre eles em poucas palavras (parafraseado):

A principal diferença entre Factory Method e Abstract Factory é que o primeiro representa um método simples, enquanto o segundo é um objeto. Por ser um método, o Factory Method pode ser sobrescrito em subclasses. O Abstract Factory, por sua vez, é um objeto que pode conter várias fábricas.

Mesmo com essa definição, a diferença ainda pode parecer um pouco “ofuscada”. Mas, não se preocupe. O exemplo apresentado neste artigo o ajudará a compreender melhor todo este contexto.

Pois bem, antes de introduzir a parte prática, vale fazer um repasse na definição teórica. O Factory Method é um padrão de projeto que encapsula a criação de um objeto através de um método (por isso o “method” no nome). Por meio de parâmetros, a fábrica (factory) decide qual produto (product) deve criar e retornar para o cliente. Com o produto pronto, podemos acessar os métodos e propriedades conforme necessário.

Mas esse comportamento não é semelhante ao Builder, abordado no artigo anterior?
Não. No Builder, a lógica de criação de objetos está na aplicação, ou seja, criamos o construtor (Builder), mas temos que “orientá-lo” para criar o produto. No Factory Method, a criação do objeto está dentro da fábrica. Informamos um parâmetro e deixamos que a fábrica decida quem criar. Mesmo assim, observe que a família de padrões criacionais possui, em termos gerais, o mesmo objetivo: solucionar problemas relacionados à criação de objetos.

Agora chega de teoria, né? 🙂

Exemplo de codificação do Factory Method

Para criar o exemplo deste artigo, imaginei uma financiadora. Neste cenário, informaremos o valor, a quantidade de parcelas e o prazo de pagamento deste valor. O Factory Method irá criar um objeto de tipo de prazo com todas as informações geradas com estes parâmetros, como o número da parcela, a data de vencimento, o valor atualizado e o valor total a pagar. É bom ressaltar que devemos nos atentar ao modo como o tipo de prazo será criado e retornado. É bem aí que o padrão irá trabalhar.

Interfaces

Em primeiro lugar, criaremos as Interfaces. Uma para o tipo de prazo e outra para o Factory Method:

Classes concretas

Agora, partindo para as classes concretas, criaremos dois tipos de prazo: mensal e anual.

Claro, sei que o método ConsultarProjecao pode ser refatorado para reaproveitamento nas duas classes, mas, como este exemplo é didático, decidi mantê-lo dessa forma para facilitar a compreensão.

Classe do Factory Method

Em seguida, implementaremos também a classe concreta do Factory Method:

Pronto, pessoal!
O próximo passo é consumir essas classes. Para isso, crie um formulário com os seguintes componentes:

  • TEdit, com o nome “EditValor”;
  • TEdit, com o nome “EditQtdeParcelas”;
  • TComboBox, com o nome “ComboBoxPrazoPagamento” e com os itens “Mensal” e “Anual”;
  • TButton, com o nome “ButtonGerarProjecao”;
  • TMemo, com o nome “Memo”.

A tela deverá ficar parecida com a imagem abaixo:

Formulário de exemplo de aplicação do Design Pattern Factory Method

No botão de geração de projeção, faremos todo o mecanismo do padrão:

Execute a aplicação, informe um valor, a quantidade parcelas, o prazo de pagamento e clique no botão de geração de projeção. O componente Memo será preenchido com o conteúdo da classe conforme o prazo selecionado no ComboBoxPrazoPagamento. Importante: Veja que no código no botão não há nenhuma instrução IF! 🙂

Conclusão

Bom, além de evitar o IF, como apontei no parágrafo anterior, uma grande vantagem é a facilidade manutenção, principalmente evolutiva.

Sabe por quê? Caso seja necessário adicionar um novo prazo (semestral, por exemplo), o código do botão permanecerá exatamente o mesmo. A alteração será feita dentro da fábrica, que receberá uma nova condição para retornar o objeto deste tipo de prazo. Para apresentar este cenário, baixe o exemplo do Factory Method no link abaixo e veja os comentários no código.

Diferenças entre os 3 padrões abordados até o momento

Com o Abstract Factory, é possível trabalhar com múltiplas fábricas. Pode-se dizer, que é um padrão de “fábrica de fábricas”. No primeiro artigo, portanto, instanciamos uma fábrica (marca) e depois “construímos” os produtos (notebooks e desktops). Podemos criar novas fábricas, como “hardware”, e trabalhar com novas fábricas dentro dela, como “placa de vídeo” e “disco rígido”.

No Factory Method, acontece um pouco diferente. Não temos um conjunto de fábricas, mas apenas uma única fábrica que nos retorna um tipo de objeto conforme algum(ns) parâmetro(s) informados. A lógica de criação do produto fica a critério da fábrica, enquanto o cliente apenas recebe a instância.

No exemplo deste artigo, o parâmetro é o prazo de pagamento, no qual faz a fábrica “decidir” quem instanciar. Sempre que uma nova condição (como um novo prazo) for adicionada, apenas a fábrica será alterada para considerar o novo produto (método TFabricaPrazos.ConsultarPrazo).

Ao mesmo passo, se uma nova informação precisa ser adicionada nos tipos de prazo (como o total a pagar), basta alterar somente as classes TPrazoMensal e TPrazoAnual, ou seja, a fábrica permanece inalterada, já que é apenas responsável por construir o produto. Observe que as responsabilidades ficam bem delimitadas: o cliente, a fábrica e o produto. A alteração em um deles não impacta fortemente no outro.

O Builder, por sua vez, é um construtor de objetos complexos. O propósito deste padrão é montar um objeto “em partes”. Um exemplo clássico do Builder é o padrão MVC. Podemos ter uma classe Builder que monta as camadas Model, View e Presenter e nos retorna um componente pronto.

Outro exemplo é a montagem de um relatório com várias seções. Podemos utilizá-lo para compor um relatório com seções dinâmicas de acordo com os parâmetros que o usuário informou. No artigo sobre o Builder, fiz a montagem de cestas de produtos, nos quais poderiam ser substituídos por objetos, constituindo, então, um produto complexo.

 

Leitores, espero que tenham entendido a diferença entre estes padrões. Qualquer dúvida, deixe um comentário!
Até a próxima! Abraço!


André Celestino