<h2>Observer: Reatividade em Tempo Real</h2>
<p>O padrão Observer implementa um sistema de publicação-subscrição onde objetos (observers) se registram para receber notificações quando um assunto (subject) sofre mudanças. É fundamental em aplicações reativas, formulários dinâmicos e eventos globais. A ideia é desacoplar produtor e consumidor de dados, permitindo que múltiplos observadores reajam simultaneamente.</p>
<pre><code class="language-javascript">class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(${this.name} recebeu: ${data});
}
}
const weather = new Subject();
const user1 = new Observer('João');
const user2 = new Observer('Maria');
weather.subscribe(user1);
weather.subscribe(user2);
weather.notify('Chuva esperada'); // Ambos são notificados</code></pre>
<h3>Aplicação Prática: Store de Estado</h3>
<p>Em aplicações modernas, o Observer é usado em gerenciadores de estado. Quando dados mudam, componentes registrados são automaticamente notificados:</p>
<pre><code class="language-javascript">class Store extends Subject {
constructor(initialState = {}) {
super();
this.state = initialState;
}
setState(newState) {
this.state = { ...this.state, ...newState };
this.notify(this.state);
}
getState() {
return this.state;
}
}
const appStore = new Store({ count: 0 });
const component1 = new Observer('Component1');
const component2 = new Observer('Component2');
appStore.subscribe(component1);
appStore.subscribe(component2);
appStore.setState({ count: 1 });
// Component1 recebeu: { count: 1 }
// Component2 recebeu: { count: 1 }</code></pre>
<h2>Mediator: Comunicação Centralizada</h2>
<p>O padrão Mediator reduz o acoplamento entre objetos ao centralizar a lógica de comunicação em um intermediário. Em vez de componentes falarem diretamente uns com os outros, todos conversam através do mediator. É excelente para dashboards complexos, diálogos modais com múltiplos campos e sistemas de chat.</p>
<pre><code class="language-javascript">class Mediator {
constructor() {
this.colleagues = [];
}
register(colleague) {
this.colleagues.push(colleague);
colleague.setMediator(this);
}
send(message, sender) {
this.colleagues.forEach(colleague => {
if (colleague !== sender) {
colleague.receive(message);
}
});
}
}
class Colleague {
constructor(name) {
this.name = name;
this.mediator = null;
}
setMediator(mediator) {
this.mediator = mediator;
}
send(message) {
console.log(${this.name} envia: ${message});
this.mediator.send(message, this);
}
receive(message) {
console.log(${this.name} recebe: ${message});
}
}
const chat = new Mediator();
const alice = new Colleague('Alice');
const bob = new Colleague('Bob');
chat.register(alice);
chat.register(bob);
alice.send('Olá Bob!');
// Alice envia: Olá Bob!
// Bob recebe: Olá Bob!</code></pre>
<h3>Caso Real: Formulário com Validação Interdependente</h3>
<p>Imagine um formulário onde campos dependem uns dos outros. Um mediator simplifica essa lógica:</p>
<pre><code class="language-javascript">class FormMediator {
constructor() {
this.fields = {};
}
registerField(name, field) {
this.fields[name] = field;
field.setMediator(this);
}
validateField(fieldName, value) {
if (fieldName === 'password' && value.length < 8) {
this.fields['confirmPassword'].disable('Senha muito curta');
return false;
}
this.fields['confirmPassword'].enable();
return true;
}
}
class FormField {
constructor(name) {
this.name = name;
this.mediator = null;
this.enabled = true;
}
setMediator(mediator) {
this.mediator = mediator;
}
onChange(value) {
if (this.mediator.validateField(this.name, value)) {
console.log(${this.name}: Válido);
}
}
disable(reason) {
this.enabled = false;
console.log(${this.name} desabilitado: ${reason});
}
enable() {
this.enabled = true;
console.log(${this.name} habilitado);
}
}
const form = new FormMediator();
const pwd = new FormField('password');
const confirm = new FormField('confirmPassword');
form.registerField('password', pwd);
form.registerField('confirmPassword', confirm);
pwd.onChange('abc'); // confirmPassword desabilitado
pwd.onChange('password123'); // confirmPassword habilitado</code></pre>
<h2>Command: Ações Desacopladas e Reversíveis</h2>
<p>O padrão Command encapsula uma solicitação como um objeto, permitindo parametrizar clientes com diferentes requisições, enfileirar operações, e implementar desfazer/refazer. É perfeito para undo/redo, filas de tarefas, macros e botões configuráveis.</p>
<pre><code class="language-javascript">class Command {
execute() {}
undo() {}
}
class LightCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.on();
}
undo() {
this.light.off();
}
}
class Light {
on() {
console.log('Luz acesa');
}
off() {
console.log('Luz apagada');
}
}
class Invoker {
constructor() {
this.history = [];
}
executeCommand(command) {
command.execute();
this.history.push(command);
}
undo() {
const command = this.history.pop();
if (command) command.undo();
}
}
const light = new Light();
const switchLight = new LightCommand(light);
const remote = new Invoker();
remote.executeCommand(switchLight); // Luz acesa
remote.undo(); // Luz apagada</code></pre>
<h3>Editor com Undo/Redo Completo</h3>
<p>Um exemplo mais realista mostra como integrar múltiplos comandos em um editor de texto:</p>
<pre><code class="language-javascript">class Document {
constructor() {
this.content = '';
}
insertText(text) {
this.content += text;
}
deleteText(length) {
this.content = this.content.slice(0, -length);
}
getText() {
return this.content;
}
}
class InsertCommand extends Command {
constructor(document, text) {
super();
this.document = document;
this.text = text;
}
execute() {
this.document.insertText(this.text);
}
undo() {
this.document.deleteText(this.text.length);
}
}
class Editor {
constructor(document) {
this.document = document;
this.history = [];
this.undone = [];
}
type(text) {
const command = new InsertCommand(this.document, text);
command.execute();
this.history.push(command);
this.undone = [];
}
undo() {
if (this.history.length > 0) {
const command = this.history.pop();
command.undo();
this.undone.push(command);
}
}
redo() {
if (this.undone.length > 0) {
const command = this.undone.pop();
command.execute();
this.history.push(command);
}
}
getContent() {
return this.document.getText();
}
}
const doc = new Document();
const editor = new Editor(doc);
editor.type('Olá');
editor.type(' Mundo');
console.log(editor.getContent()); // Olá Mundo
editor.undo();
console.log(editor.getContent()); // Olá
editor.redo();
console.log(editor.getContent()); // Olá Mundo</code></pre>
<h2>Conclusão</h2>
<p>Observer, Mediator e Command resolvem problemas distintos mas complementares: <strong>Observer</strong> cria reatividade automática através de notificações; <strong>Mediator</strong> centraliza comunicação complexa reduzindo acoplamento; <strong>Command</strong> encapsula ações permitindo composição, fila e desfazer. Esses padrões são a base de frameworks modernos—conhecê-los profundamente torna você capaz de arquitetar aplicações escaláveis e manuteníveis. Escolha o padrão certo para cada problema: não use Observer para lógica de negócio centralizada, nem Mediator quando simples callbacks bastam.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://refactoring.guru/design-patterns/observer" target="_blank" rel="noopener noreferrer">Observer Pattern - Refactoring Guru</a></li>
<li><a href="https://refactoring.guru/design-patterns/mediator" target="_blank" rel="noopener noreferrer">Mediator Pattern - Refactoring Guru</a></li>
<li><a href="https://refactoring.guru/design-patterns/command" target="_blank" rel="noopener noreferrer">Command Pattern - Refactoring Guru</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Learn" target="_blank" rel="noopener noreferrer">Design Patterns in JavaScript - MDN Web Docs</a></li>
<li><a href="https://www.patterns.dev/posts/classic-design-patterns/" target="_blank" rel="noopener noreferrer">Learning JavaScript Design Patterns - Addy Osmani</a></li>
</ul>