Abstract Factory Design Pattern
Video Lecture
Overview
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Terminology
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Abstract Factory UML Diagram

Source Code
./abstract_factory/abstract_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 | # pylint: disable=too-few-public-methods
"Abstract Factory Concept Sample Code"
from abc import ABCMeta, abstractmethod
from factory_a import FactoryA
from factory_b import FactoryB
class IAbstractFactory(metaclass=ABCMeta):
"Abstract Factory Interface"
@staticmethod
@abstractmethod
def create_object(factory):
"The static Abstract factory interface method"
class AbstractFactory(IAbstractFactory):
"The Abstract Factory Concrete Class"
@staticmethod
def create_object(factory):
"Static get_factory method"
try:
if factory in ['aa', 'ab', 'ac']:
return FactoryA().create_object(factory[1])
if factory in ['ba', 'bb', 'bc']:
return FactoryB().create_object(factory[1])
raise Exception('No Factory Found')
except Exception as _e:
print(_e)
return None
# The Client
PRODUCT = AbstractFactory.create_object('ab')
print(f"{PRODUCT.__class__}")
PRODUCT = AbstractFactory.create_object('bc')
print(f"{PRODUCT.__class__}")
|
./abstract_factory/factory_a.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
"FactoryA Sample Code"
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 FactoryA:
"The FactoryA Class"
@staticmethod
def create_object(some_property):
"A static method to get a concrete product"
try:
if some_property == 'a':
return ConcreteProductA()
if some_property == 'b':
return ConcreteProductB()
if some_property == 'c':
return ConcreteProductC()
raise Exception('Class Not Found')
except Exception as _e:
print(_e)
return None
|
./abstract_factory/factory_b.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
"FactoryB Sample Code"
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 FactoryB:
"The FactoryB Class"
@staticmethod
def create_object(some_property):
"A static method to get a concrete product"
try:
if some_property == 'a':
return ConcreteProductA()
if some_property == 'b':
return ConcreteProductB()
if some_property == 'c':
return ConcreteProductC()
raise Exception('Class Not Found')
except Exception as _e:
print(_e)
return None
|
Output
| python ./abstract_factory/abstract_factory_concept.py
<class 'factory_a.ConcreteProductB'>
<class 'factory_b.ConcreteProductC'>
|
Abstract Factory Example Use Case
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Abstract Factory Example UML Diagram
See this UML diagram of an Abstract Furniture Factory implementation that returns chairs and tables.

