A way to free your class from having to create the objects it depends on.
Now the rest of this article explains the Abstract Factory pattern in 2 parts: The Problem and The Solution.
It is less motivating to study something if we don’t understand why that thing is important. That is why before getting into the what, let’s first understand the why. So the question to be answered first is: Why should a developer care about the Abstract Factory pattern?
Let’s say we are writing an application that will have to download an open-source component named “A” to run some tasks. We don’t give much thought about the possibility of having to switch to a different component in the future, so we go ahead and write classes that new up instances of an “ADownloader” class, which is responsible for getting the open-source library “A”. Suddenly, our class has to be aware of how to create that object and what dependencies it expects. At this point we are programming to the concrete type, and not to interfaces.
Soon after, our team or company decides that we will use a different open-source library named “B” and the previous one must be replaced in our code. Since we wrote classes that directly create the objects that interact with the “A” library, we made our app more tightly coupled to the that component, which makes it much more difficult to switch.
Why should we care about the Abstract Factory pattern? Because it is a well-tested pattern that aims to solve the problem above and by understanding and applying it we can save much time considering that our object-oriented components will be more maintainable.
What if there was a way to allow our “Downloader” classes to simply ask for an object that performs downloads and have some other part of the system take care of selecting the right type, instantiating and providing it to the calling class?
Enters the “Abstract Factory” pattern.
To exemplify this solution I will switch from the open-source downloader story above, to a more concrete analogy, a car manufacturer.
Suppose that an assembly line is currently producing hatchback cars. One of the robotic arms in that line was designed specifically to install the trunk door. However, one day the manufacturer decides to produce sedan cars in that same assembly line. They soon realize that the new plan will be delayed because one of the robotic arms was programmed to only work with hatchback cars and so it will have to be reprogrammed.
The problem here is that the robotic arm in our example is programmed to work with a specific car type, a concrete type. This has made the assembly line coupled to an specific car type.
Back to our analogy, this time the technicians decided to take a more flexible approach and they programmed the arm to be unware of the type of car it is working with, and all that it knows is that it has to install doors. The arm interfaces and gets the doors from a conveyor belt which will have doors for the car type currently being built in the assembly line.
Of course, this example is not perfect but it helps us understand that making the robotic arm unware of the car type made it more extendable to work with different types.
Similarly, in the world of object-oriented software, the Abstract Factory pattern helps us create classes (robotic arms) that are unware of the concrete types of the objects it will receive from an interface (conveyor belt). Depending on the context, that interface may return different types to the calling class which makes our application less coupled, more maintainable, testable and extendable.