O que é

Visão Geral

O padrão Mediator (Mediador) é um padrão comportamental que tem como objetivo fundamental reduzir as dependências caóticas entre objetos em um sistema de software. Em vez de permitir que múltiplos objetos se comuniquem diretamente uns com os outros — criando uma rede complexa e difícil de gerenciar de referências cruzadas — o padrão restringe essa comunicação direta, forçando todos os componentes a interagirem apenas por meio de um objeto central, conhecido como o mediator.

Este padrão é particularmente valioso em cenários onde a lógica de negócio ou a interface do usuário envolve muitos elementos interdependentes, cada um potencialmente afetando o estado ou comportamento dos outros. Ao centralizar a lógica de coordenação em um único ponto, o Mediator promove um design mais desacoplado, organizado e manutenível.


O Problema: Dependências Caóticas em Sistemas Complexos

Para compreender verdadeiramente a necessidade do Mediator, vamos analisar um cenário comum: uma tela de checkout ou finalização de compra em um aplicativo de e-commerce.

Imagine uma interface contendo os seguintes componentes:

  1. CardViewComponent: Exibe visualmente o cartão de crédito (com número mascarado, nome, etc.).

  2. CardDetailsComponent: Um formulário com campos para o usuário inserir o número do cartão, CVV, data de validade, etc.

  3. StoreCreditCardComponent: Um checkbox com o rótulo "Armazenar cartão de crédito para compras futuras".

  4. ButtonComponent: O botão de "Finalizar Compra" ou "Enviar".

Inicialmente, as relações entre esses elementos podem parecer simples. No entanto, conforme a lógica de interação evolui, a comunicação direta entre eles rapidamente se transforma em um emaranhado difícil de manter.

Exemplo de Interações Problemáticas

  • O ato de marcar o checkbox StoreCreditCardComponent deve fazer com que o CardViewComponent seja exibido (para mostrar ao usuário como o cartão salvo aparecerá).

  • O ButtonComponent (botão de envio) precisa validar todos os campos do CardDetailsComponent antes de se tornar clicável e processar o pagamento.

  • Se nenhum dado tiver sido preenchido no CardDetailsComponent, o ButtonComponent deve permanecer desabilitado e o CardViewComponent não deve ser exibido.

Implementando essas regras com comunicação direta, cada componente precisa de uma referência para vários outros. O ButtonComponent precisaria conhecer o CardDetailsComponent para validá-lo. O StoreCreditCardComponent precisaria conhecer o CardViewComponent para mostrá-lo. Este é o cenário do acoplamento forte.

As consequências deste design são significativas:

  1. Dificuldade de Manutenção: Alterar a lógica de um componente (ex.: adicionar um novo campo de validação) exige modificar todos os outros componentes que dependem dele.

  2. Baixa Reusabilidade: Componentes tornam-se inseparáveis do contexto específico. O CardViewComponent, por exemplo, não pode ser facilmente reaproveitado em outro formulário porque ele tem uma dependência direta e "código hardcoded" para interagir com o CardDetailsComponent específico da tela de checkout.

  3. Testabilidade Comprometida: Testar um componente isoladamente torna-se quase impossível, pois ele depende de uma rede de outros objetos para funcionar corretamente.


A Solução: Centralizando a Comunicação

O padrão Mediator resolve este problema de forma elegante ao interromper toda a comunicação direta entre os componentes. Em vez de um componente chamar um método de outro diretamente, ele apenas notifica um mediador central sobre um evento que ocorreu.

O mediador, que possui conhecimento sobre todos os componentes envolvidos, é responsável por orquestrar a resposta adequada. Ele contém a lógica de qual componente deve reagir e de que maneira.

Reimaginando o Cenário de Checkout

No exemplo da tela de compra, a própria tela (ou uma classe dedicada) assumiria o papel de Mediator Concreto.

  • O CardDetailsComponent não valida a si mesmo para habilitar o botão. Ele simplesmente notifica o mediador: "Os meus dados foram alterados".

  • O StoreCreditCardComponent não mostra o CardViewComponent. Ele notifica o mediador: "Fui marcado/desmarcado".

  • O ButtonComponent não valida nada ao ser clicado. Ele notifica o mediador: "Fui pressionado".

O Mediator (a tela) então:

  1. Ao receber "dados alterados", executa a validação dos campos e decide se habilita ou desabilita o ButtonComponent.

  2. Ao receber "checkbox marcado", decide se deve mostrar ou ocultar o CardViewComponent.

  3. Ao receber "botão pressionado", orquestra todo o fluxo de finalização: valida todos os dados, chama a API de pagamento e navega para a tela de confirmação.

O resultado é que os componentes se tornam independentes entre si. Eles não sabem da existência dos outros; sabem apenas que devem reportar seus eventos ao mediador. Sua única dependência é em relação à interface do mediador, o que reduz drasticamente o acoplamento.


Analogia com o Mundo Real: A Torre de Controle Aéreo

Uma analogia clássica para o padrão Mediator é o sistema de controle de tráfego aéreo.

  • Componentes: Os pilotos (ou as aeronaves).

  • Mediator: A torre de controle.

  • Problema: Se cada piloto tentasse se comunicar diretamente com todos os outros aviões próximos para coordenar pousos, decolagens e rotas, o caos e os acidentes seriam inevitáveis.

  • Solução: Os pilotos nunca se comunicam diretamente entre si. Eles se comunicam apenas com a torre de controle. A torre tem uma visão global do tráfego aéreo e emite instruções específicas para cada piloto ("Jato 123, autorizado para pouso na pista 09L"; "Cessna 456, mantenha altitude 5000 pés").

  • Vantagem: A torre centraliza a lógica de coordenação. Os pilotos podem focar em suas próprias tarefas, sem precisar conhecer os detalhes de todos os outros voos. A complexidade do sistema é gerenciada em um ponto central.


