Implementação Básica

Introdução à Implementação Prática

Após compreendermos os fundamentos teóricos do padrão Mediator, é essencial consolidar esse conhecimento através da implementação concreta. Nesta seção, analisaremos uma implementação básica em C#/.NET que demonstra como o padrão Mediator pode ser aplicado para desacoplar componentes de interface do usuário em um diálogo simples. Este exemplo servirá como base para entendermos a estrutura, os componentes e o fluxo de comunicação característico do padrão.


Cenário de Exemplo: Diálogo de Confirmação

Vamos considerar um cenário prático comum em aplicações: um diálogo de confirmação que requer interação do usuário. Este diálogo contém três componentes básicos:

  1. TextBox: Um campo de texto onde o usuário deve digitar seu nome para confirmação.

  2. Checkbox: Uma caixa de seleção que o usuário deve marcar para aceitar os termos.

  3. Button: Um botão de confirmação que só deve estar habilitado quando ambas as condições forem satisfeitas:

    • O campo de texto não está vazio

    • A caixa de seleção está marcada

A lógica de negócio é clara: o botão só pode ser usado quando o usuário forneceu seu nome e aceitou os termos. Sem o Mediator, implementar essa lógica criaria dependências diretas entre os três componentes.


Estrutura do Projeto e Configuração Inicial

Para iniciar nossa implementação, criamos um projeto de console básico:

dotnet new console -o MyMediatorApp01

Este comando cria uma nova aplicação de console que servirá como ambiente para nosso exemplo. A simplicidade de uma aplicação de console permite focar na essência do padrão sem a complexidade adicional de frameworks de UI.

No arquivo Program.cs, começamos configurando o cenário inicial:

Esta configuração inicial estabelece a estrutura fundamental do padrão Mediator:

  1. Instanciação dos Componentes: Criamos três componentes concretos que representam elementos de UI.

  2. Criação do Mediador: Instanciamos o DialogMediator, que será o coordenador central.

  3. Associação Bidirecional: Conectamos cada componente ao mediador através do método SetMediator(). Esta é uma etapa crucial - o mediador conhece os componentes (através das propriedades) e os componentes conhecem o mediador (através da referência _mediator).


Definição da Interface do Mediador

O coração do padrão Mediator reside na interface que define como os componentes se comunicam com o mediador:

Esta interface minimalista define um único método:

  • Notify: O método pelo qual todos os componentes comunicam eventos ao mediador.

  • sender: O componente que originou o evento, permitindo ao mediador identificar quem está notificando.

  • @event: Uma string que descreve qual evento ocorreu. O uso de @event (com @) é necessário porque event é uma palavra reservada em C#.

A simplicidade desta interface é intencional - ela estabelece um contrato universal para comunicação, independente dos componentes específicos envolvidos.


Componente Base Abstrato

Para evitar repetição de código e garantir consistência, definimos uma classe base abstrata para todos os componentes:

Esta classe base fornece:

  • Referência ao Mediador: Cada componente mantém uma referência protegida ao mediador.

  • Método de Configuração: O método SetMediator permite injetar o mediador após a criação do componente.

A abordagem com classe base é comum, mas não obrigatória. Alternativamente, poderíamos usar uma interface IComponent com o método SetMediator, ou até mesmo injetar o mediador no construtor de cada componente.


Implementação dos Componentes Concretos

TextBox: Componente de Entrada de Texto

Características:

  • Estado Interno: Mantém o texto digitado na propriedade Text.

  • Método de Ação: Input simula a digitação do usuário.

  • Notificação ao Mediador: Após cada alteração, notifica o mediador com o evento "TextChanged".

  • Baixo Acoplamento: O TextBox não tem conhecimento sobre o Checkbox ou Button - apenas notifica o mediador.

Checkbox: Componente de Seleção

Características:

  • Estado Booleano: Representa se a caixa está marcada ou não.

  • Ação de Alternância: Toggle inverte o estado atual.

  • Notificação: Comunica ao mediador quando o estado muda ("CheckedChanged").

Button: Componente de Ação

Características:

  • Estado de Habilitação: Controla se o botão pode ser clicado.

  • Comportamento Condicional: A ação Click verifica se está habilitado antes de processar.

  • Notificação Pós-Ação: Mesmo quando desabilitado, o botão notifica o mediador sobre a tentativa de clique.


Implementação do Mediador Concreto

O DialogMediator é onde reside toda a lógica de coordenação:

Análise Detalhada:

  1. Propriedades Específicas: Diferente da interface genérica IMediator, o mediador concreto conhece os tipos específicos dos componentes. Este é um trade-off comum - quanto mais genérico o mediador, mais reutilizável, mas também mais complexo.

  2. Filtro de Eventos: O mediador verifica se o evento recebido é relevante para sua lógica de coordenação. Neste caso, só reage a "TextChanged" e "CheckedChanged", ignorando "ButtonClicked".

  3. Lógica de Coordenação Centralizada: A regra de negócio - "habilitar botão apenas quando texto não vazio E checkbox marcado" - está concentrada em um único local.

  4. Ação sobre Componentes: O mediador modifica diretamente o estado dos componentes (definindo Button.IsEnabled).

  5. Transparência: As mensagens de log ([Mediator]...) tornam visível o processo de decisão, facilitando o entendimento durante a execução.


