Pular para o conteúdo

Atividade 10: Decorator

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

  1. Um serviço de envio de e-mails que, em alguns fluxos, precisa registrar log, em outros comprimir os anexos e em outros fazer os dois, sem criar uma classe para cada combinação.
  2. Um editor de imagens que permite aplicar filtros em cadeia (brilho, contraste, sépia) em qualquer ordem e quantidade sobre uma foto.
  3. Um repositório de produtos usado pelo sistema todo; para um único caso de uso de alta leitura, é preciso adicionar cache em memória, sem mudar a interface que os demais consumidores já usam.
  4. Um catálogo de livros em que cada Livro tem apenas título, autor e preço com estrutura simples e estável que nunca ganha variações de comportamento.
  5. Uma concessionária em que todo carro vendido sai exatamente igual, sempre com o mesmo pacote opcional embutido na classe Carro.

Para cada item, responda:

  • “Faz sentido usar Decorator” ou “Não faz sentido usar Decorator”.
  • Explique rapidamente o porquê (combinação dinâmica de responsabilidades, explosão de subclasses, interface estável, overengineering, etc.).

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

Na aula, usamos a pizza montada: a massa é a base, os ingredientes são adicionados por cima, e no fim continua sendo uma pizza, só que “decorada”. Agora crie uma outra analogia.

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

  • exista um objeto base que pode receber acréscimos opcionais, combináveis e empilháveis,
  • cada acréscimo não muda o que o objeto é, apenas soma algo a ele (mantendo o “mesmo tipo” de coisa),
  • e combinar acréscimos por duplicação antecipada (uma versão para cada combinação) seja inviável.

Considere o código Java abaixo, usado na tela de pedidos de uma cafeteria:

public class Cafe {
boolean comLeite;
boolean comChantilly;
boolean comCanela;
public double custo() {
double total = 5.0;
if (comLeite) total += 1.5;
if (comChantilly) total += 2.0;
if (comCanela) total += 0.5;
return total;
}
public String getDescricao() {
String descricao = "Café";
if (comLeite) descricao += " com leite";
if (comChantilly) descricao += " com chantilly";
if (comCanela) descricao += " com canela";
return descricao;
}
}

Responda:

  1. Por que modelar os complementos como flags booleanas dentro de Cafe é um problema de design? O que acontece com essa classe a cada novo complemento (ex.: café com caramelo)?
  2. Que bugs e confusões esse código tende a gerar (ex.: esquecer de atualizar um dos métodos, combinações que fazem sentido juntas, comportamentos que não são só “somar preço”)?
  3. Proponha uma solução usando o padrão Decorator, explicando os papéis: a interface (Bebida), o componente concreto (Cafe), o decorator abstrato e os decorators concretos (Leite, Chantilly). Como as combinações passam a ser montadas sem tocar na classe Cafe?

Imagine que você foi contratado para criar o sistema de pedidos de uma cafeteria que acabou de abrir.

A casa serve uma bebida base (o café) e permite que o cliente combine complementos: leite, chantilly, calda de caramelo, entre outros. A equipe quer poder lançar novos complementos toda semana sem alterar a bebida base nem os complementos antigos.

Implemente, em Java, um sistema de bebidas que some complementos dinamicamente, usando o padrão Decorator.

  1. Crie a interface Bebida (o Component):
    • Métodos: String getDescricao() e double custo().
  2. Crie o componente concreto Cafe:
    • getDescricao() retorna "Café".
    • custo() retorna o preço base (ex.: 5.0).
  3. Crie o decorator abstrato Complemento:
    • Um campo protected final Bebida bebida (recebido no construtor).
    • Implementa a interface Bebida (pode deixar os métodos abstratos).
  4. Crie os decorators concretos:
    • Leite: soma " com leite" à descrição e 1.5 ao custo.
    • Chantilly: soma " com chantilly" à descrição e 2.0 ao custo.
    • Cada um recebe uma Bebida no construtor e delega ao objeto interno.
  5. Crie uma classe de teste, por exemplo Main, que:
    • Monte e imprima as combinações: um Cafe puro, um Cafe com Leite, e um Cafe com Leite e Chantilly (a ordem dos complementos deve ficar visível na descrição).
    • Mostre que todas as combinações usam a mesma interface Bebida e que os custos somam camada por camada.
    • Explique, em um comentário ou README, como adicionar um novo complemento (ex.: CaldaDeCaramelo por +3.0) exigiria apenas uma nova classe Complemento, sem alterar Cafe, Leite nem a Main.