Pular para o conteúdo

Lista de Exercícios 001 - SOLID

Exercício 1 – Single Responsibility Principle (SRP)

Seção intitulada “Exercício 1 – Single Responsibility Principle (SRP)”

Você recebe uma classe RelatorioVendas:

public class RelatorioVendas {
private final List<Venda> vendas;
private final LocalDate dataInicio;
private final LocalDate dataFim;
public RelatorioVendas(List<Venda> vendas, LocalDate dataInicio, LocalDate dataFim) {
this.vendas = vendas;
this.dataInicio = dataInicio;
this.dataFim = dataFim;
}
public String gerarRelatorio() {
StringBuilder sb = new StringBuilder();
sb.append("Relatório de Vendas\n");
sb.append("Período: ").append(dataInicio).append(" até ").append(dataFim).append("\n\n");
double total = 0.0;
for (Venda venda : vendas) {
sb.append("ID: ").append(venda.getId())
.append(" | Data: ").append(venda.getData())
.append(" | Cliente: ").append(venda.getCliente())
.append(" | Valor: R$ ").append(String.format("%.2f", venda.getValor()))
.append("\n");
total += venda.getValor();
}
sb.append("\nTotal de vendas: R$ ").append(String.format("%.2f", total)).append("\n");
return sb.toString();
}
public void salvarRelatorio(String caminho) {
String conteudo = gerarRelatorio();
try (FileWriter writer = new FileWriter(caminho)) {
writer.write(conteudo);
} catch (IOException e) {
throw new RuntimeException("Erro ao salvar relatório em arquivo: " + caminho, e);
}
}
public void enviarRelatorioPorEmail(String email) {
String conteudo = gerarRelatorio();
// Simulação de envio de email (sem integração real)
System.out.println("Enviando relatório para " + email + "...");
System.out.println("Assunto: Relatório de Vendas");
System.out.println("Corpo da mensagem:\n" + conteudo);
System.out.println("Relatório enviado com sucesso (simulado).");
}
// Classe auxiliar para simular uma venda real
public static class Venda {
private final String id;
private final LocalDate data;
private final String cliente;
private final double valor;
public Venda(String id, LocalDate data, String cliente, double valor) {
this.id = id;
this.data = data;
this.cliente = cliente;
this.valor = valor;
}
public String getId() {
return id;
}
public LocalDate getData() {
return data;
}
public String getCliente() {
return cliente;
}
public double getValor() {
return valor;
}
}
}
  1. Explique por que essa classe viola o SRP.
  2. Liste pelo menos três responsabilidades diferentes que ela está assumindo.
  3. Proponha uma nova estrutura de classes que distribua essas responsabilidades de forma mais adequada.

Imagine um sistema de cálculo de frete onde existe uma classe CalculadoraFrete com um switch/if-else interno que decide o valor com base no tipo de entrega: Normal, Rápida, Expressa.

CalculadoraFrete.java
public class CalculadoraFrete {
public double calcularFrete(String tipoEntrega, double peso) {
switch (tipoEntrega) {
case "Normal":
return peso * 5.0;
case "Rápida":
return peso * 10.0;
case "Expressa":
return peso * 20.0;
default:
throw new IllegalArgumentException("Tipo de entrega desconhecido: " + tipoEntrega);
}
}
}

A empresa deseja adicionar novos tipos de entrega (por exemplo, Entrega Noturna, Entrega Internacional) sem precisar modificar o código existente.

  1. Desenhe um diagrama de classes simples que respeite o OCP utilizando abstrações.
  2. No diagrama, deixe claro:
    • Uso de interfaces ou classes abstratas
    • Como cada tipo de entrega é representado
    • Como a CalculadoraFrete passa a depender da abstração, e não dos tipos concretos.

Exercício 3 – Liskov Substitution Principle (LSP)

Seção intitulada “Exercício 3 – Liskov Substitution Principle (LSP)”

Considere uma hierarquia de classes onde Pato é a classe base e PatoDeBorracha é uma subclasse.
A classe Pato possui métodos como voar(), grasnar() e nadar().
PatoDeBorracha sobrescreve esses métodos para:

  • voar() lançar uma exceção ou não fazer nada (porque pato de borracha não voa);
  • grasnar() emitir um som de apito engraçado em vez do grasnar esperado;
  • nadar() ficar apenas boiando sem se mover.
  1. Explique, com suas palavras, quando uma subclasse viola o LSP.
  2. Argumente se PatoDeBorracha é ou não um bom subtipo de Pato nesse contexto, considerando quem usa a classe base esperando “um pato de verdade”.
  3. Descreva um cenário de uso em que o comportamento de PatoDeBorracha pode surpreender quem espera um Pato normal (por exemplo, um simulador de voo de patos que, de repente, recebe um PatoDeBorracha e nada mais funciona como esperado), mostrando a quebra do LSP.

Exercício 4 – Interface Segregation Principle (ISP)

Seção intitulada “Exercício 4 – Interface Segregation Principle (ISP)”

Suponha uma interface ITrabalhador com os métodos: trabalhar(), comer(), dormir(). Ela é implementada por Robo e Funcionario.

  1. Explique por que essa interface pode violar o ISP.
  2. Proponha um conjunto de interfaces menores que segregue melhor as responsabilidades.
  3. Escreva a nova definição dessas interfaces em pseudocódigo (ou sintaxe de alguma linguagem OO) e indique quais seriam implementadas por Robo e por Funcionario.

Exercício 5 – Dependency Inversion Principle (DIP)

Seção intitulada “Exercício 5 – Dependency Inversion Principle (DIP)”

Um módulo de alto nível ProcessadorDePagamento instancia diretamente uma classe concreta PagInseguro usando new, e chama seus métodos.

  1. Explique por que isso fere o DIP.
  2. Desenhe um diagrama simples mostrando a dependência atual (módulo de alto nível → classe concreta).
  3. Redesenhe o diagrama mostrando uma solução que siga o DIP, introduzindo:
    • Uma abstração (interface) para o serviço de pagamento
    • A inversão da dependência (módulo de alto nível depende da abstração).
  4. Descreva brevemente por quais maneiras o desenvolvedor poderia injetar a implementação concreta.