Java Abstract Factory Pattern example
Abstract Factory Design Pattern
The Abstract Factory Design Pattern is a creational pattern. It's main purpose is to provide a way to create a related set of concrete objects without knowing about their concrete implementation. Actually the application should also be implemented against an abstract factory interface without even knowing about the factory implementation.
Usually the concrete factory implementation is provided to the application at runtime. Since the abstract factory should always provide (or build) objects in the form of interfaces, the application will become completely decoupled from both the factory and the business objects concrete implementation.
One may notice that, since the concrete factory implementation is provided to the application at runtime, we are actually injecting the factory as a dependency into the application. The abstract factory pattern is very common in applications that rely in the Dependency Injection paradigm.
In this article we will build a simple example consisting on an abstract factory that will create illustrative connections to a database.
The database connection
Our abstract factory will be able to produce database connections. Let's define the connection interface:
package com.byteslounge.db.connection; public interface Connection { public void connect(); }
And now a couple of illustrative concrete implementations:
package com.byteslounge.db.connection; public class MySqlConnection implements Connection { @Override public void connect() { System.out.println("Connecting to MySQL..."); } }
package com.byteslounge.db.connection; public class PostgreSqlConnection implements Connection { @Override public void connect() { System.out.println("Connecting to PostgreSQL..."); } }
The abstract factory
Now let's define the abstract connection factory interface:
package com.byteslounge.db.connection.factory; import com.byteslounge.db.connection.Connection; public interface ConnectionFactory { Connection getConnection(); }
And now a couple of concrete factory implementations, one for each of the connection types we are building (MySQL and PostgreSQL):
package com.byteslounge.db.connection.factory; import com.byteslounge.db.connection.Connection; import com.byteslounge.db.connection.MySqlConnection; public class MySqlConnectionFactory implements ConnectionFactory { @Override public Connection getConnection() { return new MySqlConnection(); } }
package com.byteslounge.db.connection.factory; import com.byteslounge.db.connection.Connection; import com.byteslounge.db.connection.PostgreSqlConnection; public class PostgreSqlConnectionFactory implements ConnectionFactory { @Override public Connection getConnection() { return new PostgreSqlConnection(); } }
Now that we have everything set, we may use the abstract factory to create connections within the application.
A sample application
We may now define an illustrative application that will use an injected factory implementation:
package com.byteslounge.application; import com.byteslounge.db.connection.Connection; import com.byteslounge.db.connection.factory.ConnectionFactory; public class Application { private final ConnectionFactory connectionFactory; public Application(ConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } public void start() { Connection connection = connectionFactory.getConnection(); connection.connect(); // proceed with application execution... } }
As we can see the application is completely decoupled from both the connection factory and the database connections concrete implementations. The application only knows that it will receive some factory that will produce database connections.
Testing
Finally we create a runnable class in order to test the application:
package com.byteslounge; import com.byteslounge.application.Application; import com.byteslounge.db.connection.factory.ConnectionFactory; import com.byteslounge.db.connection.factory.MySqlConnectionFactory; import com.byteslounge.db.connection.factory.PostgreSqlConnectionFactory; public class Main { public static void main(String[] args) { // We should fetch the database type from an // external resource (ex: configuration file). // In order to keep this example simple we will // use a private static method and an Enum DatabaseType databaseType = DatabaseType.MYSQL; ConnectionFactory connectionFactory = getConnectionFactory(databaseType); Application application = new Application(connectionFactory); application.start(); } private static ConnectionFactory getConnectionFactory( DatabaseType databaseType) { switch (databaseType) { case MYSQL: return new MySqlConnectionFactory(); default: return new PostgreSqlConnectionFactory(); } } private enum DatabaseType { MYSQL, POSTGRE; } }
The database type could be configured in an external resource, like a properties file. The client would then create a concrete factory implementation from the external configuration file and inject it into the application.
To keep this example simple we used a static method that creates the concrete factory implementation based on an Enum.
Since we are creating a MySQL factory implementation and passing it into the application, the following output will be generated when the sample is executed:
Encapsulation
A final note about encapsulation: In this example we defined an abstract factory that produces only database connections. We could have defined our factory at a higher level (like a database driver) and use it to produce all sorts of low level database artifacts, like connections, transactions, prepared statements among others.
The concrete factories would then produce the database artifacts for each distinct database type they implement. By using this paradigm we are clearly also providing an higher level of encapsulation, since our abstract factory (and its concrete implementations) would encapsulate all the the database related artifacts production.