Patterns
YouTube Video for the Abstract Factory Pattern in Java and Python
This article exists on YouTube

The entire content of this article is also available on YouTube. It's your choice: if you prefer video content, you can watch it on YouTube. If you prefer reading, feel free to continue here.

Head over to the YouTube video

What is an Abstract Factory Pattern?

The Abstract Factory Pattern is a programming pattern used in software engineering and is part of the group of creational patterns. It may be used in any object-oriented programming language, such as Java, Python, C++, and so on. What it does is to provide a way to create a so-called family of products, meaning the creation of related objects within one place. To relate products and create the family of products, interfaces are used, providing a general structure of needed methods, without providing implementation. The implementation is then done by the objects that implement the interface.

The idea of the Abstract Factory Pattern is to separate between a factory and between products. The factory produces the products. It is the place where the object composition takes place. The factory definition itself is abstract though, meaing that it defines the actions it can take, but without providing the details of how to apply them and what steps to take. The details would then be found within the concrete factories that implement the abstract factory definition.

To add new products that can be produced by the factory, the factory interface needs to be adapted. After that the concrete factories need to add the appropriate steps to define what should be done.

So what is the Abstract Factory Pattern used for? This is particularly useful in scenarios where a system needs to be independent of how its objects are created, composed, and represented. Examples of real-world use include cross-platform development, product variants, dependency injection, plugin systems, database abstraction, testing and mocking.

Cross-Platform Development
Depending on the type of software that is programmed there are things to consider when the same code runs on different operating systems. The Abstract Factory Pattern allows to abide to the system specific rules and regulations while remaining agnostically when working with the created objects in the code.

Product Variants
When an application holds families of related products the Abstract Factory Pattern can be used to create those object families, grouping functionality and preferences within one factory. A potential second or third factory would then adapt the functionality and preferences according to their needs. Prominent examples herefore could be multiple graphical controls having one type of look within one setting (factory 1) and another look in another environment (factory 2).

Dependency Injection
In the context of Dependency Injection the Abstract Factory pattern is used to decouple the so-called client code from the concrete implementation of the dependencies. The client code is the part of the code that is being injected to. It is the receiving end that gets the injectioin and continues its work with the object that was provided.
Topics relating to Dependency Injection:
  • Abstraction of Dependencies
    The factory itself is an interface. This provides a layer of abstraction that defines mainly methods for creating instances of dependencies without their implementation details. The client accesses these objects by their interface and therefore does not need to know all that the object is capable of. The only thing that is important for the client is to know what methods can be executed.
  • Injection of Dependencies
    Within Dependency Injection the objects are not created by the client. They are instead injected into the class of the client. The way this is handled is that the client knows how to have the class injected, an information that is located within the factory interface.
  • Loose Coupling
    Because the factory interface is the access point for the client, the client class becomes loosely coupled with the dependencies. With this approach, different implementations of the dependencies can be provided to the client class without requiring changes to their implementation.
  • Testability
    Making use of Dependency Injection facilitates easier testing of the client. Mock or stub implementations of dependencies allow to test the desired pieces of code. A mock would help to mimic the behavior of a real object in a controlled way, and a stub would provide predetermined responses to method calls.

Plugin Systems
With the Abstract Factory Pattern it becomes possible to allow to extend the application's functionality with plugins. Plugins can provide additional features or services to the applications.

Database Abstraction
When making use of databases often the decision is made to use one database system and then stick with it. This may become impractical in the future when for whatever reason the database system changes. Even though SQL provides a generalized way of communicating with relational databases, different accents of SQL are still there and need to be addressed, when the database is changed. The Abstract Factory Pattern provides a way to handle this through abstraction. The different database systems would be defined through the patterns' factories and the specifics of each database query would be handled by the patterns' products. The client code would then only have to change the factory, while the rest of its code would remain identical, providing a seamless switch between database systems.

