Abstract Factory Pattern

Is a sub-set of the Factory Method Pattern. Difference:

Each factory (creator) is able to create multiple products, not only one -> ability to create families of related or dependent objects (the abstract factory is a blueprint, that defines what type of Product a factory must be able to instantiate)

Definition

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Principle 1

Depend upon abstraction. Do not depend upon concrete classes.

This is known as the Dependency Inversion Principle. It appears similar to the principle, to program against an interface, not an implementation, but goes one step further: It defines, that even the high level classes (e.g. a Zoo class), should depend on an abstraction (e.g. an Animal class), as well a low level classes (e.g. Rabbit), which implements the abstraction.

Some guidelines which help to follow the Dependency Inversion Principle:

  • No variable should hold reference to a concrete class

  • No class should derive from a concrete class

  • No method should overwrite an implemented method of any of its base classes

Naturally, these rules must often be broken, but it is a guideline to strive for.

The interface factory implements multiple creation methods, in which each is supposed to instantiate a different type of object.

Example

Use the Abstract Factory when your code needs to work with various families of related products, but you don’t want it to depend on the concrete classes of those products—they might be unknown beforehand or you simply want to allow for future extensibility.

Find a template example for Python at https://refactoring.guru/design-patterns/abstract-factory/python/example

Applications are * User interfaces (compatability for multiple OS) * Light Theme / Dark Theme implementations

from abc import ABC


class Chair(ABC):

    def has_legs(self):
        pass

    def sit_on(self):
        pass


class VictorianChair(Chair):

    def has_legs(self):
        return True

    def sit_on(self):
        return False


class ModernChair(Chair):

    def has_legs(self):
        return False

    def sit_one(self):
        return True

# Equal interfaces and concretes for other furniture types like Sofa or
# CoffeeTable
# ...


class FurnitureFactory(ABC):

    def create_chair(self):
        pass

    def create_coffee_table(self):
        pass

    def create_sofa(self):
        pass


class VictorianFurnitureFactory(FurnitureFactory):

    def create_chair(self):
        return VictorianChair()

    def create_coffee_table(self):
        # Should return proper object
        pass

    def create_sofa(self):
        # Should return proper object
        pass


class ModernFurnitureFactory(FurnitureFactory):

    def create_chair(self):
        return ModernChair()

    def create_coffee_table(self):
        # Should return proper object
        pass

    def create_sofa(self):
        # Should return proper object
        pass