Card 04/ 08
Flyweight Example: Characters in a Text Editor
Each glyph's font and shape is shared; its position on the page is passed in.
The characters of a text editor are a natural Flyweight, and this example maps the template onto it. A document holds tens of thousands of characters, but only a few dozen distinct letters in a few fonts. Each distinct combination of character, font and size becomes one shared glyph, the drawable form of a character, and the page records where each one is drawn.
| Field | Kind | Why |
|---|---|---|
| Character code | Intrinsic | Every t in the same font is drawn the same way |
| Font and size | Intrinsic | Shared by every character in the same run of text |
| Row and column | Extrinsic | Different for every character on the page |
| Template | Editor example |
|---|---|
| Flyweight | Glyph, a frozen dataclass of character, font and size |
| FlyweightFactory | GlyphFactory, keyed by all three |
| Client | The page, a list of glyph, row and column |
from dataclasses import dataclass
@dataclass(frozen=True)
class Glyph:
char: str
font: str
size: int
def draw(self, row: int, column: int) -> str:
return f"{self.char!r} in {self.font} {self.size} at {row}:{column}"
class GlyphFactory:
def __init__(self) -> None:
self._glyphs: dict[tuple[str, str, int], Glyph] = {}
def get(self, char: str, font: str, size: int) -> Glyph:
key = (char, font, size)
if key not in self._glyphs:
self._glyphs[key] = Glyph(char, font, size)
return self._glyphs[key]
def count(self) -> int:
return len(self._glyphs)
factory = GlyphFactory()
text = "the cat sat on the mat " * 2000
page = [(factory.get(char, "Georgia", 12), 1 + i // 80, 1 + i % 80) for i, char in enumerate(text)]
for glyph, row, column in page[:3]:
print(glyph.draw(row, column))
print(f"characters on the page: {len(page)}")
print(f"glyph objects: {factory.count()}")
print(f"the t in \"the\" and the t in \"cat\" are one object: {page[0][0] is page[6][0]}")'t' in Georgia 12 at 1:1
'h' in Georgia 12 at 1:2
'e' in Georgia 12 at 1:3
characters on the page: 46000
glyph objects: 10
the t in "the" and the t in "cat" are one object: TrueThe text is one short sentence repeated 2,000 times: 46,000 characters, drawn from ten distinct characters, including the space. The factory made ten glyph objects, and every t on the page refers to the same one. frozen=True makes each glyph immutable, so no code can change the font of one character and with it every other.
Changing the font of a word means asking the factory for glyphs in the new font, not editing the glyphs already shared. Real editors and browsers use the same idea for their glyph caches.
← → move through sections and cards