I had a really hard time understanding what are real benefits of using AbstractFactory pattern. Now, when I've finally got it, let me explain it in 6 steps :)
Let's imagine a quite common situation in which we've been working on a custom code and we've just realised that we need to reuse some logic from utility class created by the friend of ours, working on the same team.
We have a working code but there is at least one problem with it - it violates Dependency Inversion principle. Let's try to fix it then:
Ok, much better. But the next question arises: how to create Utility class before using it...? Of course, we have a Factory pattern for this:
Our custom code is using Factory class that creates instance of Utility class and returns it as an interface for us to use.
One thing immediately to notice is that we again violate Dependency Inversion principle. For some people it can be enough reason for putting AbstractFactory in between custom code and Factory class.
But we don't have to stick to the rule, just for the sake of doing so :)
Let's ignore Dependency Inversion principle for a moment and see what will happen.
Well, on many occasions nothing will happen.....difference can be seen for more complex scenarios though.
Let's imagine that we have more than one Utility class that implements the same interface. It can look like this:
What is the problem with our design? To notice it, let's look at source code dependencies:
Custom code is placed in File 1. To make it work we need to import File 2, File 3 and apparently all files that contain all possible implementations of *Utility class - even if we use only one implementation.
It is clearly not an optimal solution.
We would like to apply Common Reuse principle. In short - we don't want to depend on things that we don't need.
If we use Utility class, version X - we wan't to depend only on its source code. Nothing more.
So, here is the final design with a use of AbstractFactory:
Assuming that each Factory class is a different module, we depend physically only on File 1, File 2, File 3, File 4, File 5.
For complex and big systems this kind of flexibility can bring many benefits.