Flyweight Pattern
The Flyweight is a structural design pattern that minimizes memory usage by sharing common state (intrinsic) among similar objects, while keeping unique state (extrinsic) separate. It enables efficient handling of large numbers of lightweight objects by reusing immutable shared components, ideal for resource-constrained systems.
Key Principles
- Intrinsic State: Shared, immutable data (e.g., color, texture) stored in flyweights.
- Extrinsic State: Unique, context-specific data (e.g., position) passed as parameters or held externally.
- Factory Management: Central registry creates/reuses flyweights based on intrinsic state.
- Immutability: Flyweights must be read-only to ensure safe sharing.
Structure
- Flyweight: Interface for shared objects; methods take extrinsic state as input.
- Concrete Flyweight: Implements flyweight with intrinsic state.
- Flyweight Factory: Pool of flyweights; returns existing or new based on key.
- Client/Context: Manages extrinsic state and references flyweights.
Python Example: Tree Rendering System
import copy
# Flyweight Interface (shared intrinsic state)
class TreeType:
def __init__(self, name, color, texture):
self.name = name
self.color = color
self.texture = texture
def draw(self, canvas, x, y): # Extrinsic state as params
print(f"Drawing {self.name} at ({x}, {y}) with color {self.color} and texture {self.texture}")
# Flyweight Factory (manages pool)
class TreeFactory:
_tree_types = {}
@classmethod
def get_tree_type(cls, name, color, texture):
key = (name, color, texture)
if key not in cls._tree_types:
cls._tree_types[key] = TreeType(name, color, texture)
print(f"Created new TreeType: {name}")
return cls._tree_types[key]
# Context (holds extrinsic state + flyweight ref)
class Tree:
def __init__(self, x, y, tree_type):
self.x = x
self.y = y
self.tree_type = tree_type
def draw(self, canvas):
self.tree_type.draw(canvas, self.x, self.y)
# Client
class Forest:
def __init__(self):
self.trees = []
def plant_tree(self, x, y, name, color, texture):
tree_type = TreeFactory.get_tree_type(name, color, texture) # Reuse if exists
tree = Tree(x, y, tree_type)
self.trees.append(tree)
def draw(self, canvas):
for tree in self.trees:
tree.draw(canvas)
# Usage
if __name__ == "__main__":
forest = Forest()
forest.plant_tree(10, 20, "Oak", "Green", "Bark") # Creates new
forest.plant_tree(30, 40, "Oak", "Green", "Bark") # Reuses
forest.plant_tree(50, 60, "Pine", "Dark Green", "Needles") # Creates new
forest.draw(None)
Output:
Created new TreeType: Oak
Drawing Oak at (10, 20) with color Green and texture Bark
Drawing Oak at (30, 40) with color Green and texture Bark
Created new TreeType: Pine
Drawing Pine at (50, 60) with color Dark Green and texture Needles
When to Use Flyweight
- Memory-Intensive Objects: Huge numbers with duplicated state (e.g., game particles, UI icons).
- Shared Intrinsic Data: Varies little vs. unique extrinsic.
- Avoid: If state is mostly unique or objects mutable.
Summary: Flyweight shares immutable common state across objects to save memory, using a factory for reuse, while extrinsic state is managed externally.