Simulação de Interação do Usuário

O código principal simula uma sequência de interações do usuário:

Fluxo de Execução Analisado:

  1. Clique Inicial (button.Click()):

    • Botão está desabilitado por padrão

    • Mensagem: "[Button] O botão está desabilitado"

    • Mediador é notificado, mas ignora o evento

  2. Entrada de Texto (textBox.Input("João")):

    • TextBox atualiza seu estado interno

    • Notifica mediador com "TextChanged"

    • Mediador avalia: texto existe (true) mas checkbox não marcado (false)

    • Botão permanece desabilitado

    • Saída: "[TextBox] João""[Mediator] Botão desabilitado"

  3. Marcação do Checkbox (checkbox.Toggle()):

    • Checkbox alterna para true

    • Notifica mediador com "CheckedChanged"

    • Mediador avalia: texto existe (true) E checkbox marcado (true)

    • Habilita o botão

    • Saída: "[Checkbox] Marcado: True""[Mediator] Botão habilitado"

  4. Clique Válido (button.Click()):

    • Botão agora está habilitado

    • Processa o clique

    • Saída: "[Button] Clique processado"

  5. Desmarcação do Checkbox (checkbox.Toggle()):

    • Retorna à condição inválida

    • Desabilita o botão novamente

  6. Tentativa de Clique Inválido (button.Click()):

    • Confirma que o botão está desabilitado


Execução e Saída do Programa

Ao executar dotnet run, observamos a seguinte sequência de saída:

Análise da Saída:

  1. Rastreabilidade Clara: Cada ação é claramente identificada pelo componente de origem ([TextBox], [Checkbox], [Button], [Mediator]).

  2. Visibilidade da Decisão: As mensagens do mediador ([Mediator] Botão habilitado/desabilitado) tornam explícita a lógica de decisão em tempo real.

  3. Fluxo Síncrono: A notificação e resposta são imediatas - quando um componente muda, o mediador reage instantaneamente.

  4. Consistência de Estado: O estado do sistema (botão habilitado/desabilitado) sempre reflete corretamente as condições de negócio.


Pontos de Atenção e Considerações

Vantagens Evidenciadas nesta Implementação

  1. Desacoplamento Efetivo: Nenhum componente referencia outro diretamente.

  2. Centralização da Lógica: A regra de habilitação está em um único local.

  3. Facilidade de Modificação: Para alterar a lógica (ex.: exigir texto com mínimo de 3 caracteres), modifica-se apenas o mediador.

  4. Testabilidade: Cada componente pode ser testado isoladamente, com um mediador mock.

Limitações e Melhorias Potenciais

  1. Tipagem Forte no Mediador: O DialogMediator conhece tipos concretos, limitando reutilização. Poderíamos usar interfaces (ITextBox, ICheckbox, IButton).

  2. Strings para Eventos: O uso de strings para eventos é propenso a erros (erros de digitação). Enums ou constantes seriam mais seguros:

  1. Configuração Manual: A associação entre componentes e mediador é manual. Em aplicações reais, um contêiner de DI (Injeção de Dependência) poderia gerenciar isso.

  2. Mediador como "God Object": Em sistemas maiores, um único mediador pode tornar-se muito complexo. A solução é criar múltiplos mediadores especializados.

  3. Ausência de Suporte a Eventos Assíncronos: Em aplicações com UI real, muitas interações são assíncronas.


Conclusão da Implementação Básica

Esta implementação básica do padrão Mediator em C# demonstra os conceitos fundamentais de forma prática e acessível. Através de um exemplo concreto de diálogo de confirmação, vimos:

  1. Como estruturar os componentes, a interface do mediador e o mediador concreto.

  2. Como estabelecer o canal de comunicação bidirecional entre componentes e mediador.

  3. Como centralizar a lógica de coordenação em um único ponto.

  4. Como os componentes permanecem desacoplados, conhecendo apenas o mediador.

  5. Como o fluxo de notificação e resposta mantém o sistema consistente.

Esta implementação serve como base sólida para compreender padrões mais avançados e bibliotecas como o MediatR, que aplicam os princípios do Mediator em cenários de aplicação mais complexos, como CQRS e mensagens entre camadas da aplicação.

O verdadeiro poder do padrão Mediator revela-se quando aplicado a sistemas com dezenas ou centenas de componentes inter-relacionados, onde a abordagem tradicional de comunicação direta tornaria o código praticamente impossível de manter e evoluir.

chevron-rightProgram.cshashtag

Atualizado