Skip to main content

Command Palette

Search for a command to run...

Flyweight Pattern

Updated
7 min read
Flyweight Pattern

The Flyweight Pattern is a structural design pattern that minimizes memory usage by sharing common parts of objects instead of storing them separately. It separates intrinsic state (shared, immutable data) from extrinsic state (context-specific data), allowing thousands or millions of objects to be represented efficiently.

Think of a text editor: every character “A” doesn’t need its own font, size, and style object. Instead, all “A” characters can share the same flyweight object, while position and color are supplied externally. This keeps memory usage low and performance high.

The Problem

Imagine building a game with thousands of trees on a map. If each tree object stores its own heavy data (texture, model, mesh), memory usage explodes.

// Without Flyweight - Memory heavy
class Tree {
    private String mesh;    // Huge data
    private String texture; // Huge data
    private int x, y;       // Position
    private int size;
}

// 100,000 trees = 100,000 meshes and textures 😱

This leads to excessive memory usage and slower performance.

The Solution: Flyweight Pattern

The Flyweight Pattern stores heavy shared data once and reuses it across many objects.

Instead of creating a full tree object for every single instance, you create one shared flyweight (e.g., the mesh + texture) and pass only the lightweight context (like x, y, size) when you draw it. This turns 100,000 heavyweight objects into 100,000 tiny references that all point to a small set of shared flyweights.

In short:

  • Shared data lives in flyweights (intrinsic state)

  • Per-object data stays outside (extrinsic state)

  • Memory usage drops dramatically while behavior stays the same

Key Components

  1. Flyweight: Interface for shared objects

  2. ConcreteFlyweight: Stores intrinsic (shared) state

  3. FlyweightFactory: Manages and reuses flyweights

  4. Client: Supplies extrinsic state at runtime

Intrinsic vs Extrinsic State

Real-World Implementation

Example 1: Forest Rendering

import java.util.HashMap;
import java.util.Map;

// Flyweight - TreeType (intrinsic state)
class TreeType {
    private String name;
    private String color;
    private String texture;

    public TreeType(String name, String color, String texture) {
        this.name = name;
        this.color = color;
        this.texture = texture;
    }

    public void draw(int x, int y, int size) {
        System.out.println("Drawing " + name + " tree at (" + x + "," + y + ")" +
                " size=" + size + " color=" + color + " texture=" + texture);
    }
}

// Flyweight Factory
class TreeFactory {
    private static Map<String, TreeType> treeTypes = new HashMap<>();

    public static TreeType getTreeType(String name, String color, String texture) {
        String key = name + "-" + color + "-" + texture;
        treeTypes.putIfAbsent(key, new TreeType(name, color, texture));
        return treeTypes.get(key);
    }
}

// Context - Tree (extrinsic state)
class Tree {
    private int x, y, size;
    private TreeType type;

    public Tree(int x, int y, int size, TreeType type) {
        this.x = x;
        this.y = y;
        this.size = size;
        this.type = type;
    }

    public void draw() {
        type.draw(x, y, size);
    }
}

// Client
public class ForestDemo {
    public static void main(String[] args) {
        TreeType pine = TreeFactory.getTreeType("Pine", "Green", "Rough");
        TreeType oak = TreeFactory.getTreeType("Oak", "Dark Green", "Smooth");

        Tree[] forest = {
            new Tree(10, 20, 5, pine),
            new Tree(15, 30, 6, pine),
            new Tree(50, 60, 8, oak),
            new Tree(70, 80, 7, pine)
        };

        for (Tree tree : forest) {
            tree.draw();
        }
    }
}

Example 2: Text Formatting

import java.util.HashMap;
import java.util.Map;

// Flyweight
class CharacterStyle {
    private String font;
    private int size;
    private boolean bold;

    public CharacterStyle(String font, int size, boolean bold) {
        this.font = font;
        this.size = size;
        this.bold = bold;
    }

    public void render(char c, int x, int y) {
        System.out.println("Rendering '" + c + "' at (" + x + "," + y + ")" +
                " with font=" + font + ", size=" + size + ", bold=" + bold);
    }
}

