Skip to content

Factory Design Pattern

Video Lecture

Section Video Links
Factory Overview Factory Overview Factory Overview Factory Overview 
Factory Use Case Factory Use Case Factory Use Case Factory Use Case 
ABCMeta Module ABCMeta Module ABCMeta Module ABCMeta Module 

Overview

When developing code, you may instantiate objects directly in methods or in classes. While this is quite normal, you may want to add an extra abstraction between the creation of the object and where it is used in your project.

You can use the Factory pattern to add that extra abstraction. The Factory pattern is one of the easiest patterns to understand and implement.

Adding an extra abstraction will also allow you to dynamically choose classes to instantiate based on some kind of logic.

Before the abstraction, your class or method would directly instantiate an object of a class. After adding the factory abstraction, the concrete product (object) is now created outside of the current class/method, and now in a subclass instead.

Imagine an application for designing houses and the house has a chair already added on the floor by default. By adding the factory pattern, you could give the option to the user to choose different chairs, and how many at runtime. Instead of the chair being hard coded into the project when it started, the user now has the option to choose.

Adding this extra abstraction also means that the complications of instantiating extra objects can now be hidden from the class or method that is using it.

This separation also makes your code easier to read and document.

The Factory pattern is really about adding that extra abstraction between the object creation and where it is used. This gives you extra options that you can more easily extend in the future.

Terminology

  • Concrete Creator: The client application, class or method that calls the Creator (Factory method).

  • Product Interface: The interface describing the attributes and methods that the Factory will require in order to create the final product/object.

  • Creator: The Factory class. Declares the Factory method that will return the object requested from it.

  • Concrete Product: The object returned from the Factory. The object implements the Product interface.

Factory UML Diagram

Factory Pattern Overview

Source Code

...Refer to Book or Videos for extra content.

./factory/factory_concept.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# pylint: disable=too-few-public-methods
"The Factory Concept"
from abc import ABCMeta, abstractmethod

class IProduct(metaclass=ABCMeta):
    "A Hypothetical Class Interface (Product)"

    @staticmethod
    @abstractmethod
    def create_object():
        "An abstract interface method"

class ConcreteProductA(IProduct):
    "A Concrete Class that implements the IProduct interface"

    def __init__(self):
        self.name = "ConcreteProductA"

    def create_object(self):
        return self

class ConcreteProductB(IProduct):
    "A Concrete Class that implements the IProduct interface"

    def __init__(self):
        self.name = "ConcreteProductB"

    def create_object(self):
        return self

class ConcreteProductC(IProduct):
    "A Concrete Class that implements the IProduct interface"

    def __init__(self):
        self.name = "ConcreteProductC"

    def create_object(self):
        return self

class Creator:
    "The Factory Class"

    @staticmethod
    def create_object(some_property):
        "A static method to get a concrete product"
        if some_property == 'a':
            return ConcreteProductA()
        if some_property == 'b':
            return ConcreteProductB()
        if some_property == 'c':
            return ConcreteProductC()
        return None

# The Client
PRODUCT = Creator().create_object('b')
print(PRODUCT.name)

Output

1
2
python ./factory/factory_concept.py 
ConcreteProductB

Example Use Case

...Refer to Book or Videos for extra content.

Factory Example UML Diagram

Chair Factory

Source Code

./factory/client.py

1
2
3
4
5
6
7
"Factory Use Case Example Code"

from chair_factory import ChairFactory

# The Client
CHAIR = ChairFactory().get_chair("SmallChair")
print(CHAIR.get_dimensions())

./factory/interface_chair.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# pylint: disable=too-few-public-methods
"The Chair Interface"
from abc import ABCMeta, abstractmethod

class IChair(metaclass=ABCMeta):
    "The Chair Interface (Product)"

    @staticmethod
    @abstractmethod
    def get_dimensions():
        "A static interface method"

./factory/chair_factory.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
"The Factory Class"

from small_chair import SmallChair
from medium_chair import MediumChair
from big_chair import BigChair

class ChairFactory:  # pylint: disable=too-few-public-methods
    "The Factory Class"

    @staticmethod
    def get_chair(chair):
        "A static method to get a chair"
        if chair == 'BigChair':
            return BigChair()
        if chair == 'MediumChair':
            return MediumChair()
        if chair == 'SmallChair':
            return SmallChair()
        return None

./factory/small_chair.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# pylint: disable=too-few-public-methods
"A Class of Chair"
from interface_chair import IChair

class SmallChair(IChair):
    "The Small Chair Concrete Class implements the IChair interface"

    def __init__(self):
        self._height = 40
        self._width = 40
        self._depth = 40

    def get_dimensions(self):
        return {
            "width": self._width,
            "depth": self._depth,
            "height": self._height
        }

./factory/medium_chair.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# pylint: disable=too-few-public-methods
"A Class of Chair"
from interface_chair import IChair

class MediumChair(IChair):
    "The Medium Chair Concrete Class implements the IChair interface"

    def __init__(self):
        self._height = 60
        self._width = 60
        self._depth = 60

    def get_dimensions(self):
        return {
            "width": self._width,
            "depth": self._depth,
            "height": self._height
        }

./factory/big_chair.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# pylint: disable=too-few-public-methods
"A Class of Chair"
from interface_chair import IChair

class BigChair(IChair):
    "The Big Chair Concrete Class implements the IChair interface"

    def __init__(self):
        self._height = 80
        self._width = 80
        self._depth = 80

    def get_dimensions(self):
        return {
            "width": self._width,
            "depth": self._depth,
            "height": self._height
        }

Output

1
2
python ./factory/client.py
{'width': 40, 'depth': 40, 'height': 40}

New Coding Concepts

ABCMeta

ABCMeta classes are a development tool that help you to write classes that conform to a specified interface that you've designed.

ABCMeta refers to Abstract Base Classes.

The benefits of using ABCMeta classes to create abstract classes is that your IDE and Pylint will indicate to you at development time whether your inheriting classes conform to the class definition that you've asked them to.

Abstract interfaces are not instantiated directly in your scripts, but instead implemented by subclasses that will provide the implementation code for the abstract interface methods. E.g., you don't create IChair, but you create SmallChair that implements the methods described in the IChair interface.

An abstract interface method is a method that is declared, but contains no implementation. The implementation happens at the class that inherits the abstract class.

You don't need to use ABCMeta classes and interfaces that you have created in your final python code. You code will still work without them.

You can try it by removing the interfaces from all of the chair classes above, and you will see that your python program will still run.

eg, change

1
class BigChair(IChair):

to

1
class BigChair():

and it will still work.

While it is possible to ensure your classes are correct without using abstract classes, it is often easier to use abstract classes as a backup method of checking correctness, especially if your projects become very large and involve many developers.

Note that in all my code examples, the abstract classes are prefixed with a capital I, to indicate that they are abstract interfaces. They have no code in their methods. They do not require a self or cls argument due to the use of @staticmethod . The inheriting class will implement the code in each of the methods that the abstract class is describing. If subclasses are inheriting an abstract base class, and they do not implement the methods as described, there will be Pylint error or warning message (E0110).

See PEP 3119 : https://www.python.org/dev/peps/pep-3119/

Summary

...Refer to Book or Videos for extra content.