Flyweight
A forest of a million trees where every Tree holds its own copy of the heavy mesh and texture.
Split each object into a shared, unchanging part (TreeType) and a tiny personal part (position).
Huge numbers of similar objects exhaust memory
The picture from life
A printing press has one metal block for each letter, and uses the same block for every "e" on the page. It does not make a new "e" for every occurrence. The block (shared) is separate from where it is placed on the page (individual).
The problem
What goes wrong when you do not use it.
A forest of a million trees where every Tree holds its own copy of the heavy mesh and texture. Memory explodes even though there are only two kinds of tree.
- Huge numbers of similar objects exhaust memory
- Most of each object's data is the same across many
- The shared part never changes (immutable)
The idea
How the pattern fixes it.
Split each object into a shared, unchanging part (TreeType) and a tiny personal part (position). A factory keeps one TreeType per kind and hands out the same one every time.
- has a / holds
- creates
Who is who
- TreeType (flyweight)
- Shared, immutable: name, color, texture.
- TreeFactory
- Returns the existing TreeType for a kind or creates it once.
- Tree
- Holds only x, y and a reference to its TreeType.
Build it in steps
- 1Separate shared (intrinsic) from personal (extrinsic) data.
- 2Make the shared part immutable.
- 3Add a factory that caches by kind.
- 4Create objects through the factory.
Watch it run
Step through the calls. Tap an arrow to jump to it.
Planting the first tree asks the factory for its type.
get('oak', 'green')The code
Python and Java. Try writing the classes yourself from the diagram first.
class TreeType: # shared and immutable
def __init__(self, name, color):
self.name = name
self.color = color
class TreeFactory:
_types = {}
@classmethod
def get(cls, name, color):
key = (name, color)
if key not in cls._types: # create each kind only once
cls._types[key] = TreeType(name, color)
return cls._types[key]
class Tree: # only the personal part
def __init__(self, x, y, kind):
self.x, self.y, self.kind = x, y, kind
def demo():
forest = [Tree(n, n, TreeFactory.get("oak", "green" if n % 2 else "dark")) for n in range(1000)]
return len(forest), len(TreeFactory._types), forest[0].kind is forest[2].kindWhat you gain
- Huge memory savings
- Faster creation: shared parts exist already
What it costs
- More complex code: two kinds of data to track
- Shared state must never change
You have already met it in
- Java's String pool and Integer.valueOf cache for small numbers
- Game engines sharing meshes and sprites
- Text editors sharing one glyph per letter
Often mixed up with
Make it stick
Render 10,000 characters of text using one Glyph object per distinct letter. Print how many Glyph objects were created.