Question 7 · Section 2

Difference Between Factory Method and Abstract Factory?

Both patterns help create objects, but at different levels:

Language versions: English Russian Ukrainian

Junior Level

Both patterns help create objects, but at different levels:

Factory Method — creates one type of object:

// Factory method creates a single product
interface Button { void render(); }
class WinButton implements Button { ... }
class MacButton implements Button { ... }

abstract class Dialog {
    // Factory Method — subclass decides which button to create
    abstract Button createButton();
}

Abstract Factory — creates a family of related objects:

// Abstract factory creates a whole family of products
// Related = they must be from the same "family" (all Windows components or all Mac components),
// otherwise the UI will look inconsistent.
interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
    Menu createMenu();
}

class WinFactory implements GUIFactory {
    Button createButton() { return new WinButton(); }
    Checkbox createCheckbox() { return new WinCheckbox(); }
    Menu createMenu() { return new WinMenu(); }
}

Key difference:

  • Factory Method = one product (button)
  • Abstract Factory = family of products (button + checkbox + menu)

Middle Level

OCP (Open/Closed Principle) — open for extension, closed for modification.

Factory Method

Mechanism: Inheritance (subclasses decide)

// Abstract class with Factory Method
public abstract class PaymentService {
    // Template method
    public void processPayment() {
        PaymentGateway gateway = createGateway();  // Factory Method
        gateway.authorize();
        gateway.capture();
    }

    // Subclass implements creation
    abstract PaymentGateway createGateway();
}

// Concrete implementations
public class CreditCardService extends PaymentService {
    @Override
    PaymentGateway createGateway() {
        return new StripeGateway();  // Subclass decides
    }
}

public class PayPalService extends PaymentService {
    @Override
    PaymentGateway createGateway() {
        return new PayPalGateway();
    }
}

When to use:

  • Need to extend the system with new types
  • Class doesn’t know which objects to create
  • Creation is delegated to subclasses

Abstract Factory

Mechanism: Composition (objects are passed in)

// Factory interface
public interface DatabaseFactory {
    Connection createConnection();
    Statement createStatement();
    ResultSet createResultSet();
}

// Concrete factory for MySQL
public class MySQLFactory implements DatabaseFactory {
    public Connection createConnection() { return new MySQLConnection(); }
    public Statement createStatement() { return new MySQLStatement(); }
    public ResultSet createResultSet() { return new MySQLResultSet(); }
}

// Concrete factory for PostgreSQL
public class PostgresFactory implements DatabaseFactory {
    public Connection createConnection() { return new PostgresConnection(); }
    public Statement createStatement() { return new PostgresStatement(); }
    public ResultSet createResultSet() { return new PostgresResultSet(); }
}

// Client works with any factory
public class DatabaseService {
    private final DatabaseFactory factory;

    public DatabaseService(DatabaseFactory factory) {
        this.factory = factory;  // Pass the needed factory
    }

    public void query() {
        Connection conn = factory.createConnection();
        Statement stmt = factory.createStatement();
        // All objects are compatible!
    }
}

When to use:

  • Need to create a family of related objects
  • Important that objects are from the same family
  • Client should not know about concrete classes

Comparison

Criterion Factory Method Abstract Factory
Creates One object Family of objects
Mechanism Inheritance Composition
Extension New subclass New factory
Complexity Low High
OCP Complies Violated when adding a product

Typical Mistakes

  1. Abstract Factory for a single product
    // Overengineering
    interface ButtonFactory { Button create(); }
    
    // Factory Method is sufficient
    abstract class Dialog { abstract Button createButton(); }
    
  2. OCP violation in Abstract Factory
    // Added a new product -> break ALL factories
    interface GUIFactory {
        Button createButton();
        Checkbox createCheckbox();
        Tooltip createTooltip();  // <- New!
    }
    // All implementations must be changed!
    

Senior Level

Architectural Scope

Factory Method = extension point in a hierarchy:

Dialog (abstraction)
  |-- createButton() <- extension point
  |     |-- WinButton
  |     +-- MacButton
  +-- Template method uses createButton()

Abstract Factory = compatibility boundary:

GUIFactory (family boundary)
  |-- WinFactory -> WinButton + WinCheckbox + WinMenu
  +-- MacFactory -> MacButton + MacCheckbox + MacMenu

Guarantee: products from different families do NOT mix!

Open/Closed Principle Trade-offs

Factory Method complies with OCP:

// Add a new type -> new subclass (don't change existing code)
class CryptoService extends PaymentService {
    @Override
    PaymentGateway createGateway() {
        return new CoinbaseGateway();
    }
}
// Existing code unchanged

Abstract Factory violates OCP on extension:

// Add a new product -> change interface -> break ALL implementations
interface GUIFactory {
    Button createButton();
    Slider createSlider();  // <- New product
}

// All factories must be updated:
class WinFactory implements GUIFactory {
    Slider createSlider() { return new WinSlider(); }  // <- Must add!
}
class MacFactory implements GUIFactory {
    Slider createSlider() { return new MacSlider(); }  // <- Must add!
}
// Existing code changed

System Evolution

