Flyweight Pattern
debt(d7/e5/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list php-meminfo and blackfire as tools, both specialist profiling tools requiring deliberate instrumentation and measurement. The automated flag is 'no', meaning no linter or static analysis catches misuse automatically. Memory bloat from missing flyweight only manifests at scale (thousands of objects), making it invisible in normal development and only apparent under profiling or when users hit OOM errors in production.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says to share identical immutable objects from a factory instead of creating new ones. This requires introducing a factory class, making flyweight objects immutable, identifying all creation sites, and threading the factory through client code — not a one-liner but also not a full architectural rework. Common mistakes (mutable flyweights, missing factory, extrinsic state leakage) suggest several interrelated changes across a component.
Closest to 'persistent productivity tax' (b5). The pattern applies to web, cli, and queue-worker contexts, and once introduced, every future maintainer must understand the intrinsic/extrinsic state split, the factory contract, and the immutability invariant. Flyweight imposes an ongoing cognitive tax on all code touching the shared objects, though it doesn't reshape the entire system's architecture.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field states developers believe flyweight is only for extremely large systems, missing that thousands of repeated UI components in ordinary PHP apps qualify. Common mistakes reinforce multiple traps: storing extrinsic state in the flyweight (defeats sharing), mutating shared objects (corrupts all contexts), and skipping the factory (creates duplicates). These are documented gotchas that are non-obvious but learnable, not catastrophic inversions of intuition.
Also Known As
TL;DR
Explanation
Flyweight separates intrinsic state (shared, immutable — stored in the flyweight) from extrinsic state (unique, context-dependent — passed by the client). A flyweight factory ensures each unique intrinsic state is only instantiated once. Classic example: a text editor storing character formatting — rather than one object per character with its font stored on each, share font objects across all characters using the same font. PHP applications: caching expensive configuration objects, connection pools, icon/resource caches.
Common Misconception
Why It Matters
Common Mistakes
- Storing extrinsic (context-specific) state in the flyweight — defeats the purpose by making it non-shareable.
- Flyweight objects that are mutable — shared objects must be immutable; mutation in one context corrupts all others.
- Not using a factory — without a factory, client code may create duplicates instead of sharing.
- Premature flyweight optimisation — measure memory usage first; flyweight adds complexity only justified by real memory pressure.
Code Examples
// One object per row — wasteful if formatter is identical:
class TableRow {
private NumberFormatter $formatter;
public function __construct(private array $data) {
$this->formatter = new NumberFormatter('en-US', NumberFormatter::CURRENCY);
// New formatter for every row — 10,000 rows = 10,000 identical formatters
}
}
// Flyweight — shared formatter:
class FormatterFactory {
private static array $cache = [];
public static function get(string $locale, int $style): NumberFormatter {
$key = $locale . ':' . $style;
return self::$cache[$key] ??= new NumberFormatter($locale, $style);
}
}
class TableRow {
public function __construct(private array $data) {}
public function formatAmount(): string {
// Shared — created once, reused by all rows:
return FormatterFactory::get('en-US', NumberFormatter::CURRENCY)
->formatCurrency($this->data['amount'], 'USD');
}
}