E ai pessoal, tudo certo?

Na primeira parte do artigo, fizemos uma reflexão do papel do arquiteto de software no time e como arquitetura de software deve ser algo de interesse de todos no time, para que não exista alguém centralizado de onde tudo depende. Vimos também sobre os padrões definidos pelo GoF (Gang of Four). Aliás, recomendo fortemente que leiam o livro.

Nestas próximas partes, vamos analisar no detalhe cada um dos 23 padrões, suas propostas, quando aplicar, quando não aplicar, vantagens de desvantagens de cada um. Vamos começar com o primeiro da lista que apresentei, o Abstract Factory.

Abstract Factory

O padrão Abstract Factory é o primeiro Pattern descrito no livro Design Patterns do GoF. Ela faz parte da categoria de Patterns Criacionais, cujo objetivo é a instanciação de objetos. Essa categoria é importante pois ela sustenta o princípio mais importante do livro: “programe para interfaces e não para implementações”.

Atualmente, os Patterns Criacionais estão em desuso, sendo substituídos pelos frameworks de Injeção de Dependência, que fazem exatamente isso: instanciam para você as classes das quais você é dependente. De toda forma, conhecer os patterns, a sua motivação e entender as suas consequências é um bom exercício de design de software.

O problema que este padrão tenta resolver é da instanciação na existência de uma família de produtos. O exemplo dado no livro é o seguinte: você tem elementos visuais (produtos), como Window, ScrollBar, Menu e etc. Esses elementos visuais têm diferentes implementações para cada família de implementação gráfica, como o Microsoft Windows, o MAC, e X do Linux.

Nesse caso, a solução consiste em duas partes:

1. Crie interfaces padrões para os diferentes produtos dessa família (como Window, ScrollBar e Menu). E todo o seu sistema vai trabalhar apenas com essas interfaces que você definiu.

2. Defina uma Abstract Factory, que tem os métodos de instanciação para cada uma dessas interfaces padrões definidas acima, no caso, os métodos para o exemplo seriam: CreateWindow, CreateScrollBar, CreateMenu. Sempre que o sistema precisar de instâncias dos produtos ele irá conseguir as mesmas através dessa Abstract Factory.

Mas de onde virá essa Factory? Em algum lugar, provavelmente na inicialização do seu sistema, você define qual Abstract Factory você vai fornecer ao sistema, e repassa essa Factory a todos os objetos que dependem dela. Veja o esquema:

Diagrama UML com exemplo de uso de uma Abstract Factory

Na figura acima, retirada do livro, o Client, que representa os demais módulos do sistema, utiliza as classes abstratas dos produtos (AbstractProductA e AbstractProductB) e a AbstractFactory, assim como foi explicado.

É importante agora analisar os benefícios e malefícios desse pattern. O ponto principal é que o pattern deixa seu sistema independente das diferentes famílias, ou seja, garante o baixo acoplamento citado anteriormente.

Outro ponto positivo é que este pattern permite adicionar, remover ou modificar rapidamente qual família de produtos deseja-se usar. Isso pode até ser feito em runtime, se usado com um pouco de Reflection para instanciar uma fábrica através de parâmetros (ou com um array referenciando as possíveis fábricas).

Já um ponto negativo desse pattern é que a adição ou remoção de um produto da família exige a modificação da AbstractFactory, o que causa um grande overhead, pois deve-se modificar todas as implementações da Factory e o cliente que usa a AbstractFactory.

Na verdade, usando-se Reflection pode-se até criar um único método “Create” que recebe como parâmetro alguma indicação de qual produto ele deve criar (uma string com o nome da classe, por exemplo). Assim, você poderia criar um novo produto sem muitos problemas, basta passar o parâmetro certo. Mas isso funciona como uma balança: se você ganha flexibilidade na criação de novos produtos, você perde com a necessidade de uma interface única para todos esses produtos, já que eles serão retornados pelo mesmo método “Create”. E o seu Client precisará fazer uma conversão para o produto específico.

Ficha resumo

Vamos a “ficha” de definição deste pattern.

  • Nome: Abstract Factory;
  • Objetivo / intenção: Permite a criação de famílias de objetos relacionados ou dependentes por meio de uma única interface e sem que a classe concreta seja especificada. Também é conhecido como Kit;
  • Motivação: O objetivo em empregar o padrão é isolar a criação de objetos de seu uso e criar famílias de objetos relacionados sem ter que depender de suas classes concretas. Isto permite novos tipos derivados de ser introduzidas sem qualquer alteração ao código que usa a classe base;
  • Aplicabilidade: Cenários onde uma família de produtos ou classes precisa ser instanciado, sem dependência de suas classes concretas, como no exemplo do livro, onde você tem elementos visuais (produtos), como Window, ScrollBar, Menu e etc. Esses elementos visuais têm diferentes implementações para cada família de implementação gráfica, como o Microsoft Windows, o MAC, e X do Linux;
  • Estrutura: Abaixo temos um exemplo de estrutura onde o cliente apenas conhece a AbstractFactory e as classes abstratas dos produtos;
Diagrama UML com exemplo de uso de uma Abstract Factory
  • Consequências: Temos a vantagem de utilizamos apenas classes abstratas ou interfaces, o que diminui muito o acoplamento entre as classes do sistema, assim como podemos adicionar, modificar ou remover produtos da família de forma rápida. Mas temos como ponto negativo que a adição ou remoção de famílias exige a modificação da AbstractFactory, o que causa um grande overhead, pois deve-se modificar todas as implementações da Factory e o cliente que usa a AbstractFactory;
  • Implementações: Conforme o exemplo, temos abaixo o código em Java que demonstra a implementação do padrão para a produção de produtos de famílias diferentes;
  • Usos conhecidos: O padrão Abstract Factory pode ser utilizado na implementação de um toolkit que disponibilize controles que funcionem em diferentes interfaces gráficas, tal como Motif, GTK+ (GNOME) ou Qt (KDE). Estas GUIs possuem diferentes padrões de controles visuais e, para facilitar a construção de aplicações que interajam facilmente com diferentes interfaces gráficas, é interessante que se definam interfaces comuns para acesso aos controles, independentemente da GUI utilizada. Este problema pode ser resolvido por meio de uma classe abstrata que declara uma interface genérica para criação dos controles visuais e de uma classe abstrata para criação de cada tipo de controle. O comportamento específico, de cada um dos padrões tecnológicos contemplados, é implementado por meio de uma classe concreta. O aplicativo, ou “cliente”, interage com o toolkit por meio das classes abstratas sem ter conhecimento da implementação das classes concretas;
  • Padrões relacionados: Factory Method, Prototype e Singleton;

Concluindo

Este foi o padrão Abstract Factory. O mesmo facilita bastante quando trabalhamos com aplicações que devem funcionar com ambientes diferentes, como interfaces gráficas, sistemas de banco de dados diferentes, mas onde temos bases comuns, apenas com comportamentos específicos definidos para cada “ambiente”.

Na próxima parte desta série, vamos abordar o padrão Factory Method.

Não deixem de postar dúvidas e feedbacks sobre a série. Até a próxima parte!