Introduce Polymorphic Creation With Factory Method

Destination
Compare
Symptom
Human

A method conditionally constructs a collaborator based on a type code carried by the caller. The conditional grows wider with every new variant; each variant's construction details leak into the shared method body; the method changes for two unrelated reasons (algorithm and variant-set).

Agent

A conditional construction block the agent must scan to know which collaborator gets built for a given type code. Adding a variant requires the agent to enumerate every such construction site in scope and add a branch — Shotgun Surgery in static reasoning.

Goal
Human

The base class declares an abstract Factory Method and uses the result polymorphically. Each subclass owns the creation knowledge for its variant; the base class's algorithm is conditional-free and stable.

Agent

Each subclass's Factory Method is locally verifiable; the base algorithm has no construction-conditional for the agent to load. The agent's per-edit budget on a variant addition is one new subclass, not N edits to construction sites.

Before the refactoring

// Branching on a type code to decide which collaborator to construct.
class DocumentReader {
constructor(format) {
this.format = format;
}
read(text) {
let parser;
if (this.format === 'xml') {
parser = new XmlParser();
} else if (this.format === 'json') {
parser = new JsonParser();
} else if (this.format === 'csv') {
parser = new CsvParser();
} else {
throw new Error(`unsupported format ${this.format}`);
}
return parser.parse(text);
}
}

After the refactoring

// Each subclass owns its createParser primitive; the base class is conditional-free.
class DocumentReader {
read(text) {
return this.createParser().parse(text);
}
createParser() {
throw new Error('abstract: subclasses must implement createParser');
}
}
class XmlReader extends DocumentReader {
createParser() {
return new XmlParser();
}
}
class JsonReader extends DocumentReader {
createParser() {
return new JsonParser();
}
}
class CsvReader extends DocumentReader {
createParser() {
return new CsvParser();
}
}
Example source: Illustrative example written for this site, adapted from Kerievsky's pattern description in Refactoring to Patterns (Addison-Wesley, 2004), chapter 6. The book frames this as the polymorphic alternative to type-code-driven construction; this JavaScript version uses document readers and parsers, same Factory Method shape.
Pressure
Human

Type-code-driven construction is Shotgun Surgery waiting to happen — a new variant requires editing every conditional that branches on the type. The variant-set becomes a cross-cutting concern duplicated everywhere a related decision is made.

Agent

Construction conditionals couple the base class to the full variant taxonomy; the agent must hold every variant in working memory to verify any edit to the base. Cross-variant invariants (e.g., that every parser implements `.parse`) are not statically enforceable when construction goes through type-code strings.

Tradeoff
Human

Factory Method requires a class hierarchy; what was one class becomes a base plus subclasses. For variant sets that are stable and small (two or three formats that have never changed), a conditional may be cheaper than the inheritance ceremony.

Agent

Factory Method spreads creation across the inheritance hierarchy; the agent must traverse subclasses to know which concrete type a base-class call produces. Static call-graph analysis loses precision on the return type of `createParser()`.

Relief
Human

The base class's algorithm is finalized; new variants land as new subclasses with no edits to the base. Per-variant creation is locally readable and locally testable; the algorithm's correctness is verified once.

Agent

Adding a new variant is one new subclass that overrides the factory hook; the base algorithm reads one virtual call instead of branching on a type code, and the algorithm's body stays constant in size as variants are added.

Trap
Human

A Factory Method whose only job is `return new X()` per subclass is constructor syntax with extra steps. The pattern pays when creation involves real choices — caching, parameter shaping, conditional sub-collaborators — not when it is a one-line `new`.

Agent

A hierarchy with one trivial Factory Method per subclass forces the agent to load the inheritance chain to know what a single base-class call returns. The pattern's context-cost gain materializes only when each Factory Method does non-trivial work — otherwise the indirection adds cost without proportional clarity.