# Flyweight Pattern

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

## Problem

A text editor renders thousands of characters. Each character could carry font, size, and color data. Storing this per character wastes enormous memory when most characters share the same styling.

## Solution

Extract the shared *intrinsic* state (font, size, color) into flyweight objects stored in a factory. Each character in the document only stores its *extrinsic* state (position, the character itself) and a reference to its flyweight style.

```
┌────────────────────┐        ┌──────────────────────┐
│  CharacterStyle    │        │  StyleFactory         │
│  (flyweight)       │        │  + get_style(font,    │
│  - font            │◄───────│      size, color)     │
│  - size            │        │  - cache: map         │
│  - color           │        └──────────────────────┘
│  + render(char, x, y)│
└────────────────────┘

Extrinsic state (char, x, y) passed in at render time.
```

## When to Use

- Your application creates a huge number of objects that share significant common state.
- Memory consumption is a concern and most object state can be made extrinsic.
- The shared objects are immutable (or effectively immutable once created).

## When to Avoid

- You only have a small number of objects — the overhead of the factory isn't worth it.
- Objects don't share enough common state to benefit from sharing.
- Making state extrinsic complicates the code more than the memory savings justify.

## Pseudocode

```
class CharacterStyle:
    field font, size, color   // intrinsic (shared)

    constructor(font, size, color):
        this.font = font
        this.size = size
        this.color = color

    method render(char, x, y):
        output char + " at (" + x + "," + y + ") [" + font + " " + size + "pt " + color + "]"

class StyleFactory:
    field cache: map<string, CharacterStyle>

    method get_style(font, size, color):
        key = font + "-" + size + "-" + color
        if key not in cache:
            cache[key] = new CharacterStyle(font, size, color)
        return cache[key]

    method count(): return len(cache)

// Usage
factory = new StyleFactory()
document = [
    (factory.get_style("Arial", 12, "black"), "H", 0, 0),
    (factory.get_style("Arial", 12, "black"), "e", 1, 0),
    (factory.get_style("Arial", 12, "red"),   "l", 2, 0),
    (factory.get_style("Arial", 12, "black"), "l", 3, 0),
    (factory.get_style("Arial", 12, "black"), "o", 4, 0),
]
for (style, char, x, y) in document:
    style.render(char, x, y)
print "Unique styles: " + factory.count()
```

## Python

```python
class CharacterStyle:
    """Flyweight: stores intrinsic state (font, size, color)."""

    __slots__ = ("font", "size", "color")

    def __init__(self, font: str, size: int, color: str) -> None:
        self.font = font
        self.size = size
        self.color = color

    def render(self, char: str, x: int, y: int) -> None:
        print(f"  '{char}' at ({x},{y}) [{self.font} {self.size}pt {self.color}]")


class StyleFactory:
    """Creates and caches flyweight CharacterStyle objects."""

    def __init__(self) -> None:
        self._cache: dict[str, CharacterStyle] = {}

    def get_style(self, font: str, size: int, color: str) -> CharacterStyle:
        key = f"{font}-{size}-{color}"
        if key not in self._cache:
            self._cache[key] = CharacterStyle(font, size, color)
            print(f"  [Factory] Created new style: {key}")
        return self._cache[key]

    @property
    def count(self) -> int:
        return len(self._cache)


if __name__ == "__main__":
    factory = StyleFactory()

    # Simulate a document with characters and positions
    document_chars = [
        ("H", 0, 0, "Arial", 12, "black"),
        ("e", 1, 0, "Arial", 12, "black"),
        ("l", 2, 0, "Arial", 12, "red"),
        ("l", 3, 0, "Arial", 12, "black"),
        ("o", 4, 0, "Arial", 12, "black"),
        (" ", 5, 0, "Arial", 12, "black"),
        ("W", 6, 0, "Arial", 16, "blue"),
        ("o", 7, 0, "Arial", 16, "blue"),
        ("r", 8, 0, "Arial", 16, "blue"),
        ("l", 9, 0, "Arial", 12, "red"),
        ("d", 10, 0, "Arial", 12, "black"),
    ]

    print("--- Building styles ---")
    rendered: list[tuple[CharacterStyle, str, int, int]] = []
    for char, x, y, font, size, color in document_chars:
        style = factory.get_style(font, size, color)
        rendered.append((style, char, x, y))

    print(f"\n--- Rendering ({len(rendered)} chars, {factory.count} unique styles) ---")
    for style, char, x, y in rendered:
        style.render(char, x, y)

    print(f"\nTotal characters: {len(rendered)}")
    print(f"Unique style objects: {factory.count}")
    print(f"Memory saved: {len(rendered) - factory.count} duplicate style objects avoided")
```

