Desafio Java

Sistema de relatórios com template method e hook

Enunciado

Crie uma classe abstrata Relatorio com o template method gerar() que executa: cabecalho(), corpo(), rodape() e, opcionalmente, anexo() (um hook). O hook anexo() tem implementação padrão vazia e pode ser sobrescrito. cabecalho() e rodape() são concretos. corpo() é abstrato. Implemente RelatorioPDF e RelatorioCSV, sendo que apenas RelatorioCSV sobrescreve anexo() para imprimir "Anexo: dados brutos". No main, gere os dois relatórios.

Requisitos

  • Relatorio deve ser abstrata e ter gerar() como final.
  • corpo() deve ser abstrato e implementado nas subclasses.
  • anexo() deve ser um hook concreto com corpo vazio.
  • Apenas RelatorioCSV sobrescreve anexo().
  • A saída deve mostrar a sequência completa para cada relatório.

Código inicial

abstract class Relatorio {
    public final void gerar() {
        // implemente a sequência
    }

    protected void cabecalho() {
        System.out.println("Cabeçalho: Relatório");
    }

    protected abstract void corpo();

    protected void rodape() {
        System.out.println("Rodapé: Fim do relatório");
    }

    protected void anexo() {
        // hook vazio
    }
}

class RelatorioPDF extends Relatorio {
    @Override
    protected void corpo() {
        // implemente
    }
}

class RelatorioCSV extends Relatorio {
    @Override
    protected void corpo() {
        // implemente
    }

    @Override
    protected void anexo() {
        // implemente
    }
}

public class Main {
    public static void main(String[] args) {
        Relatorio pdf = new RelatorioPDF();
        pdf.gerar();
        System.out.println("---");
        Relatorio csv = new RelatorioCSV();
        csv.gerar();
    }
}

Saída esperada

Cabeçalho: Relatório
Corpo: Conteúdo em PDF
Rodapé: Fim do relatório
---
Cabeçalho: Relatório
Corpo: Conteúdo em CSV
Rodapé: Fim do relatório
Anexo: dados brutos
Ver dica

No método gerar(), chame cabecalho(), corpo(), rodape() e depois anexo(). O hook anexo() já tem implementação vazia na classe base; apenas RelatorioCSV o sobrescreve.

Mostrar solução
abstract class Relatorio {
    // Template method: define a ordem dos passos
    public final void gerar() {
        cabecalho();
        corpo();
        rodape();
        anexo(); // hook opcional
    }

    protected void cabecalho() {
        System.out.println("Cabeçalho: Relatório");
    }

    protected abstract void corpo();

    protected void rodape() {
        System.out.println("Rodapé: Fim do relatório");
    }

    // Hook: implementação padrão vazia
    protected void anexo() {
    }
}

class RelatorioPDF extends Relatorio {
    @Override
    protected void corpo() {
        System.out.println("Corpo: Conteúdo em PDF");
    }
}

class RelatorioCSV extends Relatorio {
    @Override
    protected void corpo() {
        System.out.println("Corpo: Conteúdo em CSV");
    }

    @Override
    protected void anexo() {
        System.out.println("Anexo: dados brutos");
    }
}

public class Main {
    public static void main(String[] args) {
        Relatorio pdf = new RelatorioPDF();
        pdf.gerar();
        System.out.println("---");
        Relatorio csv = new RelatorioCSV();
        csv.gerar();
    }
}

Passo a passo

  1. Relatorio é abstrata e define gerar() como final para fixar a ordem dos passos.
  2. cabecalho() e rodape() são concretos e compartilhados.
  3. corpo() é abstrato e obriga as subclasses a fornecerem o conteúdo específico.
  4. anexo() é um hook: método concreto com corpo vazio, que pode ser sobrescrito opcionalmente.
  5. RelatorioPDF implementa corpo() e não mexe no hook.
  6. RelatorioCSV implementa corpo() e sobrescreve anexo() para adicionar dados brutos.
  7. No main, chamamos gerar() para cada relatório, e a saída reflete as diferenças.

Por que funciona

O Template Method permite que a classe base controle o fluxo do algoritmo, enquanto as subclasses personalizam partes específicas. O hook anexo() é um ponto de extensão opcional: se não for sobrescrito, nada acontece. Isso é útil para adicionar comportamento sem quebrar o esqueleto.

Erros comuns

  • Esquecer de chamar anexo() no template method: o hook nunca seria executado.
  • Declarar anexo() como abstrato: forçaria todas as subclasses a implementá-lo, perdendo a opcionalidade.
  • Não marcar gerar() como final: subclasses poderiam alterar a ordem dos passos.

Outra forma de resolver

Poderia usar uma interface com métodos default para o hook, mas classes abstratas permitem melhor controle de visibilidade e estado compartilhado.

Saída esperada

Cabeçalho: Relatório
Corpo: Conteúdo em PDF
Rodapé: Fim do relatório
---
Cabeçalho: Relatório
Corpo: Conteúdo em CSV
Rodapé: Fim do relatório
Anexo: dados brutos