← Back to Home
Factory Method Pattern
Define an interface for creating an object, but let subclasses decide which class to instantiate.
What is it?
Factory Method provides **extensibility without modification** - you can add new types without changing existing creation code.
Demo: The Extensibility Problem
Imagine you built an app in **v1.0** with Circle and Square. Now in **v2.0**, you need Triangle and Star. Watch what happens to each approach:
Settings:
🏭 Factory Method (v2.0)
Supports all shapes gracefully
Result:
Click a button to create shapes...
Creation Log:
No activity yet...
🔧 Direct Instantiation (v1.0)
Old hardcoded logic - breaks with new shapes!
Result:
Click a button to create shapes...
Creation Log:
No activity yet...
💡 The Key Difference:
- • **Factory Method**: All 4 shapes work perfectly - handles extension gracefully
- • **Direct Instantiation**: Only 2 shapes work - breaks when new types are added
- • Try clicking Triangle and Star to see the dramatic difference!
Code Example: The Real Problem
// Factory Method - Change ONE place, works everywhere class ShapeFactory { static createShape(type: string): Shape { switch (type) { case 'circle': return new Circle(); case 'square': return new Square(); case 'triangle': return new Triangle(); // Add once here case 'star': return new Star(); // Add once here default: throw new Error(`Unknown: ${type}`); } } } // All these classes use the factory - NO changes needed: class DrawingCanvas { createShape(type: string) { return ShapeFactory.createShape(type); // Works automatically } } class GameEngine { spawnEnemy(type: string) { return ShapeFactory.createShape(type); // Works automatically } } class UIBuilder { buildIcon(type: string) { return ShapeFactory.createShape(type); // Works automatically } } // vs Direct Instantiation - Change EVERY place manually class DrawingCanvas { createShape(type: string): Shape { switch (type) { case 'circle': return new Circle(); case 'square': return new Square(); // Must add Triangle here manually! // Must add Star here manually! default: throw new Error('Forgot to update this one!'); } } } class GameEngine { spawnEnemy(type: string): Shape { switch (type) { case 'circle': return new Circle(); case 'square': return new Square(); // Must add Triangle here manually! // Must add Star here manually! default: throw new Error('Forgot to update this one!'); } } } class UIBuilder { buildIcon(type: string): Shape { switch (type) { case 'circle': return new Circle(); case 'square': return new Square(); // Must add Triangle here manually! // Must add Star here manually! default: throw new Error('Forgot to update this one!'); } } } // And 12 other classes with duplicated creation logic... // RESULT: // Factory Method: 1 change → works everywhere // Direct Instantiation: 15 changes → high chance of missing some
Common Uses
- Plugin systems where new types can be added without changing core code
- UI widget libraries that support extensible component types
- Database drivers that can be extended with new database types
- Export systems supporting multiple formats (PDF, Excel, CSV...)
When to Use
- When you expect to add new types in the future
- When you want to avoid modifying existing creation code
- When creation logic might become complex or vary by type
- When you want to centralize object creation logic
Caution
- Can add complexity for simple scenarios that rarely change
- Still requires modifying the factory when adding new types
- May be overkill if you only have 2-3 stable types