Pular para o conteúdo

Atividade 05: Factory Method

Para cada cenário abaixo, indique se o padrão Factory Method é apropriado ou não e justifique em 2–3 frases.

  1. Um serviço de notificações que precisa enviar mensagens por email, SMS e push. O fluxo de envio é sempre o mesmo (montar a mensagem, enviar, registrar log), mas cada canal entrega de um jeito diferente, e novos canais entram frequentemente.
  2. Uma classe simples Ponto com apenas dois campos obrigatórios (x, y), utilizada em um sistema de CAD e instanciada dezenas de vezes por segundo através do construtor tradicional.
  3. Um framework de exportação de relatórios (PDF, CSV, Excel). O módulo principal não deve conhecer as classes concretas de exportação, e novas exportações devem entrar apenas com novas subclasses, sem alterar o fluxo de geração.
  4. Uma aplicação que precisa criar um cliente HTTP diferente conforme o ambiente: uma implementação mock nos testes e uma real (OkHttp) em produção, sem que o restante do código mude.
  5. Uma classe Produto com três campos obrigatórios (nome, preco, quantidadeEstoque), criada em um único ponto do sistema através do construtor tradicional e sem variações.

Para cada item, responda:

  • “Faz sentido usar Factory Method” ou “Não faz sentido usar Factory Method”.
  • Explique rapidamente o porquê (acoplamento com classes concretas, variação de criação, extensibilidade, overengineering, etc.).

Crie uma analogia própria para explicar o padrão Factory Method para alguém que não é da área de TI.

Na aula, usamos uma rede de restaurantes: o processo de atendimento é o mesmo em todas as lojas, mas cada loja (hamburgueria, pizzaria) monta o seu próprio produto, sem que o cliente precise saber como o prato é preparado. Agora crie uma outra analogia.

Descreva uma situação do mundo real em que:

  • exista um processo fixo que usa um produto criado “na hora”,
  • cada unidade/equipe decida qual produto concreto será criado,
  • e o processo principal continue funcionando quando um novo tipo de produto é adicionado.

Considere o código Java abaixo, usado para enviar notificações aos usuários de um sistema:

public class NotificadorService {
public void enviar(String canal, String destinatario, String mensagem) {
if (canal.equals("email")) {
new EmailNotificador().enviar(destinatario, mensagem);
} else if (canal.equals("sms")) {
new SmsNotificador().enviar(destinatario, mensagem);
} else if (canal.equals("push")) {
new PushNotificador().enviar(destinatario, mensagem);
}
}
}
public class EmailNotificador {
public void enviar(String destinatario, String mensagem) { /* ... */ }
}
public class SmsNotificador {
public void enviar(String destinatario, String mensagem) { /* ... */ }
}
public class PushNotificador {
public void enviar(String destinatario, String mensagem) { /* ... */ }
}

Responda:

  1. Por que essa forma de escolher o canal com if/else é um problema de design?
  2. O que precisa ser alterado para adicionar um novo canal (por exemplo, WhatsApp)? Que riscos essa mudança traz para o código que já foi testado?
  3. Proponha uma solução usando o padrão Factory Method, explicando em linhas gerais: a interface do produto (Notificador), o criador abstrato com o método fábrica e os criadores concretos por canal.

Acesse os seguintes arquivos em um projeto open source:

Esse exemplo implementa o Factory Method com ferreiros (blacksmiths): a interface Blacksmith declara o método fábrica manufactureWeapon(...), e cada ferreiro concreto (como o ElfBlacksmith, ou o OrcBlacksmith do mesmo pacote) decide qual arma criar. É a mesma estrutura vista em aula com a Logistica e o método criarTransporte().

Responda:

  1. Que papel a interface Blacksmith exerce no padrão? Qual é o método fábrica declarado nela e qual o seu tipo de retorno?
  2. No ElfBlacksmith, o que o método manufactureWeapon(...) retorna? Por que o tipo de retorno é a interface Weapon e não a classe concreta (ElfWeapon)?
  3. O cliente do exemplo (a classe App do mesmo pacote) trabalha com Blacksmith e Weapon — abstrações — e troca de ferreiro (elfo ou orc). O que seria necessário para adicionar um novo ferreiro (por exemplo, um anão) sem alterar o código que usa as armas? Relacione com o princípio OCP.

Imagine que você foi contratado para criar o Sistema de Notificações de um portal acadêmico.

O sistema precisa notificar os alunos por diferentes canais (email, SMS e push) sempre seguindo o mesmo fluxo:

  • montar a mensagem,
  • escolher o canal,
  • enviar,
  • registrar no log.

Como o fluxo é sempre o mesmo e só muda qual canal é criado, repetir o envio com new para cada canal deixaria o código acoplado às classes concretas.

Implemente, em Java, um sistema que centralize a criação do notificador em um método fábrica, usando o padrão Factory Method.

  1. Crie a interface Notificador:
    • Contrato: void enviar(String destinatario, String mensagem).
  2. Crie pelo menos três produtos concretos:
    • EmailNotificador, SmsNotificador e PushNotificador, cada um implementando Notificador e imprimindo (ou registrando) uma mensagem que identifique o canal usado.
  3. Crie a classe abstrata NotificacaoService (o Creator):
    • Um método público notificar(String destinatario, String mensagem) que representa o fluxo comum (montar mensagem, chamar o método fábrica, enviar, registrar log).
    • Um método fábrica abstrato protected abstract Notificador criarNotificador();.
  4. Crie os criadores concretos:
    • EmailService, SmsService e PushService, que estendem NotificacaoService e sobrescrevem criarNotificador() devolvendo o notificador correspondente.
  5. Crie uma classe de teste, por exemplo Main, que:
    • Instancie EmailService, SmsService e PushService.
    • Envie a mesma mensagem pelos três canais usando o método notificar(...).
    • Mostre que o fluxo é o mesmo e apenas o canal criado muda (ex.: a saída identifica o canal).
    • Explique, em um comentário ou README, como adicionar um novo canal (ex.: WhatsApp) exigiria apenas uma nova subclasse de Notificador e uma nova subclasse de NotificacaoService, sem alterar o fluxo existente.