11.4. Abstract Factory

  • EN: Abstract Factory

  • PL: Fabryka Abstrakcyjna

  • Type: object

The Abstract Factory pattern differs from the Factory Pattern in that it returns Factories, rather than objects of concrete class.

11.4.1. Pattern

  • Provide an interface for creating families of related objects

  • Factory Method is a method

  • Abstract Factory is an abstraction (interface)

  • Used for theme support (which generates buttons, inputs etc)

../../_images/designpatterns-abstractfactory-pattern.png

11.4.2. Problem

  • Violates Open/Close Principle

  • Hard to add a new theme

  • Easy to accidentally use Material widget inside of Flat theme block

../../_images/designpatterns-abstractfactory-problem.png
from enum import Enum


#%% Interfaces
class Widget:
    def render(self) -> None:
        raise NotImplementedError

class Button(Widget):
    pass

class Textbox(Widget):
    pass


#%% Material Theme
class MaterialButton(Button):
    def render(self) -> None:
        print('Material Button')

class MaterialTextbox(Textbox):
    def render(self) -> None:
        print('Material Textbox')


#%% Flat Theme
class FlatButton(Button):
    def render(self) -> None:
        print('Flat Button')

class FlatTextbox(Textbox):
    def render(self) -> None:
        print('Flat Textbox')


#%% Main
class Theme(Enum):
    MATERIAL = 1
    FLAT = 2


class ContactForm:
    def render(self, theme: Theme) -> None:
        if self.theme == Theme.MATERIAL:
            MaterialTextbox().render()
            MaterialButton().render()
        elif self.theme == Theme.FLAT:
            FlatTextbox().render()
            FlatButton().render()

11.4.3. Solution

design-patterns/creational/img/designpatterns-abstractfactory-solution.png
#%% Interfaces
class Widget:
    def render(self) -> None:
        raise NotImplementedError

class Button(Widget):
    pass

class Textbox(Widget):
    pass

class WidgetFactory:
    def create_button(self) -> Button:
        raise NotImplementedError

    def create_textbox(self) -> Textbox:
        raise NotImplementedError


#%% Material Theme
class MaterialButton(Button):
    def render(self) -> None:
        print('Material Button')

class MaterialTextbox(Textbox):
    def render(self) -> None:
        print('Material Textbox')

class MaterialWidgetFactory(WidgetFactory):
    def create_button(self) -> Button:
        return MaterialButton()

    def create_textbox(self) -> Textbox:
        return MaterialTextbox()


#%% Ant Theme
class AntButton(Button):
    def render(self) -> None:
        print('Ant Button')

class AntTextbox(Textbox):
    def render(self) -> None:
        print('Ant Textbox')

class AntWidgetFactory(WidgetFactory):
    def create_button(self) -> Button:
        return AntButton()

    def create_textbox(self) -> Textbox:
        return AntTextbox()


#%% Main
class ContactForm:
    def render(self, factory: WidgetFactory) -> None:
        factory.create_textbox().render()
        factory.create_button().render()


if __name__ == '__main__':
    theme = MaterialWidgetFactory()
    ContactForm().render(theme)

11.4.4. Assignments