What It Is
The Flyweight pattern saves memory when a program holds thousands of similar objects: the heavy shared part is stored once and referenced everywhere, while each object keeps only its small unique part.
A forest of 10,000 trees does not need 10,000 textures — it needs 2 tree kinds and 10,000 tiny (x, y, kind) records.
Example: A Forest of Shared Kinds
KindCache::get() hands out shared kind objects, creating each one only on first request. Every tree stores just a position and a reference to its kind.
Many trees, few kinds
Four trees, but "creating kind" prints only twice — oak and pine exist once each, no matter how large the forest grows. Objects in PHP are passed by handle, so every oak tree points at the very same TreeKind.
Output: PHP script
<?php
// The heavy shared part — created once per kind.
class TreeKind {
public function __construct(
public string $name,
public string $texture,
) {}
}
class KindCache {
/** @var array<string, TreeKind> */
private static array $kinds = [];
public static function get(string $name): TreeKind {
if (!isset(self::$kinds[$name])) {
echo "creating kind: ", $name, "\n";
self::$kinds[$name] = new TreeKind($name, $name . ".png");
}
return self::$kinds[$name];
}
public static function count(): int {
return count(self::$kinds);
}
}
// The light per-tree part: a position and a reference.
class Tree {
public function __construct(
public int $x,
public int $y,
public TreeKind $kind,
) {}
}
$forest = [
new Tree(1, 2, KindCache::get("oak")),
new Tree(3, 4, KindCache::get("pine")),
new Tree(5, 6, KindCache::get("oak")),
new Tree(7, 8, KindCache::get("pine")),
];
echo "trees planted: ", count($forest), "\n";
echo "kinds stored: ", KindCache::count(), "\n";
When to Use It
Use it when profiling shows memory pain from many objects that repeat heavy data — map tiles, glyphs, particles, cached styles.
Skip it below thousands of objects, or when the shared part is small. This is an optimization pattern: without the memory problem, it is only complexity.
Pros and Cons
Pros: big memory savings at scale; shared data updated in one place.
Cons: shared objects must be treated as read-only — one careless write changes "every oak in the forest"; the split into shared/unique parts complicates the model.