**Output:**
```
--- Building styles ---
  [Factory] Created new style: Arial-12-black
  [Factory] Created new style: Arial-12-red
  [Factory] Created new style: Arial-16-blue

--- Rendering (11 chars, 3 unique styles) ---
  'H' at (0,0) [Arial 12pt black]
  'e' at (1,0) [Arial 12pt black]
  'l' at (2,0) [Arial 12pt red]
  'l' at (3,0) [Arial 12pt black]
  'o' at (4,0) [Arial 12pt black]
  ' ' at (5,0) [Arial 12pt black]
  'W' at (6,0) [Arial 16pt blue]
  'o' at (7,0) [Arial 16pt blue]
  'r' at (8,0) [Arial 16pt blue]
  'l' at (9,0) [Arial 12pt red]
  'd' at (10,0) [Arial 12pt black]

Total characters: 11
Unique style objects: 3
Memory saved: 8 duplicate style objects avoided
```

## Go

```go
package main

import "fmt"

// CharacterStyle is the flyweight (intrinsic state).
type CharacterStyle struct {
	Font  string
	Size  int
	Color string
}

func (cs *CharacterStyle) Render(char string, x, y int) {
	fmt.Printf("  '%s' at (%d,%d) [%s %dpt %s]\n", char, x, y, cs.Font, cs.Size, cs.Color)
}

// StyleFactory manages flyweight creation and caching.
type StyleFactory struct {
	cache map[string]*CharacterStyle
}

func NewStyleFactory() *StyleFactory {
	return &StyleFactory{cache: make(map[string]*CharacterStyle)}
}

func (f *StyleFactory) GetStyle(font string, size int, color string) *CharacterStyle {
	key := fmt.Sprintf("%s-%d-%s", font, size, color)
	if style, ok := f.cache[key]; ok {
		return style
	}
	fmt.Printf("  [Factory] Created new style: %s\n", key)
	style := &CharacterStyle{Font: font, Size: size, Color: color}
	f.cache[key] = style
	return style
}

func (f *StyleFactory) Count() int {
	return len(f.cache)
}

type docChar struct {
	char  string
	x, y  int
	font  string
	size  int
	color string
}

func main() {
	factory := NewStyleFactory()

	document := []docChar{
		{"H", 0, 0, "Arial", 12, "black"},
		{"e", 1, 0, "Arial", 12, "black"},
		{"l", 2, 0, "Arial", 12, "red"},
		{"l", 3, 0, "Arial", 12, "black"},
		{"o", 4, 0, "Arial", 12, "black"},
		{" ", 5, 0, "Arial", 12, "black"},
		{"W", 6, 0, "Arial", 16, "blue"},
		{"o", 7, 0, "Arial", 16, "blue"},
		{"r", 8, 0, "Arial", 16, "blue"},
		{"l", 9, 0, "Arial", 12, "red"},
		{"d", 10, 0, "Arial", 12, "black"},
	}

	type rendered struct {
		style *CharacterStyle
		char  string
		x, y  int
	}

	fmt.Println("--- Building styles ---")
	var chars []rendered
	for _, dc := range document {
		style := factory.GetStyle(dc.font, dc.size, dc.color)
		chars = append(chars, rendered{style, dc.char, dc.x, dc.y})
	}

	fmt.Printf("\n--- Rendering (%d chars, %d unique styles) ---\n", len(chars), factory.Count())
	for _, c := range chars {
		c.style.Render(c.char, c.x, c.y)
	}

	fmt.Printf("\nTotal characters: %d\n", len(chars))
	fmt.Printf("Unique style objects: %d\n", factory.Count())
	fmt.Printf("Memory saved: %d duplicate style objects avoided\n", len(chars)-factory.Count())
}
```

