Back to Overview

Flyweight Pattern

The Flyweight Pattern is a structural design pattern that focuses on minimizing memory usage by sharing as much as possible with similar objects. It's particularly useful when you need to create a large number of similar objects that would otherwise consume a significant amount of memory.

The pattern achieves efficiency by separating an object's intrinsic (shared) state from its extrinsic (unique) state, allowing the sharing of common parts among multiple objects.

Problem

Imagine you're developing a forest visualization for a game, where you need to render thousands of tree objects. Each tree requires memory for its model, textures, and position data. Creating individual objects for each tree would consume an enormous amount of memory, making the application slow and inefficient.

Client FlyweightFactory Shared State (Intrinsic) Flyweight A Flyweight B Context Unique State (Extrinsic) Context 1 Context 2 Shared Tree Data (mesh, textures) Tree Instance 1 (position, scale) Tree Instance 2 (position, scale)

Components

Implementation Example

Let's implement a forest rendering system using the Flyweight pattern:

// Flyweight: TreeType class stores shared state
class TreeType {
    constructor(name, color, texture) {
        this.name = name;
        this.color = color;
        this.texture = texture;
    }
    
    render(canvas, x, y, age) {
        // Render a tree of this type at the specified position
        console.log(`Rendering ${this.name} tree at (${x},${y}) with age ${age}`);
        // Draw using the shared properties (color, texture) and unique properties (position, age)
    }
}

// Flyweight Factory: TreeFactory manages flyweight objects
class TreeFactory {
    constructor() {
        this.treeTypes = {};
    }
    
    getTreeType(name, color, texture) {
        // Get or create a flyweight tree type
        const key = `${name}-${color}-${texture}`;
        
        if (!this.treeTypes[key]) {
            this.treeTypes[key] = new TreeType(name, color, texture);
            console.log(`Created new tree type: ${key}`);
        }
        
        return this.treeTypes[key];
    }
    
    getTreeTypesCount() {
        return Object.keys(this.treeTypes).length;
    }
}

// Context: Tree class contains extrinsic state
class Tree {
    constructor(x, y, age, treeType) {
        this.x = x;
        this.y = y;
        this.age = age;
        this.treeType = treeType;
    }
    
    render(canvas) {
        this.treeType.render(canvas, this.x, this.y, this.age);
    }
}

// Client: Forest manages the trees
class Forest {
    constructor() {
        this.trees = [];
        this.treeFactory = new TreeFactory();
    }
    
    plantTree(x, y, age, name, color, texture) {
        const treeType = this.treeFactory.getTreeType(name, color, texture);
        const tree = new Tree(x, y, age, treeType);
        this.trees.push(tree);
        return tree;
    }
    
    render(canvas) {
        this.trees.forEach(tree => tree.render(canvas));
    }
    
    getStats() {
        return {
            totalTrees: this.trees.length,
            uniqueTreeTypes: this.treeFactory.getTreeTypesCount(),
            memorySaved: this.trees.length - this.treeFactory.getTreeTypesCount()
        };
    }
}

Interactive Demo: Forest Renderer

This demo shows how the Flyweight pattern can be used to efficiently render thousands of trees by sharing common tree properties.

0 Total Trees
0 Unique Tree Types
0 Objects Saved
0% Memory Savings
// Initialization information will appear here

When to Use

Benefits

Real-World Uses