[Delphi] Design Patterns GoF – Mediator

Boa noite, pessoal!
Em algumas ocasiões, um intermediário para coordenar as mensagens e interações entre objetos pode parecer uma solução adequada para evitar a forte dependência entre eles. Com o Mediator, essa solução é factível. Veremos, neste artigo, o conceito, propósito e uma aplicação prática deste padrão de projeto, mas, de antemão, já esclareço: o Mediator é bem fácil de compreender. 😉

Introdução

Antes de iniciar o artigo, gostaria de fazer um singelo agradecimento ao Maykon Capellari, um amigo de trabalho com quem discuto e compartilho bastante conhecimento sobre Engenharia de Software. Obrigado, “MK”!

Durante o desenvolvimento de um software, é comum ocorrer situações em que precisamos referenciar algumas classes para consumir seus atributos ou métodos. Porém, à medida que a complexidade da funcionalidade expande, novas classes são referenciadas, criando uma dependência cada vez maior entre elas.

O Mediator consiste em um Design Pattern que provê um mecanismo de encapsulamento das interações que ocorrem entre diferentes objetos. Dessa forma, quando dois objetos precisam interagir, a comunicação é realizada exclusivamente por meio do Mediator. O objetivo dessa abordagem é alcançar um baixo nível de acoplamento na arquitetura do software, já que os objetos passam a não referenciar diretamente outros objetos. Podemos afirmar, portanto, que o Mediator é adequado para cenários em que muitas classes referenciam muitas classes. Quando isso ocorre, uma simples alteração pode gerar um impacto significativo no software, uma vez que o acoplamento é elevado.

Analogia

Considere, como analogia, o funcionamento do aplicativo WhatsApp. Quando enviamos uma mensagem, o aplicativo age como intercessor para entregá-la ao destinatário desejado. Não precisamos estar próximos do destinatário e nem sequer tê-lo adicionado como contato.

Outra analogia bastante interessante, que encontrei no blog do Luís Gustavo Fabbro, é um controle de tráfego aéreo. As aeronaves que circulam o aeroporto não “se conhecem”, portanto, não conseguem comunicar suas intenções de decolagem e aterrissagem para evitar acidentes. Essa administração é realizada pela torre de controle que, como possui comunicação com todas as aeronaves, atua como Mediator.

Um exemplo clássico e mais técnico seria o formulário de uma aplicação Delphi. Observe que podemos adicionar diferentes componentes no formulário, mas cada um deles não tem conhecimento da existência do outro. Mesmo assim, podemos solicitar que eles se comuniquem, por exemplo, atribuindo o Text de um TEdit para o Caption de um TLabel em um evento do formulário que, neste caso, recebe o papel de Mediator.

Estrutura

A estrutura deste padrão de projeto é de fácil compreensão, composta por apenas quatro elementos:

  • Mediator: Interface que define os métodos de comunicação entre os objetos;
  • Concrete Mediator: Classe concreta que implementa a Interface Mediator;
  • Colleague: Interface que define os métodos referente às “intenções” dos objetos;
  • Concrete Colleague: Classe concreta que implementa a Interface Colleage.

Em uma tradução livre, podemos dizer que temos “mediadores” e “colegas”. A propósito, nunca comentei no blog, mas os elementos de cada Design Pattern geralmente representam essa combinação: Interface + Classe Concreta. Lembre-se que essa estrutura está presente na maioria dos artigos dessa série. 🙂

Pois bem, já que mencionamos o WhatsApp como um “provedor de serviços de mensagens”, o nosso exemplo prático seguirá a mesma ideia. Codificaremos uma aplicação de compra e venda, em que membros podem se registrar e enviar propostas para outros membros sem que seja necessário conhecê-los.

Interface Colleague

Iniciaremos pela Interface Colleague. São cinco métodos em comum para qualquer membro que for adicionado à aplicação:

Interface Mediator

A próxima etapa é a definição da Interface Mediator, que faz referência à Interface Colleague em dois de seus métodos:

Antes de continuar, vale justificar que, como as ações executadas pelos métodos serão exibidas em um controle visual (como um TMemo), decidi assiná-los como functions para retornarem uma string.

Classe Concrete Mediator

A partir de agora, codificaremos a implementação concreta das Interfaces. Iniciaremos pelo Concrete Mediator, que será responsável por toda a coordenação das interações. Para isso, faremos uso do recurso da classe TDictionary do Delphi, que representa uma coleção de pares na estrutura de chave-valor. Salvo engano, essa classe está disponível desde a versão XE4 do produto.

Para auxiliar na compreensão, procurei adicionar um comentário nos principais métodos:

Os métodos são bem simples e demonstram claramente a manipulação do dicionário. Mesmo assim, gostaria de enfatizar a forma como o dicionário foi declarado:

Com essa declaração, é possível armazenar vários objetos que implementam a Interface Colleague (como uma lista de objetos), dado que o identificador (chave) de cada posição é uma string que será preenchida com o nome do membro. Logo, podemos encontrar o objeto pelo nome do membro acessando a propriedade Items.

Classe Concrete Colleague

A última etapa para finalizar a codificação do padrão de projeto é construir o Concrete Colleague. Mais uma vez, como apoio, também adicionei alguns comentários:

Nada de muito especial. Recebemos a instância do Mediator no construtor e o utilizamos para executar cada intenção do membro (entrar, sair e enviar propostas).

Em ação!

Para avaliar todo o funcionamento do padrão de projeto, utilizaremos um formulário como Client com um componente TMemo para exibir as ações. Em primeiro lugar, o Mediator deve ser criado na inicialização e permanecer instanciado durante a execução.

Em seguida, podemos adicionar membros com a código abaixo, substituindo o texto fixo por um componente de entrada de dados:

Para remover o membro, buscamos a sua referência no dicionário para chamar o método Sair:

Por fim, a ação principal, que é o envio de propostas, recebe uma codificação bem sucinta, na qual indicamos o destinatário e a proposta como parâmetros:

Confira o resultado na imagem abaixo:

Exemplo de aplicação utilizando o Design Pattern Mediator

Neste projeto de exemplo, por ser didático, o Mediator está no próprio formulário. Em um ambiente real, essa classe pode residir em um local remoto, como um servidor de aplicação, por exemplo.

Conclusão

A codificação apresentada neste artigo está disponível no GitHub, com alguns aperfeiçoamentos. Além de usuários, criei também um Concrete Colleague para representar administradores e adicionei um novo método no Mediator para liberar o dicionário da memória ao fechar a aplicação.

Para finalizar, deixo aqui algumas boas observações sobre a aplicação do Mediator neste projeto:

  • A única dependência da classe de membros é com o Mediator;
  • Os membros (Concrete Colleagues) não se conhecem, portanto, não se referenciam diretamente;
  • A inclusão de novos Concrete Colleagues não impacta na arquitetura existente;
  • A unit do Mediator não faz referência aos Concrete Colleagues. Apenas Interfaces.

Além disso, claro, vale destacar novamente o objetivo do Mediator: investir no baixo acoplamento. Um projeto com essa característica está automaticamente submetido à fácil manutenção.

 

Fico por aqui, mas volto em breve!
Abraços!


 

André Celestino