**Output:**
```
--- Building styles ---
  [Factory] Created new style: Arial-12-black
  [Factory] Created new style: Arial-12-red
  [Factory] Created new style: Arial-16-blue

--- Rendering (11 chars, 3 unique styles) ---
  'H' at (0,0) [Arial 12pt black]
  'e' at (1,0) [Arial 12pt black]
  'l' at (2,0) [Arial 12pt red]
  'l' at (3,0) [Arial 12pt black]
  'o' at (4,0) [Arial 12pt black]
  ' ' at (5,0) [Arial 12pt black]
  'W' at (6,0) [Arial 16pt blue]
  'o' at (7,0) [Arial 16pt blue]
  'r' at (8,0) [Arial 16pt blue]
  'l' at (9,0) [Arial 12pt red]
  'd' at (10,0) [Arial 12pt black]

Total characters: 11
Unique style objects: 3
Memory saved: 8 duplicate style objects avoided
```

## JavaScript

```javascript
class CharacterStyle {
  constructor(font, size, color) {
    this.font = font;
    this.size = size;
    this.color = color;
  }

  render(char, x, y) {
    console.log(
      `  '${char}' at (${x},${y}) [${this.font} ${this.size}pt ${this.color}]`
    );
  }
}

class StyleFactory {
  constructor() {
    this._cache = new Map();
  }

  getStyle(font, size, color) {
    const key = `${font}-${size}-${color}`;
    if (!this._cache.has(key)) {
      console.log(`  [Factory] Created new style: ${key}`);
      this._cache.set(key, new CharacterStyle(font, size, color));
    }
    return this._cache.get(key);
  }

  get count() {
    return this._cache.size;
  }
}

// --- Main ---
const factory = new StyleFactory();

const document = [
  ["H", 0, 0, "Arial", 12, "black"],
  ["e", 1, 0, "Arial", 12, "black"],
  ["l", 2, 0, "Arial", 12, "red"],
  ["l", 3, 0, "Arial", 12, "black"],
  ["o", 4, 0, "Arial", 12, "black"],
  [" ", 5, 0, "Arial", 12, "black"],
  ["W", 6, 0, "Arial", 16, "blue"],
  ["o", 7, 0, "Arial", 16, "blue"],
  ["r", 8, 0, "Arial", 16, "blue"],
  ["l", 9, 0, "Arial", 12, "red"],
  ["d", 10, 0, "Arial", 12, "black"],
];

console.log("--- Building styles ---");
const rendered = document.map(([char, x, y, font, size, color]) => ({
  style: factory.getStyle(font, size, color),
  char,
  x,
  y,
}));

console.log(
  `\n--- Rendering (${rendered.length} chars, ${factory.count} unique styles) ---`
);
for (const { style, char, x, y } of rendered) {
  style.render(char, x, y);
}

console.log(`\nTotal characters: ${rendered.length}`);
console.log(`Unique style objects: ${factory.count}`);
console.log(
  `Memory saved: ${rendered.length - factory.count} duplicate style objects avoided`
);
```

**Output:**
```
--- Building styles ---
  [Factory] Created new style: Arial-12-black
  [Factory] Created new style: Arial-12-red
  [Factory] Created new style: Arial-16-blue

--- Rendering (11 chars, 3 unique styles) ---
  'H' at (0,0) [Arial 12pt black]
  'e' at (1,0) [Arial 12pt black]
  'l' at (2,0) [Arial 12pt red]
  'l' at (3,0) [Arial 12pt black]
  'o' at (4,0) [Arial 12pt black]
  ' ' at (5,0) [Arial 12pt black]
  'W' at (6,0) [Arial 16pt blue]
  'o' at (7,0) [Arial 16pt blue]
  'r' at (8,0) [Arial 16pt blue]
  'l' at (9,0) [Arial 12pt red]
  'd' at (10,0) [Arial 12pt black]

Total characters: 11
Unique style objects: 3
Memory saved: 8 duplicate style objects avoided
```

## Related Patterns

- **Composite** — flyweight is often applied to leaf nodes in composite trees to reduce memory when many identical leaves exist.
- **Object Pool** — object pool recycles expensive mutable objects; flyweight shares immutable intrinsic state.
- **Prototype** — prototype creates new objects by cloning; flyweight avoids creating new objects by sharing existing ones.
- **Factory** — a flyweight factory manages the cache of shared flyweight instances.
