Pular para o conteúdo

Mediator

Definir um objeto que encapsula como um conjunto de objetos interage. O Mediator promove o baixo acoplamento ao evitar que os objetos se refiram uns aos outros explicitamente, permitindo variar suas interações independentemente.

Quando muitos objetos precisam colaborar, referências diretas entre todos criam uma malha N-para-N: cada objeto conhece vários outros, a lógica de interação fica espalhada e qualquer mudança se propaga por toda a teia. O caso clássico é uma tela com muitos widgets cujo comportamento depende do estado uns dos outros (habilitar/desabilitar, validar, sincronizar).

  • Mediator: interface que declara os métodos de comunicação usados pelos colegas (ex.: notify(sender, event)).
  • ConcreteMediator: conhece e coordena os colegas, concentrando a lógica de interação.
  • Colleague: cada componente conhece apenas o mediador, não os outros colegas; ao mudar de estado, avisa o mediador.

Prós

  • Reduz acoplamento entre colegas: de N-para-N para N-para-1.
  • Centraliza e torna explícita a lógica de interação, facilitando alterá-la.
  • Colegas ficam mais reutilizáveis e simples.

Contras

  • Risco de o mediador virar God Object, absorvendo toda a complexidade que antes estava distribuída.
  • Ponto central de mudança e de falha.
  • Pode esconder o fluxo real de dados sob camadas de notificação.

Quando os objetos têm poucas interações simples e diretas, ou quando a coordenação natural pertence a um único agregado. Introduzir um mediador para duas ou três colaborações triviais só adiciona indireção.

public class LoginForm {
private final UsernameField username = new UsernameField();
private final PasswordField password = new PasswordField();
private final SubmitButton submit = new SubmitButton();
public void onUsernameChanged() {
submit.setEnabled(!username.isEmpty() && !password.isEmpty());
}
public void onPasswordChanged() {
submit.setEnabled(!username.isEmpty() && !password.isEmpty());
}
}

O form já centraliza as referências, mas a regra de habilitação está duplicada nos handlers e cresce a cada novo widget: adicionar um campo “lembrar-me” ou um captcha obriga a editar todos os on*Changed e a espalhar a condição. É essa teia de atualizações cruzadas — que em UIs reais degenera em widgets se referenciando diretamente (N-para-N) — que o mediador elimina.

public interface DialogMediator {
void notify(Component sender, String event);
}
public abstract class Component {
protected DialogMediator mediator;
public void setMediator(DialogMediator mediator) { this.mediator = mediator; }
}
public class TextField extends Component {
private String value = "";
public void setValue(String value) {
this.value = value;
mediator.notify(this, "changed");
}
public boolean isEmpty() { return value.isBlank(); }
}
public class Button extends Component {
private boolean enabled;
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean isEnabled() { return enabled; }
}

Mediador concreto com a lógica de interação:

public class LoginDialog implements DialogMediator {
private final TextField username = new TextField();
private final TextField password = new TextField();
private final Button submit = new Button();
public LoginDialog() {
username.setMediator(this);
password.setMediator(this);
submit.setMediator(this);
}
public void notify(Component sender, String event) {
if (event.equals("changed") && (sender == username || sender == password)) {
submit.setEnabled(!username.isEmpty() && !password.isEmpty());
}
}
public TextField username() { return username; }
public TextField password() { return password; }
public Button submit() { return submit; }
}

Os campos não se conhecem; toda a coordenação vive no LoginDialog.

classDiagram
    class DialogMediator {
        <<interface>>
        +notify(sender, event) void
    }
    class LoginDialog {
        -TextField username
        -TextField password
        -Button submit
        +notify(sender, event) void
    }
    class Component {
        #DialogMediator mediator
        +setMediator(m) void
    }
    class TextField
    class Button
    DialogMediator <|.. LoginDialog
    Component <|-- TextField
    Component <|-- Button
    Component o--> DialogMediator
    LoginDialog o--> TextField
    LoginDialog o--> Button

Ambos reduzem acoplamento na comunicação, mas de formas opostas:

  • Observer: comunicação descentralizada e um-para-muitos. O subject não conhece os observers concretos; a relação é de broadcast de mudanças de estado. Não há um coordenador — cada observer reage por conta própria.
  • Mediator: comunicação centralizada e N-para-1. Há um coordenador que conhece os colegas e implementa a lógica de interação entre eles. A intenção é orquestrar colaborações complexas, não apenas notificar.

Regra prática: use Observer quando um evento deve simplesmente propagar a muitos interessados independentes; use Mediator quando a lógica de “quem faz o quê em resposta a quê” é rica e precisa morar num lugar. Um mediador pode inclusive usar Observer internamente para receber os avisos dos colegas.

  • Mediador God Object: concentrar regras de negócio e conhecimento de todos os colegas até se tornar uma classe monstruosa e frágil. Divida por contexto e mantenha o mediador focado em interação, não em regra de domínio.
  • Colegas que continuam se referenciando por baixo do mediador, anulando o desacoplamento.
  • Usar Mediator onde Observer bastaria (simples broadcast), pagando por centralização desnecessária.
  • Lógica de domínio dentro do mediador de UI, misturando camadas.

1. Que tipo de relação de comunicação o Mediator elimina e por qual ele a substitui?

Resposta

Elimina as relações diretas N-para-N entre colegas (cada objeto conhecendo vários outros) e as substitui por relações N-para-1: todos conversam apenas com o mediador, que concentra a lógica de interação.

2. Qual o principal risco de design ao adotar Mediator?

Resposta

O mediador virar um God Object: ao absorver o conhecimento de todos os colegas e a lógica de interação, pode inchar até se tornar uma classe central frágil, difícil de manter e testar. A mitigação é mantê-lo focado em coordenação e dividir por contexto.

3. Como distinguir Mediator de Observer?

Resposta

Observer é descentralizado e um-para-muitos: o subject faz broadcast sem conhecer os observers concretos. Mediator é centralizado e N-para-1: um coordenador conhece os colegas e implementa a lógica de quem reage a quê. Observer notifica; Mediator orquestra.

4. Em que caso introduzir um Mediator é overengineering?

Resposta

Quando há poucas interações simples e diretas entre objetos, ou quando a coordenação pertence naturalmente a um único agregado. Um mediador para duas ou três colaborações triviais só adiciona indireção e um ponto central desnecessário.