Desafio TypeScript

Pipeline de processamento de dados com Template Method e hook

Enunciado

Crie uma classe abstrata Pipeline com o método concreto executar(dados: string[]): void que executa as etapas na ordem: preProcessar, processar, posProcessar. preProcessar e processar são abstratos. posProcessar é um hook (método concreto com implementação vazia) que as subclasses podem sobrescrever. Implemente PipelineMaiusculo (transforma cada string em maiúsculas) e PipelineFiltro (remove strings vazias). O método executar deve imprimir o array resultante no formato [ "A", "B" ].

Requisitos

  • Pipeline deve ser abstrata e ter executar concreto.
  • preProcessar e processar devem ser abstratos.
  • posProcessar deve ser um hook concreto que pode ser sobrescrito.
  • As subclasses devem produzir a saída correta para os dados fornecidos.

Código inicial

abstract class Pipeline {
  executar(dados: string[]): void {
    // chame preProcessar, processar e posProcessar
  }

  protected abstract preProcessar(dados: string[]): string[];
  protected abstract processar(dados: string[]): string[];

  protected posProcessar(dados: string[]): string[] {
    return dados; // hook padrão
  }
}

// Implemente PipelineMaiusculo e PipelineFiltro

Saída esperada

[ "A", "B", "C" ]
[ "A", "B", "C" ]
Ver dica

No executar, encadeie as três etapas: dados = this.preProcessar(dados), depois processar e por fim posProcessar. Para formatar o array, use JSON.stringify ou uma template string com join.

Mostrar solução
abstract class Pipeline {
  executar(dados: string[]): void {
    // Template Method: encadeia as etapas
    let resultado = this.preProcessar(dados);
    resultado = this.processar(resultado);
    resultado = this.posProcessar(resultado);
    console.log(`[ ${resultado.map(s => `"${s}"`).join(", ")} ]`);
  }

  protected abstract preProcessar(dados: string[]): string[];
  protected abstract processar(dados: string[]): string[];

  // Hook: implementação padrão que pode ser sobrescrita
  protected posProcessar(dados: string[]): string[] {
    return dados;
  }
}

class PipelineMaiusculo extends Pipeline {
  protected preProcessar(dados: string[]): string[] {
    return dados; // nenhuma pré-transformação
  }

  protected processar(dados: string[]): string[] {
    return dados.map(s => s.toUpperCase());
  }
}

class PipelineFiltro extends Pipeline {
  protected preProcessar(dados: string[]): string[] {
    return dados.filter(s => s.trim() !== ""); // remove vazios
  }

  protected processar(dados: string[]): string[] {
    return dados; // nenhuma transformação adicional
  }

  // Sobrescreve o hook para adicionar algo
  protected posProcessar(dados: string[]): string[] {
    return dados.map(s => s.trim());
  }
}

const dados = ["a", "", "b", "c"];
new PipelineMaiusculo().executar(dados);
new PipelineFiltro().executar(dados);

Passo a passo

  1. Pipeline é uma classe abstrata com o método concreto executar, que define o esqueleto do algoritmo.
  2. executar chama preProcessar, depois processar e por fim posProcessar, armazenando o resultado em resultado.
  3. preProcessar e processar são abstratos, então cada subclasse deve implementá-los.
  4. posProcessar é um hook: tem implementação padrão (retorna os dados sem alteração) e pode ser sobrescrito.
  5. PipelineMaiusculo implementa preProcessar sem alterações e processar convertendo para maiúsculas.
  6. PipelineFiltro implementa preProcessar removendo strings vazias e processar sem alterações; além disso, sobrescreve posProcessar para aparar espaços.
  7. No final, executar formata o array como [ "A", "B", "C" ] usando map e join.

Por que funciona

O Template Method garante que a ordem das etapas seja sempre a mesma, enquanto as subclasses fornecem as implementações específicas. O hook posProcessar permite que subclasses adicionem comportamento opcional sem quebrar o algoritmo principal.

Erros comuns

  • Esquecer de chamar posProcessar no executar: o hook nunca seria executado.
  • Declarar posProcessar como abstrato: aí não seria um hook, e todas as subclasses seriam obrigadas a implementá-lo.
  • Modificar o array original dentro de preProcessar sem retornar: o encadeamento quebraria. Sempre retorne o novo array.
  • Usar private nos métodos abstratos: eles precisam ser acessíveis nas subclasses.

Outra forma de resolver

Poderia usar composição: uma classe Pipeline que recebe funções de pré-processamento e processamento no construtor. Isso é mais flexível, mas menos orientado a objetos e não aproveita o Template Method.

Saída esperada

[ "A", "B", "C" ]
[ "A", "B", "C" ]