Abstract Factory Pattern explained simply
When you want to create families of related objects without specifying their concrete classes, the Abstract Factory Pattern is your best friend.
What is the Abstract Factory Pattern?
- Provides an interface for creating families of related or dependent objects.
- Does not specify their concrete classes.
- Helps ensure that products from the same family are used together.
In short:
A factory of factories!
Class Diagram
Here is how the pattern maps to a database access layer. The DatabaseFactory is the abstract factory, and each concrete factory (MySQLFactory, PostgreSQLFactory) produces a matching family of products (Connection + QueryBuilder).
Real-Life Analogy
Think about how your application talks to a database. During development you might use MySQL on your local machine, but production runs PostgreSQL. You do not want if (database == "mysql") scattered across every single file that touches the database.
Instead, you build one factory per database family. Each factory knows how to create the right connection and the right query builder for its database. The rest of your code just talks to the factory interface and never knows which database is behind it.
That is the Abstract Factory Pattern in action.
Structure
- Abstract Factory (
DatabaseFactory): Interface that declares methods for creating each product in the family. - Concrete Factories (
MySQLFactory,PostgreSQLFactory): Implementations that produce concrete products belonging to one family. - Abstract Products (
Connection,QueryBuilder): Interfaces for each kind of product the factory can create. - Concrete Products (
MySQLConnection,MySQLQueryBuilder,PostgreSQLConnection,PostgreSQLQueryBuilder): The actual objects. Each concrete factory creates its own variant.
Example in Java
Abstract Product Interfaces
public interface Connection {
void connect();
void close();
}
public interface QueryBuilder {
String select(String table);
String insert(String table, String[] columns);
}
Concrete Products – MySQL Family
public class MySQLConnection implements Connection {
@Override
public void connect() {
System.out.println("Connected to MySQL database.");
}
@Override
public void close() {
System.out.println("MySQL connection closed.");
}
}
public class MySQLQueryBuilder implements QueryBuilder {
@Override
public String select(String table) {
return "SELECT * FROM `" + table + "` LIMIT 100;";
}
@Override
public String insert(String table, String[] columns) {
return "INSERT INTO `" + table + "` (" + String.join(", ", columns) + ") VALUES (...);";
}
}
Concrete Products – PostgreSQL Family
public class PostgreSQLConnection implements Connection {
@Override
public void connect() {
System.out.println("Connected to PostgreSQL database.");
}
@Override
public void close() {
System.out.println("PostgreSQL connection closed.");
}
}
public class PostgreSQLQueryBuilder implements QueryBuilder {
@Override
public String select(String table) {
return "SELECT * FROM \"" + table + "\" FETCH FIRST 100 ROWS ONLY;";
}
@Override
public String insert(String table, String[] columns) {
return "INSERT INTO \"" + table + "\" (" + String.join(", ", columns) + ") VALUES (...) RETURNING id;";
}
}
Notice the small but real differences. MySQL uses backtick-quoted identifiers and LIMIT, while PostgreSQL uses double-quoted identifiers and FETCH FIRST ... ROWS ONLY. Each family encapsulates its own dialect so the calling code never has to care.
Abstract Factory
public interface DatabaseFactory {
Connection createConnection();
QueryBuilder createQueryBuilder();
}
Concrete Factories
public class MySQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new MySQLConnection();
}
@Override
public QueryBuilder createQueryBuilder() {
return new MySQLQueryBuilder();
}
}
public class PostgreSQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new PostgreSQLConnection();
}
@Override
public QueryBuilder createQueryBuilder() {
return new PostgreSQLQueryBuilder();
}
}
Using the Abstract Factory
public class Application {
public static void main(String[] args) {
// Swap this one line to change the entire database family.
DatabaseFactory factory = new MySQLFactory();
Connection conn = factory.createConnection();
QueryBuilder qb = factory.createQueryBuilder();
conn.connect();
System.out.println(qb.select("users"));
System.out.println(qb.insert("users", new String[]{"name", "email"}));
conn.close();
System.out.println("---");
// Switching to PostgreSQL is a one-line change.
factory = new PostgreSQLFactory();
conn = factory.createConnection();
qb = factory.createQueryBuilder();
conn.connect();
System.out.println(qb.select("users"));
System.out.println(qb.insert("users", new String[]{"name", "email"}));
conn.close();
}
}
Output:
Connected to MySQL database.
SELECT * FROM `users` LIMIT 100;
INSERT INTO `users` (name, email) VALUES (...);
MySQL connection closed.
---
Connected to PostgreSQL database.
SELECT * FROM "users" FETCH FIRST 100 ROWS ONLY;
INSERT INTO "users" (name, email) VALUES (...) RETURNING id;
PostgreSQL connection closed.
The entire database family changed, but the client code (Application) did not need a single if statement. That is the whole point.
Why Use the Abstract Factory Pattern?
- Consistency: A
MySQLFactorywill never accidentally pair a MySQL connection with a PostgreSQL query builder. Products from the same family always match. - Scalability: Need to add SQLite support? Create
SQLiteFactory,SQLiteConnection, andSQLiteQueryBuilder. Zero changes to existing code. - Decoupling: Your business logic depends on
ConnectionandQueryBuilderinterfaces, not on MySQL or PostgreSQL classes. You can swap databases from a config file.
Real-World Use Cases
- Database abstraction layers – exactly the example above. Libraries like JDBC and Hibernate use this pattern internally.
- Cross-platform UI toolkits – a
WindowsUIFactorycreates Windows buttons and dialogs, aMacUIFactorycreates Mac variants. - Cloud provider SDKs – one factory for AWS services, another for GCP, same interface for your application code.
- Serialization – a
JsonSerializerFactoryand anXmlSerializerFactorythat each produce a matching set of readers and writers.
Abstract Factory vs Factory Method
| Factory Method | Abstract Factory Pattern |
|---|---|
| Creates one product | Creates families of products |
| Single factory method | Multiple related create methods |
e.g. createConnection() |
e.g. createConnection() + createQueryBuilder() together |
| Simpler, focused | Bigger, ensures family consistency |
Summary
The Abstract Factory Pattern is all about producing families of related objects without coupling your code to their concrete classes. When you see yourself needing to swap an entire group of related objects together – like switching from MySQL to PostgreSQL, or from one cloud provider to another – this is the pattern you reach for.
It keeps your families consistent, your code decoupled, and your architecture ready to scale.