Factory Method and Abstract Factory
- Reading time
- 2 min read
- Word count
- 387 words
- Diagram count
- 0 diagrams
Source: Victor Bona's Obsidian Compendium snapshot, Knowledge base/Design Patterns/Factory Method and Abstract Factory.md.
Factory Method is a creational pattern that defines an interface for creating an object but lets subclasses decide which class to instantiate. Instead of calling a constructor directly, the client calls a factory method which returns a product of an appropriate subtype. This promotes loose coupling by abstracting the creation logic.
- Intent: “Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”
- Use Cases: When a class cannot anticipate the class of objects it needs to create, or to delegate the choice of concrete type to subclasses. For example, an
ImageReaderinterface could define a factory methodcreateDecoder()that subclasses (PNGImageReader,JPEGImageReader) implement to produce format-specific decoder objects.
// Product interface and concrete products
interface Button { render(): void; }
class WindowsButton implements Button {
render() { console.log("Rendering Windows-style button"); }
}
class MacOSButton implements Button {
render() { console.log("Rendering MacOS-style button"); }
}
// Creator base class with factory method
abstract class Dialog {
// Factory Method: to be implemented by subclasses
abstract createButton(): Button;
renderDialog(): void {
// use the factory method to get a Button, then use it
const okButton = this.createButton();
okButton.render();
}
}
// Concrete Creators override factory method
class WindowsDialog extends Dialog {
createButton(): Button { return new WindowsButton(); }
}
class MacOSDialog extends Dialog {
createButton(): Button { return new MacOSButton(); }
}
// Client usage:
const dialog: Dialog = (platform === "Windows") ? new WindowsDialog() : new MacOSDialog();
dialog.renderDialog(); // creates and uses an appropriate Button via factory
In this example, Dialog is an abstract creator with a factory method createButton(). Subclasses decide which Button (product) to create. The client code works with the abstract Dialog but actually gets Windows or MacOS buttons based on the concrete subclass instantiated. This decouples the button creation from the dialog logic.
Abstract Factory is a related pattern that provides an interface to create families of related objects without specifying their concrete classes. An abstract factory groups individual factory methods for a suite of products. For example, an abstract GUI factory may create multiple UI components (buttons, checkboxes, menus) in a consistent style (all Windows, or all MacOS).
- Intent: Encapsulate a group of factories with a common theme to produce families of related products without exposing concrete classes.
- Use Case: Ensures that a set of products (e.g., UI widget kit) are created in matching variants. It’s useful when products must be used together and you want to swap families easily (e.g., switching from one database provider to another by swapping factories).
Example: Abstract factory for GUI toolkit:
// Abstract product interfaces:
interface Button { draw(): void; }
interface Checkbox { draw(): void; }
// Concrete product implementations for Windows:
class WinButton implements Button { draw() { /* ... */ } }
class WinCheckbox implements Checkbox { draw() { /* ... */ } }
// Concrete product implementations for Mac:
class MacButton implements Button { draw() { /* ... */ } }
class MacCheckbox implements Checkbox { draw() { /* ... */ } }
// Abstract Factory interface:
interface GUIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
// Concrete factories:
class WinFactory implements GUIFactory {
createButton(): Button { return new WinButton(); }
createCheckbox(): Checkbox { return new WinCheckbox(); }
}
class MacFactory implements GUIFactory {
createButton(): Button { return new MacButton(); }
createCheckbox(): Checkbox { return new MacCheckbox(); }
}
// Client code:
function buildUI(factory: GUIFactory) {
const btn = factory.createButton();
const chk = factory.createCheckbox();
btn.draw();
chk.draw();
}
// If we want a Windows UI:
buildUI(new WinFactory());
// For Mac UI:
buildUI(new MacFactory());
Here, the GUIFactory abstract factory defines methods to create each product type. WinFactory and MacFactory produce Windows and Mac variants of the products. The client (buildUI) is unaware of concrete classes; it just uses whichever factory is passed in. This makes it easy to switch entire families of products by choosing a different factory at runtime.
Summary: Factory patterns promote the Open/Closed Principle by encapsulating object creation. They decouple code from specific concrete classes, making it easier to extend or change the creation of objects without modifying existing code.