Abstract Factory

Abstract Factory

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.

  • We can also call it as factory of factories

Classes in abstract factory pattern

  • AbstractFactory - declares a interface for operations that create abstract products.
  • ConcreteFactory - implements operations to create concrete products.
  • AbstractProduct - declares an interface for a type of product objects.
  • Product - defines a product to be created by the corresponding ConcreteFactory; it implements the AbstractProduct interface.
  • Client - uses the interfaces declared by the AbstractFactory and AbstractProduct classes.

When to abstract factory?

  • The problems solved by abstract factory are similar to those solved by the factory method pattern, but with greater abstraction in the types of objects that need to be created. Therefore, in the case of abstract factory, it is required to work with several families of products related to each other rather than in a set of products.
  • The family of objects with which the client must work is not known a priori. Rather, this knowledge depends directly on the interaction of another user with the system (end user or system).
  • In the event that it is necessary to extend the internal components (the number of families and objects that are created) without having to have the code coupled, but rather have interfaces and abstractions that allow to easily extend with factories and specific products.

Advantages

  • Compatibility between products created by the same factory class is guaranteed.
  • Clean code because the open-closed principle is guaranteed since new product families can be introduced without breaking the existing code.
  • Cleaner code because the single responsibility principle (SRP) is respected since the responsibility of creating the concrete product is transferred to the concrete creator class instead of the client class having this responsibility.

Disadvantages

  • However, the main drawback of the abstract factory pattern, like most design patterns, is that there is an increase in complexity in the code and an increase in the number of classes required for the code. However, this disadvantage is well known when applying design patterns for it is the price to pay for gaining abstraction in the code.

Example use cases of abstract factory

use case: produce different types of mobiles

Let's assume we have a manufacturing factory which creates mobile phones like Oneplus, Sony, iphone and we want to apply the abstract factory pattern to this case.

We need a abstract factory class.

from abc import ABC, abstractmethod

# abstract product
class AbstractMobile(ABC):
    @abstractmethod
    def getBrand(self):
        pass

# product
class Iphone(AbstractMobile):
    def getBrand(self):
        return "Iphone"

# product
class OnePlus(AbstractMobile):
    def getBrand(self):
        return "OnePlus"

# product
class Sony(AbstractMobile):
    def getBrand(self):
        return "Sony"

# abstract factory
class AbstractMobileFactory(ABC):

    @abstractmethod
    def getMobile(self, name):
        """ get mobile"""

# product factory
class AndroidMobileFactory(AbstractMobileFactory):
    def getMobile(self, name):
        if(name == "OnePlus"):
            return OnePlus()
        elif(name == "Sony"):
            return Sony()

# product factory
class IphoneMobileFactory(AbstractMobileFactory):
    def getMobile(self):
        return Iphone()


# factory producer
class FactoryProducer:
    @staticmethod
    def getFactory(os_type):
        if(os_type == "android"):
            return AndroidMobileFactory()
        elif(os_type == "ios"):
            return IphoneMobileFactory()

# client
if __name__ == "__main__":
    android_factory = FactoryProducer.getFactory('android')
    one_plus_mobile = android_factory.getMobile('OnePlus')
    print(one_plus_mobile.getBrand())
    one_plus_mobile = android_factory.getMobile('Sony')
    print(one_plus_mobile.getBrand())

    ios_factory = FactoryProducer.getFactory('ios')
    iphone_mobile = ios_factory.getMobile()
    print(one_plus_mobile.getBrand())

From the above code

  • AbstractMobileFactory is abstract factory
  • AbstractMobile is abstract product
  • FactoryProducer is abstract product factory
  • __name__ == "__main__" is the client