What is the structure of a Abstract Factory Pattern?

  1. AbstractFactory
    Creating this interface declares a set of methods for creating product objects. It defines a contract for the concrete factory classes to implement the creation of families of products.
  2. ConcreteFactory1 and ConcreteFactory2
    These are implementations of the AbstractFactory interface. They have the responsibility for creating a family of products.
  3. AbstractProductA and AbstractProductB
    Creating these interfaces enables to declare methods common to a family of products. They define a contract for concrete product classes to implement.
  4. ConcreteProductA1, ConcreteProductA2, ConcreteProductB1, ConcreteProductB2
    These are implementations of the AbstractProductA or AbstractProductB interfaces. Each concrete product class provides a specific implementation of the methods declared in the corresponding interface.

Example Codes

There are a number of programming languages where the Abstract Factory Pattern can prove useful. Every language that supports the construct of classes can make use of this pattern.

All the codes within this article can also be found on the GitHub repository programming-patterns and in there within the directory Abstract Factory Pattern.

Java Example

The following example is a Java application putting the Abstract Factory Pattern into action. All the following files need to be placed into the same folder so that the code within the files can work properly, because the files also reference each other. Being the smallest possible code example, this little project consists of ten Java files:

  1. AbstractFactory.java
  2. AbstractProductA.java
  3. AbstractProductB.java
  4. ConcreteFactory1.java
  5. ConcreteFactory2.java
  6. ConcreteProductA1.java
  7. ConcreteProductA2.java
  8. ConcreteProductB1.java
  9. ConcreteProductB2.java
  10. Main.java

The first thing that needs to be defined are the products that we want to create. We define them as abstract products, because this can be seen as a general description of what parameters the product should have and what it can do. Within this example we will create two of these abstract products and we name them A and B.

public interface AbstractProductA {
    void build();
}
public interface AbstractProductB {
    void build();
}

It is the goal to eventually build concrete products and not abstract ones. They base upon the abstract products though and therefore also implement their logic. Every concrete product we build exists in two variants, that we call 1 and 2. This mens that we have four concrete products in total, meaning two concrete products of type 1 and two concrete products of type 2. Those are our two families of products.

As we did also define what the products can do within their abstract defintion, we need to implement whatever it is that has been defined. Within this example it would be the build() method that needs implementation. Within this implementation we will print a simple text that will later show when the application is executed.

public class ConcreteProductA1 implements AbstractProductA {
    @Override
    public void build() {
        System.out.println("Building concrete product A1");
    }
}
public class ConcreteProductB1 implements AbstractProductB {
    @Override
    public void build() {
        System.out.println("Building concrete product B1");
    }
}
public class ConcreteProductA2 implements AbstractProductA {
    @Override
    public void build() {
        System.out.println("Building concrete product A2");
    }
}
public class ConcreteProductB2 implements AbstractProductB {
    @Override
    public void build() {
        System.out.println("Building concrete product B2");
    }
}

For the concrete products to be created we need to define factories. What a factory should look like in general will be defined within an abstract factory class.

public interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

Out of the abstract factory we now create two concrete factories that we will name 1 and 2. They both need to implement what we have defined within their abstract definition. This would be the two methods createProductA() and createProductB().

public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}
public class ConcreteFactory2 implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB2();
    }
}

And that is it already. The main construct of the Abstract Factory Pattern consists of these nine Java files. The only thing that remains is to apply their logic within the executing main class. The main(...) method calls the createProducts(...) twice by providing first an instance of the concrete factory 1 and second an instance of the concrete factory 2.

Within the createProducts(...) method the two products A and B are created, based on the concrete factory that was provided for them. After their creation their methods are both executed, which is their build() method.

public class Main {
    public static void main(String[] args) {
        System.out.println("Abstract Factory Pattern in Java");
        System.out.println("--------------------------------");
        System.out.println();
  
        createProducts(new ConcreteFactory1());
        createProducts(new ConcreteFactory2());
    }