Simple Factory (static method)
  -> (need extensibility)
Factory Method (inheritance)
  -> (need multiple products)
Abstract Factory (families)
  -> (need flexibility)
DI Container (Spring)

Simple Factory vs Factory Method

Simple Factory = static method. Cannot be extended via inheritance. Factory Method = virtual method in subclass. Can be extended.

Modern Java Implementations

Factory Method -> Supplier:

// Instead of inheritance
public class Dialog {
    private final Supplier<Button> buttonFactory;

    public Dialog(Supplier<Button> buttonFactory) {
        this.buttonFactory = buttonFactory;
    }

    public void render() {
        Button button = buttonFactory.get();
        button.render();
    }
}

// Usage
Dialog dialog = new Dialog(WinButton::new);

Abstract Factory -> Records:

// Record for storing a family
public record GUIFactory(
    Supplier<Button> button,
    Supplier<Checkbox> checkbox,
    Supplier<Menu> menu
) {}

// Predefined factories
public static final GUIFactory WINDOWS = new GUIFactory(
    WinButton::new, WinCheckbox::new, WinMenu::new
);

public static final GUIFactory MAC = new GUIFactory(
    MacButton::new, MacCheckbox::new, MacMenu::new
);

Spring Context as Meta-Factory

// Spring itself decides which beans to create
@Configuration
public class DatabaseConfig {

    @Bean
    @Profile("mysql")
    public DatabaseFactory mysqlFactory() {
        return new MySQLFactory();
    }

    @Bean
    @Profile("postgres")
    public DatabaseFactory postgresFactory() {
        return new PostgresFactory();
    }
}

// Spring = Abstract Factory on steroids
// + Profile = family selection at runtime

Performance Implications

Factory Method:

Monomorphic call (1 subclass) -> inlining
Bimorphic call (2 subclasses) -> still fast
Megamorphic call (>2 subclasses) -> indirect call

Abstract Factory:

Additional level of indirection:
  client -> factory interface -> concrete factory -> product

Overhead: 2 virtual calls instead of 1

Production Experience

Real scenario: Abstract Factory prevented mixing

  • UI framework: Windows and Mac themes
  • Problem: developers were mixing components
  • Solution: Abstract Factory guarantees compatibility
  • Result: impossible to create MacButton in Windows theme

Best Practices

  1. Factory Method for a single extensible product
  2. Abstract Factory for a family of related products
  3. **Supplier** instead of Factory Method in Modern Java

**Supplier** — Java 8+ functional interface, returns an object with no parameters.

  1. Spring Profiles instead of manual Abstract Factory
  2. Avoid Abstract Factory if there’s only one family
  3. Remember OCP violation when extending Abstract Factory
  4. Record for storing factory families

Records — Java 16+ compact immutable classes.

Senior Summary

  • Factory Method = inheritance, one product, OCP-friendly
  • Abstract Factory = composition, family of products, OCP violation
  • Evolution: Simple -> Factory Method -> Abstract Factory -> DI
  • Modern Java: Supplier replaces Factory Method
  • Spring = Meta-Factory with Profiles
  • OCP Trade-off: Adding a new product to Abstract Factory requires changing ALL factory implementations. If there are 10 factories — 10 files to edit.
  • Guarantee: Abstract Factory prevents mixing products

Interview Cheat Sheet

Must know:

  • Factory Method creates one product via inheritance, Abstract Factory — a family of products via composition
  • Factory Method complies with OCP (new subclass doesn’t change code), Abstract Factory violates OCP when adding a new product
  • Evolution: Simple Factory -> Factory Method -> Abstract Factory -> DI Container (Spring)
  • In Modern Java: Factory Method is replaced by Supplier, Abstract Factory — by Records or Map<Enum, Supplier>
  • Spring Context = Meta-Factory with @Profiles for family selection at runtime
  • Megamorphic calls: >2 subclasses in Factory Method -> JIT cannot inline
  • Abstract Factory guarantees: products from different families do NOT mix

Common follow-up questions:

  • When to choose Factory Method vs Abstract Factory? — One product = Factory Method, family of related = Abstract Factory
  • Why does Abstract Factory violate OCP? — Adding a new product requires changing the interface -> ALL implementations must be updated
  • How does Simple Factory differ from Factory Method? — Simple Factory = static method (cannot extend), Factory Method = virtual method in subclass
  • How does Spring replace Abstract Factory? — @Configuration + @Profile = bean family selection at runtime

Red flags (DO NOT say):

  • “I use Abstract Factory for a single product” — overengineering, Factory Method is enough
  • “Factory Method and Abstract Factory are the same” — fundamentally different mechanisms and goals
  • “Abstract Factory complies with OCP” — it violates OCP when adding a new product
  • “Spring doesn’t need Factory patterns” — Spring itself uses Abstract Factory under the hood

Related topics:

  • [[08. When to use Builder]] — creational pattern for complex objects
  • [[09. What is Prototype pattern]] — creational copying pattern
  • [[01. What are design patterns]] — general introduction
  • [[02. What pattern categories exist]] — Creational patterns
  • [[10. When to use Strategy]] — behavioral pattern, composition-based alternative