Factory Method Pattern
The Factory Method Pattern is a creational design pattern that provides an interface for creating objects but allows subclasses to alter the type of objects that will be created. It addresses the problem of creating product objects without specifying their concrete classes.
Problem
Imagine you're building a logistics management application. In the beginning, it only handled truck transportation, but now you need to add ship transportation. This would require changes to your existing code, which might introduce bugs.
Or consider a cross-platform UI application that needs to create different UI elements depending on the operating system. You'd want a way to create appropriate UI components without hardcoding the specific classes throughout your application.
Components
- Creator: An abstract class or interface that declares the factory method, which returns an object of type Product.
- Concrete Creator: Subclasses that override the factory method to return an instance of a Concrete Product.
- Product: An interface or abstract class for the objects the factory method creates.
- Concrete Product: Specific products created by corresponding Concrete Creators.
Implementation Example
Here's a basic implementation of the Factory Method pattern in JavaScript:
// Product Interface
class Button {
render() {
throw new Error("Button.render() must be implemented");
}
onClick() {
throw new Error("Button.onClick() must be implemented");
}
}
// Concrete Products
class WindowsButton extends Button {
render() {
return `<button class="windows-button">Windows Button</button>`;
}
onClick() {
return "Windows button clicked";
}
}
class MacButton extends Button {
render() {
return `<button class="mac-button">Mac Button</button>`;
}
onClick() {
return "Mac button clicked";
}
}
// Creator Abstract Class
class Dialog {
render() {
// Call the factory method to create a button
const button = this.createButton();
return `
<div class="dialog">
<div class="dialog-content">
${button.render()}
</div>
</div>
`;
}
// Factory Method
createButton() {
throw new Error("Dialog.createButton() must be implemented");
}
}
// Concrete Creators
class WindowsDialog extends Dialog {
createButton() {
return new WindowsButton();
}
}
class MacDialog extends Dialog {
createButton() {
return new MacButton();
}
}
// Client code
function clientCode(dialog) {
// Client uses the creator without knowing which concrete creator it's working with
return dialog.render();
}
// Example usage
function getDialog(os) {
if (os === "Windows") {
return new WindowsDialog();
} else if (os === "Mac") {
return new MacDialog();
} else {
throw new Error("Unknown operating system");
}
}
// Application
const currentOS = "Windows"; // Could be determined at runtime
const dialog = getDialog(currentOS);
const renderedHTML = clientCode(dialog);
Interactive Demo: UI Components Factory
This demo demonstrates how the Factory Method pattern allows you to create platform-specific UI components. Select a platform to see how different UI components are created by different factories.
When to Use
- When you don't know ahead of time what types of objects your code will need to create
- When you want subclasses to specify the types of objects they create
- When you want to localize the knowledge of which concrete class gets created
- When you need to create families of related objects but want to avoid rigid dependencies between client code and concrete classes
Benefits
- Avoids tight coupling between the creator and concrete products
- Follows the Single Responsibility Principle by moving product creation code to one place
- Follows the Open/Closed Principle by allowing introduction of new types of products without breaking existing client code
- Creates objects via inheritance rather than direct instantiation
- Connects parallel class hierarchies
Real-World Uses
- UI Frameworks creating platform-specific components
- Software with database abstraction layers, where different factories create connections for different database systems
- Document processors where factory methods create different types of documents
- Logistics applications for creating different types of transportation models (trucks, ships, planes)
- Game development for spawning different types of enemies or obstacles