Source Code
./abstract_factory/client.py
| "Abstract Factory Use Case Example Code"
from furniture_factory import FurnitureFactory
FURNITURE = FurnitureFactory.get_furniture("SmallChair")
print(f"{FURNITURE.__class__} : {FURNITURE.get_dimensions()}")
FURNITURE = FurnitureFactory.get_furniture("MediumTable")
print(f"{FURNITURE.__class__} : {FURNITURE.get_dimensions()}")
|
./abstract_factory/furniture_factory.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | # pylint: disable=too-few-public-methods
"Abstract Furniture Factory"
from interface_furniture_factory import IFurnitureFactory
from chair_factory import ChairFactory
from table_factory import TableFactory
class FurnitureFactory(IFurnitureFactory):
"The Abstract Factory Concrete Class"
@staticmethod
def get_furniture(furniture):
"Static get_factory method"
try:
if furniture in ['SmallChair', 'MediumChair', 'BigChair']:
return ChairFactory().get_chair(furniture)
if furniture in ['SmallTable', 'MediumTable', 'BigTable']:
return TableFactory().get_table(furniture)
raise Exception('No Factory Found')
except Exception as _e:
print(_e)
return None
|
./abstract_factory/interface_furniture_factory.py
1
2
3
4
5
6
7
8
9
10
11
12 | # pylint: disable=too-few-public-methods
"The Abstract Factory Interface"
from abc import ABCMeta, abstractmethod
class IFurnitureFactory(metaclass=ABCMeta):
"Abstract Furniture Factory Interface"
@staticmethod
@abstractmethod
def get_furniture(furniture):
"The static Abstract factory interface method"
|
./abstract_factory/chair_factory.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | "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"
try:
if chair == 'BigChair':
return BigChair()
if chair == 'MediumChair':
return MediumChair()
if chair == 'SmallChair':
return SmallChair()
raise Exception('Chair Not Found')
except Exception as _e:
print(_e)
return None
|
./abstract_factory/interface_chair.py
| # 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"
|
./abstract_factory/small_chair.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Chair"
from interface_chair import IChair
class SmallChair(IChair): # pylint: disable=too-few-public-methods
"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
}
|
./abstract_factory/medium_chair.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Chair"
from interface_chair import IChair
class MediumChair(IChair): # pylint: disable=too-few-public-methods
"""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
}
|
./abstract_factory/big_chair.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Chair"
from interface_chair import IChair
class BigChair(IChair): # pylint: disable=too-few-public-methods
"The Big Chair Concrete Class that 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
}
|
./abstract_factory/table_factory.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | "The Factory Class"
from small_table import SmallTable
from medium_table import MediumTable
from big_table import BigTable
class TableFactory: # pylint: disable=too-few-public-methods
"The Factory Class"
@staticmethod
def get_table(table):
"A static method to get a table"
try:
if table == 'BigTable':
return BigTable()
if table == 'MediumTable':
return MediumTable()
if table == 'SmallTable':
return SmallTable()
raise Exception('Table Not Found')
except Exception as _e:
print(_e)
return None
|
./abstract_factory/interface_table.py
| # pylint: disable=too-few-public-methods
"The Table Interface"
from abc import ABCMeta, abstractmethod
class ITable(metaclass=ABCMeta):
"The Table Interface (Product)"
@staticmethod
@abstractmethod
def get_dimensions():
"A static interface method"
|
./abstract_factory/small_table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Table"
from interface_table import ITable
class SmallTable(ITable): # pylint: disable=too-few-public-methods
"The Small Table Concrete Class implements the ITable interface"
def __init__(self):
self._height = 60
self._width = 100
self._depth = 60
def get_dimensions(self):
return {
"width": self._width,
"depth": self._depth,
"height": self._height
}
|
./abstract_factory/medium_table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Table"
from interface_table import ITable
class MediumTable(ITable): # pylint: disable=too-few-public-methods
"The Medium Table Concrete Class implements the ITable interface"
def __init__(self):
self._height = 60
self._width = 110
self._depth = 70
def get_dimensions(self):
return {
"width": self._width,
"depth": self._depth,
"height": self._height
}
|
./abstract_factory/big_table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Class of Table"
from interface_table import ITable
class BigTable(ITable): # pylint: disable=too-few-public-methods
"The Big Chair Concrete Class implements the ITable interface"
def __init__(self):
self._height = 60
self._width = 120
self._depth = 80
def get_dimensions(self):
return {
"width": self._width,
"depth": self._depth,
"height": self._height
}
|
Output
| python ./abstract_factory/client.py
<class 'small_chair.SmallChair'> : {'width': 40, 'depth': 40, 'height': 40}
<class 'medium_table.MediumTable'> : {'width': 110, 'depth': 70, 'height': 60}
|
New Coding Concepts
Exception Handling
Your Python code may produce errors. It happens to everybody. It is hard to foresee all possible errors, but you can try to handle them in case anyway.
Use the Try
, Except
and optional finally
keywords to manage error handling.
In the example code, if no chair or table is returned, an Exception
error is raised and it includes a text string that can be read and written to the console.
Within your code you can use the raise
keyword to trigger Python built in exceptions or even create your own.
| def get_furniture(furniture):
"Static get_factory method"
try:
if furniture in ['SmallChair', 'MediumChair', 'BigChair']:
return ChairFactory().get_chair(furniture)
if furniture in ['SmallTable', 'MediumTable', 'BigTable']:
return TableFactory().get_table(furniture)
raise Exception('No Factory Found')
except Exception as _e:
print(_e)
return None
|
If WoodenTable
is requested from the factory, it will print No Factory Found
You don't need to always raise an exception to make one happen. In that case you can handle the possibility of any type of error using just try
and except
, with the optional finally
if you need it.
| try:
print(my_var)
except:
print("An unknown error Occurred")
finally:
print("This is optional and will get called even if there is no error")
|
The above code produces the message An Error Occurred
because my_var
is not defined.
The try/except
allows the program to continue running, as can be verified by the line printed in the finally
statement. So, this has given you the opportunity to manage any unforeseen errors any way you wish.
Alternatively, if your code didn't include the try/except
and optional finally
statements, the Python interpreter would return the error NameError: name 'my_var' is not defined
and the program will crash at that line.
Also note how the default Python inbuilt error starts with NameError
. You can handle this specific error explicitly using an extra except
keyword.
| try:
print(my_var)
except NameError:
print("There was a `NameError`")
except:
print("An unknown error Occurred")
finally:
print("This is optional and will get called even if there is no error")
|
You can add exception handling for as many types of errors as you wish.
Python Errors and Exceptions : https://docs.python.org/3/tutorial/errors.html
Summary
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.