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
Pipelinedeve ser abstrata e terexecutarconcreto.preProcessareprocessardevem ser abstratos.posProcessardeve 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
Pipelineé uma classe abstrata com o método concretoexecutar, que define o esqueleto do algoritmo.executarchamapreProcessar, depoisprocessare por fimposProcessar, armazenando o resultado emresultado.preProcessareprocessarsão abstratos, então cada subclasse deve implementá-los.posProcessaré um hook: tem implementação padrão (retorna os dados sem alteração) e pode ser sobrescrito.PipelineMaiusculoimplementapreProcessarsem alterações eprocessarconvertendo para maiúsculas.PipelineFiltroimplementapreProcessarremovendo strings vazias eprocessarsem alterações; além disso, sobrescreveposProcessarpara aparar espaços.- No final,
executarformata o array como[ "A", "B", "C" ]usandomapejoin.
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
posProcessarnoexecutar: o hook nunca seria executado. - Declarar
posProcessarcomo abstrato: aí não seria um hook, e todas as subclasses seriam obrigadas a implementá-lo. - Modificar o array original dentro de
preProcessarsem retornar: o encadeamento quebraria. Sempre retorne o novo array. - Usar
privatenos 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" ]