[Delphi] Design Patterns GoF – Bridge

Retomando a nossa série de artigos sobre Design Patterns, hoje apresento-lhes o padrão Bridge. Pela tradução – “ponte” – já podemos imaginar um pouco do propósito deste padrão, concordam? Talvez seja uma classe que “conecte” duas partes do sistema, como uma ponte real liga duas cidades.
Bom, não é bem isso. Acompanhe o artigo e conheça a ideia por trás deste padrão!

Introdução

Embora a tradução de “bridge” seja “ponte”, veremos que o objetivo do padrão de projeto Bridge não é conectar duas abstrações diferentes. Essa é uma solução do Adapter, discutido no artigo anterior, que atua como um intermediário entre duas classes para torná-las compatíveis. De forma bastante resumida, o propósito do Bridge é eliminar múltiplas heranças e reduzir a quantidade de classes existentes no projeto.

Cenário

Para iniciar, vou apresentar um cenário bem típico. Considere que a nossa aplicação possua uma funcionalidade de exportação de dados de clientes e produtos para os formatos XLS e HTML. Para evitar a duplicação de código, há uma classe base chamada TExportador e duas heranças a partir dela: TExportadorClientes e TExportadorProdutos. Como exportamos para dois formatos diferentes, precisamos, agora, criar uma especialização para cada uma dessas classes filhas:

  • TExportadorClientesXLS
  • TExportadorClientesHTML
  • TExportadorProdutosXLS
  • TExportadorProdutosHTML

Até o momento, essa é a nossa hierarquia de classes:

Hierarquia de Classes

Embora pareça viável, essa hierarquia está sujeita a se transformar em um emaranhado de classes. Sabe por quê?

Imagine que o cliente tenha solicitado a funcionalidade de exportação dos dados de fornecedores também. E mais, além de XLS e HTML, o cliente precisará de exportação em formato CSV para importar o arquivo em outro sistema. Se seguirmos a lógica da hierarquia apresentada, esta será a nova arquitetura:

Hierarquia de Classes sem Bridge

6 classes novas?
Pois é. A imagem quase nem coube na página. Imagine então se surgisse a necessidade de exportação dos dados de funcionários? No mínimo seriam mais 4 classes criadas. Aos poucos, a arquitetura torna-se uma “macarronada” de classes, dificultando não só a manutenção, como também a evolução dessas funcionalidades. Veja que o conceito de Herança da Orientação a Objetos, apesar de muito importante, pode ser um complicador quando empregado abusivamente.

Bridge

Essa situação pode ser resolvida com o padrão de projeto Bridge!

Na teoria, o padrão de projeto traz a seguinte descrição: “Desacoplar uma abstração de sua implementação”. Traduzindo na prática, o que faremos a seguir é separar e agrupar as responsabilidades em diferentes classes, reduzindo radicalmente a complexidade da arquitetura. Para este propósito, o padrão Bridge é composto por 4 elementos:

  • Abstraction: classe abstrata ou interface da abstração principal;
  • Refined Abstraction: implementação concreta da Abstraction;
  • Implementor: interface da abstração utilizada pela Abstraction;
  • Concrete Implementor: implementação concreta da Implementor.

Você deve ter levantado a sobrancelha ao ler a função de cada um dos elementos, não é? 🙂

Compreendo que podem parecem semelhantes, ou talvez confusos, mas o exemplo prático a seguir será mais elucidativo.

Corrigindo a arquitetura com o Bridge

Para “consertar” a nossa arquitetura com o padrão Bridge, devemos, inicialmente, separar o escopo das exportações e os tipos de formatos, resultando em duas classes base, ao invés de uma só. O objeto da classe de exportação receberá um objeto da classe de tipo de formato para realizar a exportação. Em outras palavras, “injetaremos” o tipo de formato no objeto de exportação. Feito isso, a nossa arquitetura ficará dessa forma:

Hierarquia de Classes com Bridge

Bem melhor, não?

Interface Implementor

Então, mãos à obra! Em primeiro lugar, criaremos a Interface do Implementor, que se refere ao tipo de formato:

Classes Concrete Implementors

Em seguida, criaremos uma classe para cada tipo de formato, implementando a Interface acima. Essas classes serão nossos Concrete Implementors. Para XLS, trabalharemos com o objeto TExcelApplication.

Para HTML, utilizaremos uma TStringList nativa para armazenar as tags e os valores.

Cada uma das classes acima exporta os dados conforme o tipo específico de formato. Vale ressaltar que não importa a origem dos dados. O procedimento de exportação será o mesmo.

Interface Abstraction

O terceiro passo é criar a Interface do Abstraction que, no nosso caso, é o exportador:

Classes Refined Abstractions

Este único método da Interface será implementado pelas nossas Refined Abstractions. Primeiro, a classe de exportação de clientes:

E então, a classe de exportação de produtos:

Observe que os métodos de exportação são muito parecidos. Sendo assim, poderíamos criar uma classe abstrata de exportação, mas, para manter o exemplo bem didático, decidi manter dessa forma.

Em ação!

Bom, talvez você ainda não tenha identificado as vantagens. Confira abaixo, por exemplo, como é feita a chamada da exportação dos dados de clientes para XLS:

Notou a linha que indica que a exportação deve ser em formato XLS? Sim, através de um parâmetro na construção do Refined Abstraction (objeto de exportação):

Perfeito! Se quisermos exportar para HTML, basta apenas alterar o parâmetro:

E no caso dos produtos?
Mesma coisa! Instancie um exportador, informe o formato no construtor e passe os dados como parâmetro do método ExportarDados:

Moleza, moleza!

Conclusão

E se surgisse a necessidade de exportação dos dados de fornecedores, como exemplifiquei no início do artigo?
Bom, a única alteração seria a criação de uma nova Refined Abstraction, chamada TExportadorFornecedores. Só isso. Os formatos já estarão prontos! 🙂

Bom, acho que não preciso falar muito das vantagens, não é? Além de diminuir a quantidade de classes e agrupar as responsabilidades, facilitamos a manutenção evolutiva da nossa arquitetura. Quando um novo formato for adicionado, ficará “automaticamente” disponível para todos os exportadores. Da mesma forma, quando um novo exportador for criado, todos os formatos já estarão disponíveis para serem utilizados.

Ainda está com dúvidas sobre o padrão? Baixe o exemplo deste artigo no link abaixo para compreendê-lo melhor na prática. O projeto inclui algumas melhorias e refatorações não apresentadas no artigo. Aproveite para estudá-las também!

 

Grande abraço, pessoal!


 

André Celestino