Flyweight 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.
Flyweight UML Diagram

Source Code
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
./flyweight/flyweight_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
57 | # pylint: disable=too-few-public-methods
"The Flyweight Concept"
class IFlyweight():
"Nothing to implement"
class Flyweight(IFlyweight):
"The Concrete Flyweight"
def __init__(self, code: int) -> None:
self.code = code
class FlyweightFactory():
"Creating the FlyweightFactory as a singleton"
_flyweights: dict[int, Flyweight] = {} # Python 3.9
# _flyweights = {} # Python 3.8 or earlier
def __new__(cls):
return cls
@classmethod
def get_flyweight(cls, code: int) -> Flyweight:
"A static method to get a flyweight based on a code"
if not code in cls._flyweights:
cls._flyweights[code] = Flyweight(code)
return cls._flyweights[code]
@classmethod
def get_count(cls) -> int:
"Return the number of flyweights in the cache"
return len(cls._flyweights)
class Context():
"""
An example context that holds references to the flyweights in a
particular order and converts the code to an ascii letter
"""
def __init__(self, codes: str) -> None:
self.codes = list(codes)
def output(self):
"The context specific output that uses flyweights"
ret = ""
for code in self.codes:
ret = ret + FlyweightFactory.get_flyweight(code).code
return ret
# The Client
CONTEXT = Context("abracadabra")
# use flyweights in a context
print(CONTEXT.output())
print(f"abracadabra has {len('abracadabra')} letters")
print(f"FlyweightFactory has {FlyweightFactory.get_count()} flyweights")
|
Output
| python ./flyweight/flyweight_concept.py
abracadabra
abracadabra has 11 letters
FlyweightFactory has 5 flyweights
|
Example Use Case
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Example UML Diagram

Source Code
./flyweight/client.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 | "The Flyweight Use Case Example"
from table import Table
from flyweight_factory import FlyweightFactory
TABLE = Table(3, 3)
TABLE.rows[0].columns[0].data = "abra"
TABLE.rows[0].columns[1].data = "112233"
TABLE.rows[0].columns[2].data = "cadabra"
TABLE.rows[1].columns[0].data = "racadab"
TABLE.rows[1].columns[1].data = "12345"
TABLE.rows[1].columns[2].data = "332211"
TABLE.rows[2].columns[0].data = "cadabra"
TABLE.rows[2].columns[1].data = "445566"
TABLE.rows[2].columns[2].data = "aa 22 bb"
TABLE.rows[0].columns[0].justify = 1
TABLE.rows[1].columns[0].justify = 1
TABLE.rows[2].columns[0].justify = 1
TABLE.rows[0].columns[2].justify = 2
TABLE.rows[1].columns[2].justify = 2
TABLE.rows[2].columns[2].justify = 2
TABLE.rows[0].columns[1].width = 15
TABLE.rows[1].columns[1].width = 15
TABLE.rows[2].columns[1].width = 15
TABLE.draw()
print(f"FlyweightFactory has {FlyweightFactory.get_count()} flyweights")
|
./flyweight/flyweight.py
| "The Flyweight that contains an intrinsic value called code"
class Flyweight(): # pylint: disable=too-few-public-methods
"The Flyweight that contains an intrinsic value called code"
def __init__(self, code: int) -> None:
self.code = code
|
./flyweight/flyweight_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 | "Creating the FlyweightFactory as a singleton"
from flyweight import Flyweight
class FlyweightFactory():
"Creating the FlyweightFactory as a singleton"
_flyweights: dict[int, Flyweight] = {} # Python 3.9
# _flyweights = {} # Python 3.8 or earlier
def __new__(cls):
return cls
@classmethod
def get_flyweight(cls, code: int) -> Flyweight:
"A static method to get a flyweight based on a code"
if not code in cls._flyweights:
cls._flyweights[code] = Flyweight(code)
return cls._flyweights[code]
@classmethod
def get_count(cls) -> int:
"Return the number of flyweights in the cache"
return len(cls._flyweights)
|
./flyweight/column.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 | "A Column that is used in a Row"
from flyweight_factory import FlyweightFactory
class Column(): # pylint: disable=too-few-public-methods
"""
The columns are the contexts.
They will share the Flyweights via the FlyweightsFactory.
`data`, `width` and `justify` are extrinsic values. They are outside
of the flyweights.
"""
def __init__(self, data="", width=11, justify=0) -> None:
self.data = data
self.width = width
self.justify = justify # 0:center, 1:left, 2:right
def get_data(self):
"Get the flyweight value from the factory, and apply the extrinsic values"
ret = ""
for data in self.data:
ret = ret + FlyweightFactory.get_flyweight(data).code
ret = f"{ret.center(self.width)}" if self.justify == 0 else ret
ret = f"{ret.ljust(self.width)}" if self.justify == 1 else ret
ret = f"{ret.rjust(self.width)}" if self.justify == 2 else ret
return ret
|
./flyweight/row.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | "A Row in the Table"
from column import Column
class Row(): # pylint: disable=too-few-public-methods
"A Row in the Table"
def __init__(self, column_count: int) -> None:
self.columns = []
for _ in range(column_count):
self.columns.append(Column())
def get_data(self):
"Format the row before returning it to the table"
ret = ""
for column in self.columns:
ret = f"{ret}{column.get_data()}|"
return ret
|
./flyweight/table.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 | "A Formatted Table that includes rows and columns"
from row import Row
class Table(): # pylint: disable=too-few-public-methods
"A Formatted Table"
def __init__(self, row_count: int, column_count: int) -> None:
self.rows = []
for _ in range(row_count):
self.rows.append(Row(column_count))
def draw(self):
"Draws the table formatted in the console"
max_row_length = 0
rows = []
for row in self.rows:
row_data = row.get_data()
rows.append(f"|{row_data}")
row_length = len(row_data) + 1
if max_row_length < row_length:
max_row_length = row_length
print("-" * max_row_length)
for row in rows:
print(row)
print("-" * max_row_length)
|
Output
| python ./flyweight/client.py
-----------------------------------------
|abra | 112233 | cadabra|
|racadab | 12345 | 332211|
|cadabra | 445566 | aa 22 bb|
-----------------------------------------
FlyweightFactory has 12 flyweights
|
New Coding Concepts
String Justification
In ./flyweight/column.py, there are commands center()
, ljust()
and rjust()
.
These are special commands on strings that allow you to pad strings and align them left, right, center depending on total string length.
eg,
| >>> "abcd".center(10)
' abcd '
|
| >>> "abcd".rjust(10)
' abcd'
|
| >>> "abcd".ljust(10)
'abcd '
|
Summary
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.