Abstract Factory Design Pattern

How this pattern is described? Well, according to the following sources, description is somehow vague:

  1. https://sourcemaking.com/design_patterns/abstract_factory : “…lots of #ifdef case statements with options for all currently supported platforms begin to procreate like rabbits throughout the code”. So, is this pattern about rabbits?
  2. https://www.tutorialspoint.com/design_pattern/abstract_factory_pattern.htm : “super-factory”, “factory of factories”.
  3. https://www.geeksforgeeks.org/abstract-factory-pattern/ : Some creation pattern that is another layer of abstraction over factory pattern.

Does this make anything clear? Of course, NOT. Such explanations are vague, wrong and unclear why Abstract Factory is needed, since it raises simple question that no one asks? Why should I use Abstract Factory at all? Maybe I can use just a Factory? Maybe I can use just “new” and that would be it? In other words, I will never use some design pattern unless it is logically and explicitly proven useful or even unavoidable in order to solve some problem. But it looks like that people use design patterns just to look cool and knowing some stuff about patterns. This way of using some technique is extremely bad programming example which causes lots of over-engineering in the systems which is the same bad thing as under-engineering.

So, I will try to remove the shadows around this pattern. First of all, let us take a look into the structure of this pattern. The structure is pretty simple actually. First of all, we have some interface:

interface IFoo {
    void Bar();
}

Now we have lots of objects implementing interface IFoo which implements method “void Bar()” somehow differently. Why? Because they can. So, let us say we have the following implementations: Foo1, Foo2, Foo3, …, FooN. After that, we have some factory that can produce all of the foos.

interface IFooFactory {
    IFoo GetFoo(string fooType);
}

As you can see, I could have actually introduced concrete factory in advance. But instead, I have decided to introduce an interface for the factory. In this way, I am saying that we can have multiple of factories. Why? Because we do not know in advance if there will be a need for one or more factories. So, it is good to introduce an interface in case we will have to introduce something more. In other words, now we have just to introduce factories for the IFooFactory, and we already have our own Abstract Factory Design Pattern implementation. And that is everything about structure of the Abstract Factory.

In other words, it is really simple. You have just two interfaces you are working around:

interface IFactory {
    ISomeItem Create();
}
 
interface ISomeItem {
    // Contract regarding ISomeItem.
}

After that, you just implement either ISomeItem or IFactory. And that is everything you need to know about Abstract Factory. So, it is actually incorrect to say it is “factory of factories”, since you do not make any of the factories from the client point of view. For some weird reason, lots of tutorials include client as a reference, but why if it’s point of view is not taken into account at all? I am not so sure. Maybe people again pretend that they know what they do not actually at all.

So, then why is it still called as “factory of factories”. Well, it is a bit like that, but the factory itself is produced mostly at the startup of the application or something like that, i.e. there is mostly no point to have interface instantiating a factory (and this also violates SOLID, since complex logic is introduced for more paths than one) in some of the class. In some class, factory is needed at most and that is it. So, basically you would have the following code regarding this pattern:

std::string GetOperatingSystem() {
    #ifdef IS_MAC
    return "mac";
    #elif IS_WIN
    return "win";
    #endif
    return "";
}
 
int main() {
    // It is logical to assume that dialogs, some controls get built really differently on different operating systems due to massive
    // differences in their implementations.
    IViewProvider someNativeDialogProvider = ViewProviderFactory().Get(GetOperatingSystem());
 
    SomeViewClient client(someNativeDialogProvider);
    client.InitializeApp();
 
    return 0;
}
 
// Yes, it is more reasonable to be more abstract here detection of device itself and concentrate on the structure we want to layout for the user.
class SomeViewClient
{
public:
    SomeViewClient(IViewProvider& provider) : m_provider(provider) {}
 
    void InitializeApp()
    {
        m_provider.ListenForUserInput({ "first_name", "last_name" }, [this](const blah& input) {});
        m_provider.OpenMainDialog("My Awesome App", [this]
        {
            m_provider.ShowInputField("first_name", "Enter Your Name");
            m_provider.ShowInputField("last_name", "Enter Your Last Name");
            m_provider.ShowButton("Submit All Data");
        });
    }
 
private:
    IViewProvider& m_provider;
};

I think there Abstract Factory can be used, since buttons, input listeners, fields, dialogs will all be different across the platforms. So, what is the main point for the Abstract Factory? The main points are the following:

  1. Detect some input that you would like to leave out of the logic you implement. For instance, you would like on creation of data items on separate database, separate operating system. It would be not nice to have everything boiler-plated in a single place.
  2. When detecting that input, create the Factory interface that creates another Factory based on that input.
  3. Create all of the Factories you need.
  4. Each Factory should create family of objects that is relevant for the input you have extracted out of the system.

Leave a Reply

Your email address will not be published. Required fields are marked *