Creational Patterns
Factory Method
Define an interface for creating an object, but let subclasses decide which class to instantiate
Factory Method Pattern
1. Learning Objectives
- Understand the core problem Factory Method solves and how it respects the Open/Closed Principle.
- Differentiate between Simple Factory, Factory Method, and Abstract Factory.
- Trace how dynamic method dispatch (vtables) handles polymorphic object instantiation at runtime.
- Analyze parallel class hierarchies and how they align Creators and Products.
- Implement type-safe, extensible hierarchies in Java, Python, and C++.
2. Problem & Naive Solution
Consider an application that processes alerts. Initially, it only needs to send Email Notifications. We implement an EmailNotification class.
Later, business requirements expand to support SMS Notifications and Push Notifications.
The Naive Solution
To handle multiple notification types, a developer might write a central controller containing a switch block checking enum values:
This introduces significant coupling:
- Violates Open/Closed Principle: Adding a new channel (e.g., WhatsApp) forces us to modify the core
dispatch()method, risking regressions. - Tight Coupling: The high-level
NotificationManagerdepends directly on every concrete notification class (EmailNotification,SMSNotification), preventing clean code division.
3. Issues
If we instantiate objects directly in the client, it is impossible to swap product variants without rewriting core business flows. When object instantiation code is scattered across different client modules, updating class constructors requires changing multiple source files, making maintenance difficult.
4. Pattern Introduction & UML
The Factory Method Pattern resolves this by defining an abstract method for creating objects. Instead of instantiating products directly, creators delegate this responsibility to specialized subclasses:
- Product Interface (
Notification): Defines the contract that all concrete products must implement. - Concrete Products (
EmailNotification,SMSNotification): Implement the product contract. - Creator Class (
NotificationFactory): Declares the abstract factory methodcreateNotification()and contains core business logic that operates on the product interface. - Concrete Creators (
EmailFactory,SMSFactory): Override the factory method to return a specific concrete product instance.
5. Participants
- Product (
Notification): The abstract base/interface for the objects the factory method creates. - Concrete Product (
EmailNotification): The concrete object type being instantiated. - Creator (
NotificationFactory): Declares the factory method returning aNotificationreference. - Concrete Creator (
EmailFactory): Overrides the factory method to instantiate and returnEmailNotification.
6. Theory (Comparison Matrix)
It is common to confuse different factory patterns. The matrix below clarifies their differences:
| Pattern | Structural Style | Implementation Strategy | Decoupling Target |
|---|---|---|---|
| Simple Factory | Concrete class with a static method | Uses an internal switch or if-else block to instantiate classes |
Isolates client code from the new operator |
| Factory Method | Polymorphic abstract inheritance | Subclasses override an abstract method to return a specific product | Decouples core business logic from concrete product types (OCP compliant) |
| Abstract Factory | Interface containing multiple factory methods | A factory object instantiates families of related products | Decouples client from product families (e.g., Windows UI vs. macOS UI components) |
7. Syntax Explanation
Implementing the pattern requires defining abstract creators and overriding the factory method:
- Java: Uses the
abstractkeyword for the factory method, forcing concrete subclass factories to implement it. - Python: Leverages the
abcmodule, marking the factory method with@abstractmethod. - C++: Declares a pure virtual function (e.g.
virtual std::unique_ptr<Notification> createNotification() = 0;). Using smart pointers prevents memory leaks and supports runtime polymorphism cleanly.
8. Step-by-Step Implementation
- Step 1: Create the abstract Product interface (e.g.,
Notification). - Step 2: Implement concrete products (e.g.,
EmailNotification,SMSNotification) implementing the product interface. - Step 3: Declare the abstract Creator class (e.g.,
NotificationFactory) containing the abstract factory method and any template methods that operate on the product. - Step 4: Implement concrete creator classes (e.g.,
EmailFactory,SMSFactory) that override the factory method to return instances of their respective products. - Step 5: Refactor client code to interact only with the abstract creator and product interfaces.
9. Complete Code (Mini Project)
10. Code Walkthrough
Let's review the key elements of the refactored code:
- The client code operates strictly on abstract interfaces:
NotificationFactoryandNotification. It contains no direct references to concrete subclasses likeEmailNotificationorEmailFactory. - The
deliver()method in the abstract creator encapsulates the business logic. It calls the abstract factory methodcreateNotification(), deferring the instantiation decision to subclasses. - To support a new type of notification, we only need to implement a new product class (e.g.
PushNotification) and a corresponding factory class (PushFactory). The existing creator and client code remains unmodified.
11. Execution Flow
- Creator Instantiation: The client instantiates a concrete creator class (e.g.
EmailFactory) on the heap. - Method Trigger: The client invokes the template method
deliver("message")on the creator. - Deferred Instantiation: The template method calls the factory method
createNotification(). - Polymorphic Creation: The runtime uses dynamic dispatch to execute the overridden method in
EmailFactory, instantiating the concrete productEmailNotificationon the heap. - Execution: The template method executes the business logic on the newly created product instance and returns.
12. Internal Working (JVM & Vtables)
At the virtual machine and compiler level, dynamic dispatch resolves the correct factory method using virtual tables:
- Vtable Setup: During compilation, the compiler generates a virtual table (vtable) for the abstract creator class
NotificationFactory. Subclasses override the vtable pointer offsets. - Method Resolution: When the template method calls
createNotification(), the runtime checks the object header pointer to determine the concrete class type. It then performs a lookup in the subclass vtable to invoke the correct factory method. - Polymorphic Instantiation: The subclass factory method executes a standard memory allocation instruction (e.g.
newin Java/C++), allocating memory on the heap and returning the pointer reference.
13. Complexity Analysis
- Time Complexity: $O(1)$ constant time for instantiation and polymorphic delegation.
- Space Complexity: $O(N + M)$ memory footprint, where $N$ is the number of concrete product classes and $M$ is the number of concrete creator classes. Splitting instantiations across separate classes increases compile-time metadata, but does not impact runtime execution speed.
14. Best Practices
- Decouple Creator and Product Hierarchies: Maintain parallel class hierarchies. Avoid combining the creator and product logic into a single class hierarchy.
- Enforce Constructor Encapsulation: Make concrete product constructors package-private or protected to prevent clients from bypassing the factories.
- Use Simple Factory for simpler scenarios: If you do not need subclass flexibility or parallel class hierarchies, a Simple Factory (a single class with a static creation method) is cleaner and avoids subclass bloat.
15. Common Mistakes
- Subclass Explosion: Creating a new creator subclass for every simple product variation. If product variants only differ in attributes rather than behavior, pass parameter arguments to a single factory instead.
- Confusing Factory Method with Simple Factory: Calling a static helper method a "Factory Method." Factory Method relies on class inheritance and virtual method overriding.
- Adding business logic to factory methods: Bloating the factory method with business validation or state tracking. Keep the factory method focused strictly on object instantiation.
16. Framework Usage
- Java Calendar: The
Calendar.getInstance()method behaves like a factory, returning a specific calendar subclass (e.g.GregorianCalendar) based on the system locale. - Java Collections Iterator: The
iterator()method in Java collections (e.g.ArrayList.iterator(),HashSet.iterator()) is a classic Factory Method. The abstract collection interface defines the method, and each collection subclass overrides it to return a specialized iterator. - Spring FactoryBean: Spring's
FactoryBeaninterface allows developers to customize bean instantiation by implementing agetObject()factory method.
17. Interview Discussion
Answer: By abstracting object creation. When adding a new product, we create a new product subclass and a new creator subclass. The existing creator interfaces, template methods, and client code remain unchanged.
Answer: Subclass explosion. For every new product class, the developer must also implement a corresponding creator subclass. This can quickly double the number of classes in the codebase.
Answer: By using parameterized factory methods (passing an identifier to a single factory method that handles instantiation) or by using generic classes (e.g. template-based creators in C++).
18. Practice Exercises
- Easy: Implement a simple
DocumentFactoryin Python that returnsPDFDocumentorWordDocumentinstances. - Medium: Design a C++
DatabaseConnectorFactorycontaining a template methodconnect(). Concrete factories should return connection handlers for MySQL and PostgreSQL. - Hard: Design a parameterized Factory Method in Java that uses reflection to dynamically load and register new product classes at runtime from a configuration file, avoiding subclass explosion.
19. Challenge Problem
Design a cross-platform GUI Widget Engine using the Factory Method pattern. The engine constructs buttons, checkboxes, and text inputs for Windows, macOS, and Linux. The central rendering loop operates on abstract widget contracts, completely isolated from operating system details. Write the implementation in Java, Python, or C++ and show how adding support for a new operating system requires zero changes to the core rendering loop.
20. Summary & Cheat Sheet
- The Factory Method pattern defines an interface for creating objects, delegating instantiation to subclasses.
- It promotes loose coupling by decoupling client code from concrete product classes.
- It implements parallel class hierarchies matching creators to products.
- The pattern supports the Open/Closed Principle by allowing new products to be added without modifying existing code.
21. Quiz
1. Which problem is solved by the Factory Method pattern?
A) Global instance synchronization race conditions
B) Hardcoded, tightly coupled object instantiations in client code (Correct)
C) Subclasses throwing exceptions for unimplemented parent methods
2. How does the Factory Method pattern delegate instantiation responsibility?
A) By using static database connection utilities
B) Through class inheritance, allowing subclasses to override an abstract factory method (Correct)
C) Using reflection to access private constructors directly
3. What is a key disadvantage of the Factory Method pattern?
A) It requires making all classes public
B) It prevents dynamic casting
C) Subclass explosion, requiring a new creator subclass for every product class (Correct)
4. How does Factory Method differ from Abstract Factory?
A) Factory Method creates a single product via inheritance; Abstract Factory creates families of related products via composition (Correct)
B) Factory Method uses static methods; Abstract Factory uses public fields
C) There is no difference between the two patterns
5. In Java collections, which method is a classic example of the Factory Method pattern?
A) add()
B) size()
C) iterator() (Correct)
6. What is a template method in the context of the Factory Method pattern?
A) A method in the creator class containing core business logic that operates on the product returned by the factory method (Correct)
B) A method that compiles C++ generic templates
C) A static helper method
7. Why are virtual destructors mandatory in C++ abstract product classes?
A) To speed up virtual lookup checks
B) To ensure concrete subclass resources are cleaned up correctly when deleted via base pointer references (Correct)
C) C++ virtual classes do not require destructors
8. Which SOLID principle is directly supported by the Factory Method pattern?
A) Single Responsibility Principle
B) Open/Closed Principle (Correct)
C) Liskov Substitution Principle
9. What is a parallel class hierarchy?
A) Two unrelated class hierarchies that execute in parallel threads
B) Mirroring creator and product hierarchies, linking each concrete creator subclass to a concrete product class (Correct)
C) Storing classes in parallel packages
10. When should a developer prefer Simple Factory over Factory Method?
A) When they want to enforce absolute immutability
B) When the object instantiation logic is simple and does not require subclassing flexibility (Correct)
C) When calling database connection APIs
22. Next Lesson Preview
In the next lesson, we will cover the Abstract Factory Pattern. We will learn how to create families of related or dependent objects without specifying their concrete classes!
Related Topics
- SingletonEnsure a class has only one instance and provide a global point of access to it
- BuilderConstruct complex objects step-by-step using a fluent interface with method chaining
- Abstract FactoryProvide an interface for creating families of related or dependent objects without specifying their concrete classes