    private static void createProducts(AbstractFactory concreteFactory) {
        AbstractProductA productA = concreteFactory.createProductA();
        AbstractProductB productB = concreteFactory.createProductB();

        productA.build();
        productB.build();
    }
}

There also needs to be a Manifest.txt file within the same folder. This is a metadata file used primarily in JAR files. It contains information about the JAR file itself and its contents. It is placed within thie META-INF folder within the JAR archive file structure.

Main-Class: Main

To compile this code into an executable Java application which would be a JAR file, a couple of commands need to be executed from within the same directory. The commands are found within the build file.

build.cmd (Windows)
@echo off

javac *.java
jar cvfm AbstractFactoryDemo.jar Manifest.txt *.class
build (MacOS / Linux)
#!/bin/bash

javac *.java
jar cvfm AbstractFactoryDemo.jar Manifest.txt *.class

Now that the compilation has taken place, there are new files within the same directory. Every JAVA file has its equivalent CLASS file, and there is also a file called AbstractFactoryDemo.jar. This is the Java application that needs to be executed. The command for that is within the run file.

run.cmd (Windows)
@echo off

java -jar AbstractFactoryDemo.jar
run (MacOS / Linux)
#!/bin/bash

java -jar AbstractFactoryDemo.jar

Testing the Java Example

Now that all the files are ready and within their correct location they can be put to test. First we need to compile the Java code and then we can execute it to see what it will print in the Windows Command Prompt or in the MacOS / Linux Terminal.

build.cmd (Windows)

build.cmd (Windows)

build (MacOS / Linux)

build (MacOS / Linux)

run.cmd (Windows)

run.cmd (Windows)

run (MacOS / Linux)

run (MacOS / Linux)

Python Example

The following example is a Python application putting the Abstract Factory Pattern into action. All the following files need to be placed into the same folder so that the code within the files can work properly, because the files also reference each other. As this is the smallest possible example, this little project consists of ten Python files.

  1. abstract_factory.py
  2. abstract_product_a.py
  3. abstract_product_b.py
  4. concrete_factory_1.py
  5. concrete_factory_2.py
  6. concrete_product_a1.py
  7. concrete_product_a2.py
  8. concrete_product_b1.py
  9. concrete_product_b2.py
  10. main.py

The first thing that needs to be defined are the products that we want to create. We define them as abstract products, because this can be seen as a general description of what parameters the product should have and what it can do. Within this example we will create two of these abstract products and we name them A and B. As this is an abstract class, we will make use of the abc module for its ABC (Abstract Base Class) and its @abstractmethod decorator.

from abc import ABC, abstractmethod

class AbstractProductA(ABC):
    @abstractmethod
    def build(self):
        pass
from abc import ABC, abstractmethod

class AbstractProductB(ABC):
    @abstractmethod
    def build():
        pass

It is the goal to eventually build concrete products and not abstract ones. They base upon the abstract products though and therefore also implement their logic. Every concrete product we build exists in two variants, that we call 1 and 2. This mens that we have four concrete products in total, meaning two concrete products of type 1 and two concrete products of type 2. Those are our two families of products.

As we did also define what the products can do within their abstract defintion, we need to implement whatever it is that has been defined. Within this example it would be the build() method that needs implementation. Within this implementation we will print a simple text that will later show when the application is executed.

from abstract_product_a import AbstractProductA

class ConcreteProductA1(AbstractProductA):
    def build(self):
        print("Building concrete product A1")
from abstract_product_b import AbstractProductB

class ConcreteProductB1(AbstractProductB):
    def build(self):
        print("Building concrete product B1")
from abstract_product_a import AbstractProductA

class ConcreteProductA2(AbstractProductA):
    def build(self):
        print("Building concrete product A2")
from abstract_product_b import AbstractProductB

class ConcreteProductB2(AbstractProductB):
    def build(self):
        print("Building concrete product B2")

For the concrete products to be created we need to define factories. What a factory should look like in general will be defined within an abstract factory class. As this is an abstract class, we will make use of the abc module for its ABC (Abstract Base Class) and its @abstractmethod decorator.

