Abstract Factory Pattern: Building Whole Product Families, Not Just Single Products
The article explains the Abstract Factory pattern by contrasting product families with product hierarchies, showing why the Factory Method fails for bundled products, presenting the pattern's structure and code, and detailing its open‑closed principle tilt and appropriate usage scenarios.
1. Product Family vs. Product Hierarchy
Before understanding Abstract Factory, the author introduces a two‑dimensional view. The previous Factory Method deals with a single dimension – many implementations of the same product (e.g., Payment). Abstract Factory handles two intertwined dimensions: a set of product types (Order, Invoice, Shipment) and multiple channels (Online, Store). The table of channels × documents clarifies the distinction.
Product Hierarchy (column): Different implementations of the same product, such as ElectronicInvoice and PaperInvoice – the focus of Factory Method.
Product Family (row): A whole set of products that belong together, e.g., the three online documents OnlineOrder, ElectronicInvoice, VirtualShipment. All items in a row must be used together.
One sentence to remember: a product hierarchy is "different brands of the same thing", while a product family is "a complete set of different things under one brand".
2. Why the Factory Method Breaks for Product Families
If we try to solve the family problem with separate factories ( OrderFactory, InvoiceFactory, ShipmentFactory), the client code must manually create a matching trio:
Order order = new OnlineOrderFactory().create();
Invoice invoice = new ElectronicInvoiceFactory().create(); // remember to pick "electronic"
Shipment shipment = new VirtualShipmentFactory().create(); // remember to pick "virtual"The constraint "must match" now relies on the programmer’s discipline. A slip—using PaperInvoiceFactory for an online order—compiles and runs, but produces a wrong physical invoice for an online customer. Adding a new channel also requires editing three lines, increasing the risk of mixed‑match bugs.
3. Abstract Factory Introduced: One Factory for a Whole Family
Abstract Factory eliminates the above problem by providing a single factory per product family. First, define abstract product interfaces (unchanged from the previous article):
public interface Order { void submit(); }
public interface Invoice { void issue(); }
public interface Shipment { void ship(); }The abstract factory declares a creation method for each product:
public interface OrderChannelFactory {
Order createOrder();
Invoice createInvoice();
Shipment createShipment();
}Concrete factories implement this interface for a specific channel:
// Online channel – produces the whole online family
public class OnlineChannelFactory implements OrderChannelFactory {
public Order createOrder() { return new OnlineOrder(); }
public Invoice createInvoice() { return new ElectronicInvoice(); }
public Shipment createShipment() { return new VirtualShipment(); }
}
// Store channel – produces the whole store family
public class StoreChannelFactory implements OrderChannelFactory {
public Order createOrder() { return new StoreOrder(); }
public Invoice createInvoice() { return new PaperInvoice(); }
public Shipment createShipment() { return new PickupShipment(); }
}Client code now depends on a single factory:
public class OrderService {
private final OrderChannelFactory factory;
public OrderService(OrderChannelFactory factory) { this.factory = factory; }
public void placeOrder() {
Order order = factory.createOrder();
Invoice invoice = factory.createInvoice(); // no need to pick a brand
Shipment shipment = factory.createShipment(); // guaranteed to match
// ...
}
}
// Switching channels is a one‑liner
new OrderService(new OnlineChannelFactory()); // all online
new OrderService(new StoreChannelFactory()); // all storeCompared with the Factory Method, the mismatch risk disappears and channel switching requires only changing the outer factory.
4. Roles and Skeleton
Abstract Factory (OrderChannelFactory): declares methods to create a whole family.
Concrete Factory (OnlineChannelFactory, StoreChannelFactory): each produces a complete row of products.
Abstract Product (Order, Invoice, Shipment): the product interfaces.
Concrete Product (OnlineOrder, PaperInvoice, …): concrete implementations that occupy a cell in the grid.
The accompanying grid diagram (shown below) visualizes this two‑dimensional relationship.
5. The Open/Closed Principle Tilt
The pattern’s support for the Open/Closed Principle is asymmetric:
Adding a new product family (new row): extremely easy – create the three concrete products and a new concrete factory; no existing code changes.
Adding a new product type (new column): painful – the abstract factory interface must gain a new method, and every existing concrete factory must implement it, violating the principle.
Example: adding a "Live" channel only requires a new LiveChannelFactory and three concrete products. Adding a new "Coupon" product type forces every factory (Online, Store, Live) to add createCoupon() and implement it.
6. When to Use and When to Avoid
Use Abstract Factory if:
The system has genuine product families that must be used together (e.g., skin themes, cross‑database components, multi‑channel order documents).
Families are expected to grow, while the set of product types remains relatively stable.
You want a single switch to change the whole family and enforce consistency at compile time.
Avoid it when:
Products are independent and only need separate implementations – the Factory Method suffices.
The set of product types changes frequently – the “column” tilt makes maintenance costly.
There is only one family and no foreseeable need for switching.
7. Real‑World Examples and Comparison with Other Factory Patterns
Classic library examples that act as abstract factories: java.sql.Connection – creates statements, prepared statements, blobs that belong to the same database family. javax.xml.parsers.DocumentBuilderFactory and javax.xml.transform.TransformerFactory – produce matching parser/transformer components.
UI skin frameworks – a theme factory creates buttons, text fields, scrollbars that share a visual style.
Comparison of the three factory patterns:
Simple Factory: one factory with a switch; adding a product requires modifying the switch (violates Open/Closed).
Factory Method: one factory per product; adding a product adds a pair of classes (fits Open/Closed).
Abstract Factory: one factory per product family; guarantees bundled creation; adding a family is easy, adding a product type is hard (the tilt).
In summary, Abstract Factory is the two‑dimensional extension of the factory family, ideal when you need to create and switch whole product families consistently. Its characteristic "open‑closed tilt" determines the boundary of applicability: frequent family growth with stable product types fits perfectly; otherwise, fall back to Factory Method or Simple Factory.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
