El patrón Template Method fija el esqueleto de un proceso, delegando pasos concretos a subclases.
1. El Problema de Partida
Existen validadores casi idénticos para content/knowledge/ y content/blog/: ambos leen el archivo, parsean frontmatter con gray-matter, y reportan errores — pero cada colección exige campos distintos (knowledge necesita translation_id y category; blog necesita tags). Copiar el "leer + parsear + reportar" en cada validador duplica lógica.
2. La Solución Template Method
// src/lib/validation/ContentValidator.js
import matter from 'gray-matter';
import fs from 'fs';
export class ContentValidator {
// Método plantilla: orden fijo, no se sobrescribe
validate(filePath) {
const raw = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter } = matter(raw);
const errors = this.checkRequiredFields(frontmatter); // Paso variable
return errors.length ? this.report(filePath, errors) : null;
}
checkRequiredFields(frontmatter) {
throw new Error('checkRequiredFields debe implementarse en la subclase');
}
report(filePath, errors) {
return `❌ ${filePath}: faltan campos ${errors.join(', ')}`; // Hook con default
}
}
export class KnowledgeValidator extends ContentValidator {
checkRequiredFields(fm) {
return ['translation_id', 'category'].filter((f) => !fm[f]);
}
}
export class BlogValidator extends ContentValidator {
checkRequiredFields(fm) {
return ['tags'].filter((f) => !fm[f]);
}
}3. Uso
// scripts/validate-content.js
import { KnowledgeValidator, BlogValidator } from '@/lib/validation/ContentValidator';
const kv = new KnowledgeValidator();
console.log(kv.validate('content/knowledge/es/design-patterns/state.mdx'));
const bv = new BlogValidator();
console.log(bv.validate('content/blog/es/mi-post.mdx'));4. Ventajas
- Leer, parsear y reportar viven en un solo lugar (
ContentValidator), nunca duplicados. - Añadir una colección nueva (
projects) = una subclase con solocheckRequiredFields. - Consistencia garantizada: todos los validadores reportan errores con el mismo formato.