There is a way to generate objects without being tied to concreteness of a certain object. That is called a Factory Pattern or a Factory Method. However, often what happens is that we need to create not one type of an object but several similar ones. Imagine running a restaurant, and needing to have several factories at your disposal, among which there are two similar factories: ColdFactory and HotFactory. There are times when your clients might want a cool version of a particular soup, especially during summer. Here you can see that instead of one type of created objects we have two: ColdSoup and HotSoup, but both of them are children of the class Soup. In the same way as both of our factories are children of AbstractFactory class. Imagine that we had some weird clients out there that enjoyed not only cold soup, but also cold pizza, hot soda and so on. We can extend the functionality of our factories onto several more classes in exact same manner. And thus, we have come to create the Abstract Factory.
The Abstract Factory Pattern:
provides an interface for creating families of related or dependent objects without specifying their concrete classes.
There are many similarities and differences between Factory Method and Abstract Factory Pattern. We should be aware of them to be able to distinguish and know where to apply each one of them appropriately.
- Factory Method creates objects through inheritance. You need to extend a class and override a factory method, which creates objects. You make a subclass do the creation, not the parent class. Clients interact with abstract definitions so they really have no idea who is responsible, thus the code stays loosely coupled.
- Abstract Factory does it through composition. It provides an abstract type for creating a family of products. Subclasses of that type define how those products are created. It also keeps the client decoupled from the implementation. On top of that just as we have seen, this pattern groups related products into “families”.
- One drawback of Abstract Factory is that it would need to change its interface if we decide to add another set of related products to it. This pattern requires bigger interface, because it is responsible for creation of the whole family of products.
- Both patterns are good at object creation: they encapsulate the creation and keep themselves loosely tied to the rest of the application.
Here you can see another example of Abstract Factory implementation. Here we implement a GUIFactory that creates different buttons based on the context.
// GUIFactory in PHP
// Source: https://en.wikipedia.org/wiki/Abstract_factory_pattern
interface Button
{
public function paint();
}
interface GUIFactory
{
public function createButton(): Button;
}
class WinFactory implements GUIFactory
{
public function createButton(): Button
{
return new WinButton();
}
}
class OSXFactory implements GUIFactory
{
public function createButton(): Button
{
return new OSXButton();
}
}
class WinButton implements Button
{
public function paint()
{
echo "Windows Button";
}
}
class OSXButton implements Button
{
public function paint()
{
echo "OSX Button";
}
}
$appearance = "osx";
$factory = NULL;
switch ($appearance) {
case "win":
$factory = new WinFactory();
break;
case "osx":
$factory = new OSXFactory();
break;
default:
break;
}
if ($factory instanceof GUIFactory) {
$button = $factory->createButton();
$button->paint();
}
# GUIFactory in Python
# Source: https://en.wikipedia.org/wiki/Abstract_factory_pattern
from __future__ import print_function
from abc import ABCMeta, abstractmethod
class Button:
__metaclass__ = ABCMeta
@abstractmethod
def paint(self):
pass
class LinuxButton(Button):
def paint(self):
return "Render a button in a Linux style"
class WindowsButton(Button):
def paint(self):
return "Render a button in a Windows style"
class MacOSButton(Button):
def paint(self):
return "Render a button in a MacOS style"
class GUIFactory:
__metaclass__ = ABCMeta
@abstractmethod
def create_button(self):
pass
class LinuxFactory(GUIFactory):
def create_button(self):
return LinuxButton()
class WindowsFactory(GUIFactory):
def create_button(self):
return WindowsButton()
class MacOSFactory(GUIFactory):
def create_button(self):
return MacOSButton()
appearance = "linux"
if appearance == "linux":
factory = LinuxFactory()
elif appearance == "osx":
factory = MacOSFactory()
elif appearance == "win":
factory = WindowsFactory()
else:
raise NotImplementedError(
"Not implemented for your platform: {}".format(appearance)
)
if factory:
button = factory.create_button()
result = button.paint()
print(result)
Decision which one to use should be based on whether you are creating a family of related products or just a couple of different ones. Factories will keep your code clean and make it ready to reuse again.

