# Form Template Method

> When subclass methods follow the same sequence of steps with different details, pull the sequence into the superclass and leave only the steps in subclasses.

- kind: technique
- category: dealing-with-generalization
- canonical: https://aipatterns.dev/refactoring/techniques/form-template-method
- inspired-by: https://refactoring.guru/form-template-method

## Problem

Two or more subclasses have methods that do the same steps in the same order, but some steps differ in detail. You cannot pull the method up as-is, so the overall algorithm is duplicated and can drift.

## Solution

**Split each method into steps with identical signatures, pull the step sequence up into a superclass method, and leave each subclass to override only the steps that differ.**

## Before

```ts
class CsvImporter extends Importer {
  async run(file: Buffer): Promise<ImportSummary> {
    const rows = parseCsv(file.toString("utf8"));
    const valid = rows.filter((r) => r.sku && Number(r.qty) >= 0);
    await db.inventory.upsertMany(valid.map((r) => ({ sku: r.sku, qty: Number(r.qty) })));
    return { imported: valid.length, rejected: rows.length - valid.length };
  }
}

class JsonImporter extends Importer {
  async run(file: Buffer): Promise<ImportSummary> {
    const rows: { sku: string; quantity: number }[] = JSON.parse(file.toString("utf8"));
    const valid = rows.filter((r) => r.sku && r.quantity >= 0);
    await db.inventory.upsertMany(valid.map((r) => ({ sku: r.sku, qty: r.quantity })));
    return { imported: valid.length, rejected: rows.length - valid.length };
  }
}
```

## After

```ts
type StockRow = { sku: string; qty: number };

abstract class Importer {
  async run(file: Buffer): Promise<ImportSummary> {
    const rows = this.parse(file.toString("utf8"));
    const valid = rows.filter((r) => r.sku && r.qty >= 0);
    await db.inventory.upsertMany(valid);
    return { imported: valid.length, rejected: rows.length - valid.length };
  }
  protected abstract parse(text: string): StockRow[];
}

class CsvImporter extends Importer {
  protected parse(text: string): StockRow[] {
    return parseCsv(text).map((r) => ({ sku: r.sku, qty: Number(r.qty) }));
  }
}

class JsonImporter extends Importer {
  protected parse(text: string): StockRow[] {
    const rows: { sku: string; quantity: number }[] = JSON.parse(text);
    return rows.map((r) => ({ sku: r.sku, qty: r.quantity }));
  }
}
```

## Why

- Validation, persistence and the summary exist once.
- A new format needs only a `parse` method.
- The algorithm's order is fixed in one place, so subclasses cannot skip validation.

## Steps

1. In each subclass, extract the differing parts into methods with [Extract Method](https://aipatterns.dev/refactoring/techniques/extract-method.md). Give matching steps the same name and signature in every subclass. Run the tests.
2. Normalize the data passed between steps so all subclasses use the same types (here, `StockRow`). Run the tests.
3. The main methods should now be identical. Pull one up with [Pull Up Method](https://aipatterns.dev/refactoring/techniques/pull-up-method.md). Run the tests.
4. Declare the varying steps `abstract` in the superclass. Delete the duplicate main methods from subclasses. Run the tests.

## Watch out

- Do not use inheritance when the steps could be passed in as functions or objects. A function `parse` parameter, or a [Strategy](https://aipatterns.dev/design-patterns/strategy.md), keeps the importer composable and avoids a hierarchy. Prefer that in languages with first-class functions.
- Do not form a template from two methods that only look similar today. The skeleton becomes a constraint that later variants fight with hooks and flags.
- Keep the number of overridable steps small. Many hooks make the flow hard to follow.
- The inverse is to inline the template back into subclasses. To move from inheritance to composition, use [Replace Inheritance with Delegation](https://aipatterns.dev/refactoring/techniques/replace-inheritance-with-delegation.md).

## Related

- Fixes [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md) in near-identical subclass methods.
- Produces the [Template Method](https://aipatterns.dev/design-patterns/template-method.md) pattern; [Strategy](https://aipatterns.dev/design-patterns/strategy.md) is the composition alternative.
- Built from [Extract Method](https://aipatterns.dev/refactoring/techniques/extract-method.md) and [Pull Up Method](https://aipatterns.dev/refactoring/techniques/pull-up-method.md).

## Agent checklist

- [ ] The subclass methods share the same step order, differing only in step details.
- [ ] Varying steps have identical names and signatures before the pull-up.
- [ ] Function or strategy parameters were considered as an alternative to inheritance.
- [ ] The superclass exposes only a few abstract steps.