// Flyweight Factory
class StyleFactory {
    private static Map<String, CharacterStyle> styles = new HashMap<>();

    public static CharacterStyle getStyle(String font, int size, boolean bold) {
        String key = font + "-" + size + "-" + bold;
        styles.putIfAbsent(key, new CharacterStyle(font, size, bold));
        return styles.get(key);
    }
}

// Client
public class TextEditorDemo {
    public static void main(String[] args) {
        String text = "HELLO";
        CharacterStyle style = StyleFactory.getStyle("Arial", 12, true);

        for (int i = 0; i < text.length(); i++) {
            style.render(text.charAt(i), i * 10, 50);
        }
    }
}

Workflow Diagram

Real-World Use Cases

  • Text Rendering: Characters sharing font/style objects

  • Game Development: Trees, particles, bullets with shared assets

  • UI Components: Reusing icons, themes, and styles

  • Caching Systems: Shared instances for repeated data

  • Networking: Pooling frequently used protocol objects

When to Use the Flyweight Pattern

✅ Use When

  1. Huge Number of Objects: You create many similar objects

  2. Memory Constraints: RAM usage becomes a bottleneck

  3. Shared State: Many objects share common data

  4. Immutable Data: Intrinsic state doesn’t change

  5. Performance Optimization: Reduce memory footprint

❌ Avoid When

  1. Few Objects: No memory savings to gain

  2. State Changes Often: Intrinsic state is mutable

  3. Complex Extrinsic State: Passing context becomes messy

  4. Over-Engineering: Adds complexity without benefit

Benefits

  1. Memory Efficiency: Dramatic reduction in memory usage

  2. Performance: Faster creation and reduced GC pressure

  3. Scalability: Handles millions of objects gracefully

  4. Shared Resources: Reuse expensive assets

Drawbacks

  1. Complexity: Separating intrinsic/extrinsic state adds complexity

  2. Thread Safety: Shared objects must be immutable

  3. Context Passing: Clients must manage extrinsic state carefully

  4. Maintenance: Harder to debug shared state issues

Advanced Example: Particle System

import java.util.HashMap;
import java.util.Map;

// Flyweight
class ParticleType {
    private String sprite;
    private String color;

    public ParticleType(String sprite, String color) {
        this.sprite = sprite;
        this.color = color;
    }

    public void draw(int x, int y, int velocity) {
        System.out.println("Drawing particle " + sprite + " color=" + color +
                " at (" + x + "," + y + ") with velocity=" + velocity);
    }
}

// Factory
class ParticleFactory {
    private static Map<String, ParticleType> particles = new HashMap<>();

    public static ParticleType getParticle(String sprite, String color) {
        String key = sprite + "-" + color;
        particles.putIfAbsent(key, new ParticleType(sprite, color));
        return particles.get(key);
    }
}

// Client
public class ParticleSystemDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            ParticleType particle = ParticleFactory.getParticle("Spark", "Blue");
            particle.draw(i, i * 2, 10);
        }
    }
}

Flyweight vs Prototype

Best Practices

  1. Make Flyweights Immutable: Avoid shared state changes

  2. Use Factories: Always retrieve flyweights through a factory

  3. Cache Wisely: Use eviction if flyweights grow too many

  4. Document Extrinsic State: Clearly define what is shared vs external

  5. Measure Before/After: Validate memory savings

  6. Thread Safety: Ensure shared objects are safe for concurrent use

Conclusion

The Flyweight Pattern is your memory-saving powerhouse when handling massive numbers of similar objects. It keeps shared data centralized while letting each object maintain its own context, delivering high performance with low memory usage.

Remember: Share the heavy stuff, keep the light stuff outside! 🪶


🎯 Key Takeaway

The Flyweight Pattern is about sharing common data to save memory. When you have millions of similar objects, let the flyweights carry the heavy load.


Flyweight walked into a heap and said, “Relax, I’m just one instance.”
The GC replied, “One instance? You’re shared by 10,000 objects.”
The Flyweight smirked, “Exactly. I’m basically RAM’s influencer.🪶😄

Happy Optimizing! 🪶✨