from abc import ABC, abstractmethod

class AbstractFactory(ABC):
    @abstractmethod
    def createProductA(self):
        pass

    @abstractmethod
    def createProductB(self):
        pass

Out of the abstract factory we now create two concrete factories that we will name 1 and 2. They both need to implement what we have defined within their abstract definition. This would be the two methods createProductA() and createProductB().

from abstract_factory import AbstractFactory
from concrete_product_a1 import ConcreteProductA1
from concrete_product_b1 import ConcreteProductB1

class ConcreteFactory1(AbstractFactory):
    def createProductA(self):
        return ConcreteProductA1()
    
    def createProductB(self):
        return ConcreteProductB1()
from abstract_factory import AbstractFactory
from concrete_product_a2 import ConcreteProductA2
from concrete_product_b2 import ConcreteProductB2

class ConcreteFactory2(AbstractFactory):
    def createProductA(self):
        return ConcreteProductA2()
    
    def createProductB(self):
        return ConcreteProductB2()

And that is it already. The main construct of the Abstract Factory Pattern consists of these nine Python files. The only thing that remains is to apply their logic within the executing main class. The main() method calls the createProducts(...) twice by providing first an instance of the concrete factory 1 and second an instance of the concrete factory 2.

Within the createProducts(...) method the two products A and B are created, based on the concrete factory that was provided for them. After their creation their methods are both executed, which is their build() method.

from concrete_factory_1 import ConcreteFactory1
from concrete_factory_2 import ConcreteFactory2

def main():
    print("Abstract Factory Pattern in Python")
    print("----------------------------------")
    print()

    createProducts(ConcreteFactory1())
    createProducts(ConcreteFactory2())

def createProducts(concreteFactory):
    productA = concreteFactory.createProductA()
    productB = concreteFactory.createProductB()

    productA.build()
    productB.build()

if __name__ == "__main__":
    main()

There is no need to compile anything in Python. The code is executed directly using the python command under Windows and the python3 command under MacOS / Linux.

run.cmd (Windows)
@echo off

python main.py
run (MacOS / Linux)
#!/bin/bash

python3 main.py

Testing the Python Example

Now that all the files are ready and within their correct location they can be put to test. Python does not need to be compiled before being run. Therefore we can execute it right away to see what it will print in the Windows Command Prompt or in the MacOS / Linux Terminal.

run.cmd (Windows)

run.cmd (Windows)

run (MacOS / Linux)

run (MacOS / Linux)

More Information

So, where do I have my information from? In general, there are lots of places on the internet that offer great information. But that is just one source of information. Another source would be books. Those have the advantage that usually several people work on the same book before it is released, raising the chance for distribution of good and proven information.

Head First - Design Patterns

Building Extensible and Maintainable Object-Oriented Software

A book that has proven to be a valuable asset within my personal library is Head First - Design Patterns by the publisher O'Reilly. It has a fun approach to this topic, having recurring characters, such as the Guru, the Developer, the Skeptical Developer, Joe, and many more.

Java Design Patterns

A Hands-On Experience with Real-World Examples

Another highly valuable book is Java Design Patterns: A Hands-On Experience with Real-World Examples by the publisher Apress. It offers a conservative and rather scientific approach to the subject. It also offers code examples in Java, although any object-oriented programming language applies to the design patterns shown here.

We use cookies

We deeply value your privacy and want to ensure you have the best possible experience on our website. By accepting our Cookie Consent, you're helping us tailor your visit to your preferences, providing you with personalized content and ensuring that all features work seamlessly.

However, if you choose not to accept our Cookie Consent, some parts of the website may not function optimally. We want you to enjoy full access to all the great content and features we offer, so we encourage you to consider accepting our Cookie Consent.

Please be aware that even if you choose not to accept our Cookie Consent, you can still browse our website. However, some functionalities may be limited, and you may not experience the website to its fullest potential.

For more details on how we handle your data, please refer to our Privacy Policy.