When object creation happens directly at every call site, the caller depends on a concrete class. Swapping the implementation requires touching every caller. Factory breaks that coupling — it concentrates creation responsibility in one place and lets callers depend only on the abstraction.

The name Factory points to three variants, not one pattern. Factory Method, Abstract Factory, and Static Factory Method. They get grouped together often, yet the intent and application differ. Three variants with different intent under a similar name.

The Shared Intent

What the three variants share is separating creation from use.

Code that creates objects directly means the caller knows a concrete class. A call like new MySQLConnection() carries the knowledge of MySQLConnection inside the caller, and replacing it with PostgreSQL forces every call site to change. Factory abstracts that call. The caller depends only on an interface like Connection, and what concrete implementation arrives is the Factory’s decision.

That is where the common ground ends. The variants part ways in how they split creation from use.

Factory Method

Factory Method delegates object creation to subclasses.

A parent class defines an abstract method createProduct(), and subclasses implement it to fix the concrete class. The parent’s other methods use the result of that abstract method. The full call flow lives in the parent, with one decision delegated downward. It reads as a form of Template Method.

The defining trait is that it runs on inheritance. A new concrete class means a new subclass. It fits when the domain is stable and the extension point is clearly identified. It strains in languages where multiple inheritance is awkward, or when the extension point shifts often.

The JDK’s Collection.iterator() is canonical. The Collection interface defines Iterator creation abstractly, and implementations like ArrayList and HashSet return iterators that fit their internal structure.

Abstract Factory

Abstract Factory addresses consistent creation of a family of related objects.

One interface groups creation methods for several products. A UIFactory defines createButton(), createWindow(), and createScrollbar() together; MacUIFactory returns Mac-style components and WindowsUIFactory returns Windows-style ones. The caller gets a guarantee that objects within a family belong together.

DB drivers follow the same pattern. DriverFactory groups createConnection(), createStatement(), and createResultSet(), and each DB-specific factory returns a consistent family.

It fits when products only make sense in pairs. Abstracting a single object’s creation through this pattern is overkill. And adding a new product to the family means changing every Factory implementation — a constraint that suits stable domains with well-defined product types.

Static Factory Method

Static Factory Method is a static method that compensates for the limits of constructors. The pattern Joshua Bloch organized in Effective Java, Item 1.

Constructors carry four limits. They have no names. The same signature cannot define multiple constructors. They must return a new instance on every call. The caller has to know the exact return type. Static Factory Method addresses all four.

  • Named creationBigInteger.probablePrime() tells the reader what is created. Constructor overloads cannot carry the same meaning.
  • CachingInteger.valueOf(int) caches frequently used values (-128 to 127) and returns the same instance. The base of the Flyweight pattern.
  • Varied return types — Declare an interface as the return type and return an implementation. The caller of Collections.unmodifiableList() does not know the concrete class. Changing the implementation does not affect callers.
  • The returned class need not exist at call time — JDBC’s DriverManager.getConnection() is the canonical case. Which Driver is loaded at call time decides what class of instance comes back.

Even the Java standard library shows the pattern repeatedly — Optional.of, List.of, Map.of, Stream.of, Files.newBufferedReader. It is the variant most often encountered in practice.

Limits exist. Static methods do not inherit cleanly — without protected, subclasses cannot override them. And without an obvious name, the method becomes harder to discover than a constructor. Conventions like of, from, valueOf, getInstance, and newInstance exist to soften that.

Choosing Among Them

Following the threads of the three variants, the application conditions line up like this.

  • Multiple concrete implementations behind the same signature — Factory Method (inheritance). When the domain is stable and the extension point is clear.
  • Related objects that need to fit together — Abstract Factory (composition). UI families, DB driver families — products that require consistency together.
  • Constructor limits get in the way (no names, no caching, concrete type exposure) — Static Factory Method (alternative). The most common choice in practice.

The three are not mutually exclusive. A library often shows all three working in different places.

Relation to DI Containers

A DI container reads as a generalization of Factory.

Containers handle both object creation and dependency injection. Configuration decides which implementation goes where, and the caller depends only on the abstraction (the interface). Where Abstract Factory takes care of family-consistent creation, the container takes the same role. Static Factory Method’s caching matches the effect of a container’s singleton scope.

Does that make explicit Factory disappear? For simple creation, the container suffices. But when the choice of concrete depends on domain logic at runtime, explicit Factory still reads more clearly. Picking a different PaymentProcessor per payment method, or a different DiscountPolicy per user tier — those are runtime decisions, not container configuration.

The DIP covered in the dependency-injection post is the abstraction that makes this separation possible, and Factory is one way to express that abstraction in code.

Conclusion

Three variants gather under the name Factory, but the deciding axis differs. Factory Method extends through inheritance, Abstract Factory holds object families consistent, and Static Factory Method compensates for constructor limits. The same intent — separating creation from use — gets expressed three different ways.

The variant most often met in practice is Static Factory Method. From the Java standard library to domain code, the same pattern repeats. Factory Method and Abstract Factory get picked when the situation fits specific conditions — inheritability available, object family present.

The names look similar enough to be confused for one pattern, but the application decision separates by intent.

References

  • Singleton — Where Static Factory Method’s getInstance meets single-instance guarantee
  • Dependency Injection — The Hierarchy of DIP, IoC, and DI — How a DI container generalizes Factory
  • Joshua Bloch — Effective Java (3rd ed.), Item 1: Consider static factory methods instead of constructors
  • GoF — Design Patterns: Elements of Reusable Object-Oriented Software (1994)