Difference between Factory Method and Abstract Factory
Both Factory Method and Abstract Factory belong to the Creational design pattern family and serve to decouple client code from concrete object instantiation. The fundamental dif...
🟢 Junior Level
Both Factory Method and Abstract Factory belong to the Creational design pattern family and serve to decouple client code from concrete object instantiation. The fundamental difference lies in their scope and the underlying mechanism used:
- Factory Method: Creates one standalone product relying on inheritance. A base class defines an abstract creation method, delegating the instantiation decision to subclasses.
- Abstract Factory: Creates a whole family of related or dependent products relying on composition. An interface declares multiple creation methods, ensuring that created products are mutually compatible.
Factory Method Abstract Factory
┌────────────────────┐ ┌───────────────────────────┐
│ Dialog Class │ │ GUIFactory Interface │
├────────────────────┤ ├───────────────────────────┤
│ + createButton() │ │ + createButton(): Button │
└─────────┬──────────┘ │ + createCheckbox(): Check │
│ (Inheritance) └─────────────┬─────────────┘
▼ │ (Composition)
┌────────────────────┐ ┌─────────────┴─────────────┐
│ WindowsDialog │ ▼ ▼
│ (Returns 1 Button) │ ┌───────────────┐ ┌───────────────┐
└────────────────────┘ │ WindowsFactory│ │ MacFactory │
└───────────────┘ └───────────────┘
(Produces WinBtn (Produces MacBtn
+ WinCheckbox) + MacCheckbox)
Quick Analogy
- Factory Method: Ordering coffee from a barista — you ask for a coffee, and the subclass decides whether to brew a Latte, Espresso, or Cappuccino.
- Abstract Factory: Ordering a themed modular furniture set — you choose between Victorian or Modern style. The factory guarantees you receive a Victorian Chair with a Victorian Sofa; you never end up with an incompatible mismatch.
1. Factory Method Code Example
// Product interface
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
public void send(String message) { System.out.println("Email: " + message); }
}
// Creator base class
public abstract class NotificationService {
// Template method executing business logic
public void notifyUser(String message) {
Notification notification = createNotification(); // Factory Method call
notification.send(message);
}
// Factory Method to be overridden by subclasses
protected abstract Notification createNotification();
}
public class EmailNotificationService extends NotificationService {
@Override
protected Notification createNotification() {
return new EmailNotification();
}
}
2. Abstract Factory Code Example
// Family of Products: Buttons and Checkboxes
public interface Button { void paint(); }
public interface Checkbox { void paint(); }
public class WindowsButton implements Button { public void paint() { System.out.println("Win Button"); } }
public class WindowsCheckbox implements Checkbox { public void paint() { System.out.println("Win Checkbox"); } }
// Abstract Factory contract
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// Concrete Factory for Windows family
public class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}
🟡 Middle Level
Mechanism Comparison: Inheritance vs Composition
| Dimension | Factory Method | Abstract Factory |
|---|---|---|
| Primary Mechanism | Inheritance: Instantiation logic is implemented in a subclass overriding a template/abstract method. | Composition: A factory object is injected into client code as a dependency. |
| Instantiation Target | A single polymorphic product. | A suite of related, mutually compatible products. |
| Client Relationship | Client class is often the creator base class itself. | Client class is decoupled from the factory and interacts strictly through an interface. |
| Extension Model | Add a new subclass of the creator to produce a new product. | Add a new concrete factory class to introduce an entire new product family. |
Client Integration with Abstract Factory
The client interacts solely with the abstract interfaces, preventing incompatible cross-family product mixing:
public class Application {
private final Button button;
private final Checkbox checkbox;
// Factory injected via Dependency Injection (Composition)
public Application(GUIFactory factory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
public void render() {
button.paint();
checkbox.paint();
}
}
Taxonomy: Simple Factory vs Factory Method vs Abstract Factory
1. Simple Factory (Not a GoF Pattern):
A plain static helper class containing conditional logic (if / switch):
NotificationFactory.create("SMS") -> Instantiates SmsNotification.
❌ Violates Open/Closed Principle (requires modifying the method when adding types).
2. Factory Method (GoF):
Polymorphic virtual method in an abstract creator hierarchy:
Base class defines the creation contract; subclasses determine the concrete product.
3. Abstract Factory (GoF):
An interface declaring a group of Factory Methods:
Encapsulates the creation of complete product suites into a cohesive contract.
🔴 Senior Level
The Open/Closed Principle (OCP) Asymmetry in Abstract Factory
The Abstract Factory pattern displays an inherent architectural duality regarding the Open/Closed Principle:
- Adding a New Product Family (✅ Conforms to OCP):
Introducing a
LinuxFactorywithLinuxButtonandLinuxCheckboxrequires zero changes to existing application interfaces or client classes. - Adding a New Product Type (❌ Violates OCP):
If the GUI requires a new widget
createSlider():- The root interface
GUIFactorymust be modified. - All existing implementations (
WindowsFactory,MacFactory,LinuxFactory) break compilation until they implement the new method.
- The root interface
JVM JIT Optimization: Monomorphic vs Megamorphic Call Sites
At runtime, the JVM’s JIT compiler (C2) optimizes polymorphic factory call sites based on profiling data:
- Monomorphic Call Site (1 concrete factory at runtime):
The JIT compiler performs speculative inlining. Calls to
factory.createButton()are replaced directly with machine instructions allocating memory (new WindowsButton()), eliminating virtual call overhead (invokevirtual/invokeinterface). - Bimorphic Call Site (2 concrete factory types):
The compiler emits a branch table check (
if (class == WindowsFactory)), retaining high inline cache hit rates. - Megamorphic Call Site (3+ factory types at the same site): Speculative inlining fails. The JVM falls back to indirect method table lookup (vtable / itable dispatch), incurring cache-miss and call-indirection penalties in tight loops.
Modern Java: Functional Interfaces & Records
In modern Java (Java 8–21), class hierarchies for Factory Method can be replaced with java.util.function.Supplier<T> and constructor method references:
public class NotificationManager {
private final Supplier<Notification> notificationSupplier;
// Pass constructor reference instead of building subclass hierarchies
public NotificationManager(Supplier<Notification> notificationSupplier) {
this.notificationSupplier = notificationSupplier;
}
public void dispatch(String msg) {
Notification notification = notificationSupplier.get();
notification.send(msg);
}
}
// Client usage:
var emailManager = new NotificationManager(EmailNotification::new);
var smsManager = new NotificationManager(SmsNotification::new);
An Abstract Factory suite can be expressed concisely using Java Records:
public record GuiKit(
Supplier<Button> buttonSupplier,
Supplier<Checkbox> checkboxSupplier
) {}
// Family configurations:
public static final GuiKit WIN_KIT = new GuiKit(WindowsButton::new, WindowsCheckbox::new);
public static final GuiKit MAC_KIT = new GuiKit(MacButton::new, MacCheckbox::new);
Spring Framework as a Dynamic Meta-Factory
In enterprise Spring applications, manual Abstract Factory hierarchies are replaced by the Spring IoC Container (ApplicationContext):
@Configurationclasses declare@Beandefinitions.- Profiles (
@Profile("dev")vs@Profile("prod")) or conditions (@ConditionalOnProperty) switch entire families of infrastructure beans (e.g., S3 Bucket + RDS in production vs In-Memory Storage + H2 in test) declaratively.
@Configuration
public class StorageInfrastructureConfig {
@Bean
@Profile("cloud")
public StorageService cloudStorage() {
return new S3StorageService();
}
@Bean
@Profile("local")
public StorageService localStorage() {
return new LocalDiskStorageService();
}
}
4 Tricky Questions
1. Why does adding a new product type to an Abstract Factory violate the Open/Closed Principle (OCP)?
Answer: The Abstract Factory interface defines a fixed contract with explicit methods for each product type (createButton(), createCheckbox()). If a new product type (e.g., createSlider()) is required, the interface itself must be modified. This breaks source and binary compatibility across all existing concrete factory implementations, forcing developers to modify and recompile every concrete factory in the codebase.
2. How can you implement Factory Method in modern Java without creating a hierarchy of subclasses?
Answer: By utilizing functional interfaces—specifically java.util.function.Supplier<T>—and Java constructor method references (ConcreteProduct::new). Instead of defining an abstract creator class and multiple subclasses, the creator class takes a Supplier<Product> as a constructor argument. This achieves dynamic, polymorphic product creation without the class explosion of subclassing.
3. How do Factory Method and Abstract Factory relate to each other inside real-world frameworks?
Answer: An Abstract Factory is commonly implemented as an aggregate of Factory Methods. Each method declared in an Abstract Factory interface (such as createButton()) is conceptually a Factory Method that concrete factories implement to produce concrete product variants. While Factory Method focuses on a single operation via inheritance, Abstract Factory orchestrates a collection of related Factory Methods via composition.
4. How does JIT compilation optimize polymorphic factory calls, and what causes deoptimization?
Answer: The JVM JIT compiler observes call sites. If a single factory implementation is invoked at a call site (monomorphic), the JIT compiler speculatively devirtualizes the call and inlines the product constructor directly. If 2 implementations are observed (bimorphic), it generates conditional type-check branches. If 3 or more distinct concrete factory classes pass through the same call site (megamorphic), the JIT compiler abandons inlining and falls back to indirect itable (interface method table) lookups, causing higher CPU branch misprediction and latency overhead.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
“Factory Method creates a single product by relying on inheritance, where an abstract class delegates the instantiation to subclasses. Abstract Factory creates a family of related, compatible products relying on composition, where client code is injected with a factory interface containing multiple creation methods. While Abstract Factory adheres to the Open/Closed Principle when adding new families (e.g., Mac vs Windows), it violates OCP whenever a new product type is introduced to the interface. In modern Java, Factory Method is frequently replaced with
Supplier<T>and constructor references, while Spring’sApplicationContextacts as an enterprise-grade dynamic Abstract Factory.”
Key Takeaways
- Factory Method = 1 Product via Inheritance: Overridden method in a subclass.
- Abstract Factory = Family of Products via Composition: Injected interface guaranteeing product interoperability.
- Simple Factory: Static method with switch-case; non-GoF; violates OCP.
- Modern Alternative:
Supplier<T>and constructor method references (Product::new). - Spring IoC: Acts as a runtime meta-factory with
@Beanand@Profile.
Red Flags & Anti-Patterns
- 🚩 Using Abstract Factory for a single product: Unnecessary abstraction layer and overengineering.
- 🚩 Confusing Simple Factory with Factory Method: Failing to recognize that GoF Factory Method requires polymorphic inheritance, not a static switch statement.
- 🚩 Creating bloated factory hierarchies in Spring applications: Instead of leveraging
@Configurationand dependency injection. - 🚩 Ignoring OCP violation in Abstract Factory: Adding new product types frequently to an existing Abstract Factory interface.
Related Topics
- When to use Builder — Step-by-step assembly of complex objects
- What is Prototype pattern — Instantiation via cloning
- What are design patterns — Overview and foundations
- What pattern categories exist — Creational, structural, and behavioral classification
- When to use Strategy — Encapsulating interchangeable algorithms