Estrutura do Padrão

A implementação do Mediator segue uma estrutura bem definida, composta por quatro elementos principais:

  1. Componentes (Colleagues): São as classes que contêm a lógica de negócio ou funcionalidades específicas. Cada componente mantém uma referência para um objeto mediador, tipicamente através de uma interface (IMediator). Em vez de se comunicar com outros componentes, ele notifica o mediador sobre eventos.

  2. Interface do Mediator (IMediator): Declara métodos de comunicação, geralmente um método como Notify(sender: object, event: string). Esta interface define o contrato pelo qual os componentes enviam notificações para o mediador.

  3. Mediator Concreto: Implementa a interface IMediator. Esta classe conhece todos os componentes concretos e contém a lógica de orquestração complexa. Ela decide como reagir a cada notificação recebida, potencialmente chamando métodos em outros componentes.

  4. Cliente: Configura o sistema criando instâncias de todos os componentes e injetando a mesma instância do mediator em cada um deles, estabelecendo assim o canal de comunicação.

O fluxo é sempre: Componente → Notifica o Mediator → Mediator orquestra outros Componentes.


Quando Usar o Padrão Mediator

O padrão Mediator é indicado nas seguintes situações:

  • Quando você possui um conjunto de classes fortemente acopladas, onde a mudança em uma classe exige a modificação de várias outras, tornando a manutenção custosa.

  • Quando a reutilização de um componente é dificultada devido às suas muitas dependências diretas com outros componentes.

  • Quando você se encontra criando múltiplas subclasses de um componente apenas para ajustar seu comportamento em diferentes contextos de interação. O Mediator permite variar esse comportamento sem alterar os próprios componentes.

  • Em interfaces gráficas complexas (GUIs), onde widgets (botões, caixas de texto, listas) precisam interagir de formas intricadas.

  • Em fluxos de formulário ou processos de negócio com múltiplas etapas interdependentes.

Prós e Contras

Prós

  • Redução do Acoplamento: Desacopla os componentes, permitindo que variem independentemente.

  • Centralização do Controle: A lógica de coordenação complexa é concentrada em um único lugar, facilitando sua compreensão e manutenção.

  • Reusabilidade de Componentes: Componentes tornam-se mais fáceis de reutilizar em diferentes partes do sistema, pois dependem apenas do mediador.

  • Simplificação da Comunicação: Substitui relacionamentos muitos-para-muitos por relacionamentos um-para-muitos (todos os componentes para o mediador).

Contras

  • Risco de "God Object": Se não for bem projetado, o Mediator Concreto pode crescer indefinidamente, acumulando demasiada lógica de negócio e se tornando uma classe monolítica e difícil de manter.

  • Fere o Princípio da Explicitação de Dependências (DEP): Ao injetar o mediador, que por sua vez conhece todos os componentes, pode-se ocultar as dependências reais que um componente precisa para funcionar. É um trade-off consciente entre desacoplamento e explicitação.

  • Complexidade na Criação: A configuração inicial (instanciar e conectar todos os componentes ao mediador) pode ser mais complexa do que em um design com acoplamento direto.


Relação com Outros Padrões

O Mediator dialoga e às vezes é confundido com outros padrões de comportamento:

  • Observer: Ambos tratam de comunicação indireta. No entanto, enquanto o Observer é um padrão distribuído (vários observadores se inscrevem em um assunto), o Mediator é centralizado (todos se comunicam com um ponto central). Eles podem ser usados em conjunto: um mediator pode implementar a lógica de orquestração usando eventos no estilo Observer internamente.

  • Facade: Ambos simplificam a interação com um conjunto de objetos. A Facade fornece uma interface simplificada para um subsistema complexo, mas os objetos dentro do subsistema ainda podem se comunicar diretamente. O Mediator inibe a comunicação direta e força a coordenação através dele.

  • Chain of Responsibility & Command: Estabelecem outros fluxos de comunicação indireta, mas com finalidades diferentes (passar uma requisição por uma cadeia até ser tratada vs. encapsular uma solicitação em um objeto). O Mediator foca na coordenação de múltiplos participantes em resposta a eventos.


Conclusão

O padrão Mediator é uma ferramenta poderosa para organizar sistemas complexos e caóticos, especialmente aqueles com muitos pontos de interação. Ao eliminar as dependências diretas entre os componentes e centralizar a lógica de coordenação em um objeto dedicado, ele promove um design mais limpo, desacoplado e resiliente a mudanças.

Sua aplicação é ideal em domínios como desenvolvimento de interfaces gráficas (GUIs), fluxos de formulário interativos, processos de negócio com múltiplas etapas ou jogos (onde várias entidades do jogo interagem). Em frameworks como ASP.NET Core, o conceito é aplicado em estruturas como o MediatR library, que implementa o padrão Mediator para desacoplar comandos, consultas e seus handlers.

Dominar este padrão significa adquirir a habilidade de transformar uma rede confusa de dependências em um sistema bem orquestrado, onde cada peça conhece seu papel e se comunica de forma clara e organizada através de um canal central.

Atualizado