Home/Learn/Low Level Design/Flyweight Pattern

Flyweight Pattern

Advanced
Structural Patterns

Uses sharing to support large numbers of fine-grained objects efficiently by separating intrinsic (shared) state from extrinsic (context-specific) state.

Overview

Flyweight is a memory-optimization pattern for systems that instantiate massive numbers of similar objects. The Flyweight stores only intrinsic state — data that is the same across all instances of the same type (e.g. character glyph shape, chess piece appearance). Extrinsic state — position, color, owner — is passed in by the context at runtime. A FlyweightFactory maintains a pool/cache of shared Flyweight instances. Java's String pool is a Flyweight implementation. Integer.valueOf(-128..127) caches and reuses Integer objects. The pattern trades computation (passing extrinsic state each call) for memory.

Flyweight Implementation

Separate the shared intrinsic state into the Flyweight class. Pass extrinsic state as method parameters. Use a FlyweightFactory with a HashMap cache to reuse instances.

Java — Flyweight (CharacterGlyph + GlyphFactory)
import java.util.HashMap;
import java.util.Map;

// Flyweight — stores INTRINSIC state only (shared, immutable)
public final class CharacterGlyph {
    private final char character;     // intrinsic: the glyph shape
    private final String fontFamily;  // intrinsic: font (shared per char+font combo)
    private final int fontSize;

    public CharacterGlyph(char character, String fontFamily, int fontSize) {
        this.character  = character;
        this.fontFamily = fontFamily;
        this.fontSize   = fontSize;
        System.out.println("Creating new glyph for: '" + character + "' " + fontFamily);
    }

    // Extrinsic state (position, color) passed at render time
    public void render(int x, int y, String color) {
        System.out.printf("Rendering '%c' at (%d,%d) color=%s font=%s%n",
            character, x, y, color, fontFamily);
    }
}

// FlyweightFactory — cache shared instances
public class GlyphFactory {
    private static final Map<String, CharacterGlyph> CACHE = new HashMap<>();

    public static CharacterGlyph getGlyph(char c, String font, int size) {
        String key = c + "-" + font + "-" + size;
        return CACHE.computeIfAbsent(key, k -> new CharacterGlyph(c, font, size));
    }

    public static int cachedCount() { return CACHE.size(); }
}

// Context — holds extrinsic state + reference to shared Flyweight
public class CharacterContext {
    private final CharacterGlyph glyph; // shared flyweight
    private final int x, y;             // extrinsic: position
    private final String color;         // extrinsic: color

    public CharacterContext(char c, String font, int size, int x, int y, String color) {
        this.glyph = GlyphFactory.getGlyph(c, font, size); // shared instance
        this.x = x; this.y = y; this.color = color;
    }

    public void render() { glyph.render(x, y, color); }
}

// Rendering a document with 1000 'A' characters — only ONE CharacterGlyph created
List<CharacterContext> document = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    document.add(new CharacterContext('A', "Arial", 12, i * 10, 0, "black"));
}
document.forEach(CharacterContext::render);
System.out.println("Glyphs in cache: " + GlyphFactory.cachedCount()); // 1, not 1000

Java String Pool & Integer Cache

Java's String interning and Integer cache are built-in Flyweight implementations. String literals are automatically interned (stored in the string pool); Integer.valueOf() returns cached instances for values -128 to 127.

Java — String pool, Integer cache, Chess Flyweight
// String pool — Flyweight for String literals
String s1 = "hello";  // stored in string pool
String s2 = "hello";  // reuses same object from pool
System.out.println(s1 == s2);          // true — same reference
System.out.println(s1 == new String("hello")); // false — new object on heap

// Manual interning
String s3 = new String("hello").intern(); // move to pool
System.out.println(s1 == s3);            // true

// Integer cache (-128 to 127)
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
System.out.println(a == b); // true — cached

Integer c = Integer.valueOf(128);
Integer d = Integer.valueOf(128);
System.out.println(c == d); // false — new objects

// Practical Flyweight: chess board
// 32 pieces on a 64-square board
// PieceType (intrinsic: appearance, point value) — 6 flyweights max
// PiecePosition (extrinsic: row, col, color, alive) — per piece instance
enum PieceType { PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING }

public record ChessPiece(PieceType type, int row, int col, boolean isWhite) {
    // row, col, isWhite are extrinsic; type is the flyweight key
}

Key Points to Remember

  • 1Intrinsic state is shared and immutable — stored in the Flyweight.
  • 2Extrinsic state is context-dependent — passed as parameters at call time.
  • 3FlyweightFactory caches instances by key — computeIfAbsent() for thread-safe lazy creation.
  • 4Java String pool and Integer.valueOf(-128..127) cache are canonical Flyweight examples.
  • 5Flyweight reduces memory; it increases code complexity — only use when profiling proves memory pressure.

Interview Questions

Sign in to ask Aria
1

What is the difference between intrinsic and extrinsic state in Flyweight?

MediumAmazon
2

Why does Integer.valueOf(127) == Integer.valueOf(127) return true but 128 does not?

MediumGoogle
3

How is the Java String pool an example of Flyweight pattern?

EasyMicrosoft
4

Design a Flyweight for a game with 10,000 identical enemy soldiers.

HardAdobe

Ask Aria about Flyweight Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…