Card 03/ 08

CodeDifficulty: Intermediate3 min

Flyweight Template in Python, Java and TypeScript

A factory that returns the same object for the same key, and a count of objects made.

The Flyweight template is the pattern in its pure form, with the participant names from the class diagram. Four placements of two kinds of tree are drawn through a factory, and the output counts the flyweights the factory made and checks that two requests for one key return the same object. The steps add each participant in Python, and the tabs give the complete program in three languages, each with its output.

Building the template one participant at a time

Flyweight

Holds the intrinsic state and takes the extrinsic state as an argument.

py
class Flyweight:
    def __init__(self, intrinsic: str) -> None:
        self._intrinsic = intrinsic

    def operation(self, extrinsic: str) -> str:
        return f"{self._intrinsic} at {extrinsic}"

Factory

Creates a flyweight for a key once, and returns the stored one after that.

py
class FlyweightFactory:
    def get(self, key: str) -> Flyweight:
        if key not in self._pool:
            self._pool[key] = Flyweight(key)
        return self._pool[key]

Client

Holds the extrinsic state, the positions, and passes it in.

py
for key, position in placements:
    print(factory.get(key).operation(position))

The complete template

py
class Flyweight:
    def __init__(self, intrinsic: str) -> None:
        self._intrinsic = intrinsic

    def operation(self, extrinsic: str) -> str:
        return f"{self._intrinsic} at {extrinsic}"


class FlyweightFactory:
    def __init__(self) -> None:
        self._pool: dict[str, Flyweight] = {}

    def get(self, key: str) -> Flyweight:
        if key not in self._pool:
            self._pool[key] = Flyweight(key)
        return self._pool[key]

    def count(self) -> int:
        return len(self._pool)


factory = FlyweightFactory()
placements = [("oak", "1,1"), ("pine", "2,5"), ("oak", "3,8"), ("oak", "9,2")]
for key, position in placements:
    print(factory.get(key).operation(position))

print(f"placements: {len(placements)}, flyweights: {factory.count()}")
print(f"same object: {factory.get('oak') is factory.get('oak')}")
OutputRan with python:3.12-slim (Python 3.12.13)
oak at 1,1
pine at 2,5
oak at 3,8
oak at 9,2
placements: 4, flyweights: 2
same object: True
The factory stores each flyweight under its key, so `oak` is created once.
java
import java.util.HashMap;
import java.util.Map;

public class FlTemplate {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        String[][] placements = {{"oak", "1,1"}, {"pine", "2,5"}, {"oak", "3,8"}, {"oak", "9,2"}};
        for (String[] placement : placements) {
            System.out.println(factory.get(placement[0]).operation(placement[1]));
        }
        System.out.println("placements: " + placements.length + ", flyweights: " + factory.count());
        System.out.println("same object: " + (factory.get("oak") == factory.get("oak")));
    }
}

final class Flyweight {
    private final String intrinsic;

    Flyweight(String intrinsic) {
        this.intrinsic = intrinsic;
    }

    String operation(String extrinsic) {
        return intrinsic + " at " + extrinsic;
    }
}

class FlyweightFactory {
    private final Map<String, Flyweight> pool = new HashMap<>();

    Flyweight get(String key) {
        return pool.computeIfAbsent(key, Flyweight::new);
    }

    int count() {
        return pool.size();
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
oak at 1,1
pine at 2,5
oak at 3,8
oak at 9,2
placements: 4, flyweights: 2
same object: true
`computeIfAbsent` creates the flyweight only when the key is missing.
ts
class Flyweight {
  constructor(private readonly intrinsic: string) {}

  operation(extrinsic: string): string {
    return `${this.intrinsic} at ${extrinsic}`;
  }
}

class FlyweightFactory {
  private readonly pool = new Map<string, Flyweight>();

  get(key: string): Flyweight {
    let flyweight = this.pool.get(key);
    if (flyweight === undefined) {
      flyweight = new Flyweight(key);
      this.pool.set(key, flyweight);
    }
    return flyweight;
  }

  count(): number {
    return this.pool.size;
  }
}

const factory = new FlyweightFactory();
const placements: [string, string][] = [["oak", "1,1"], ["pine", "2,5"], ["oak", "3,8"], ["oak", "9,2"]];
for (const [key, position] of placements) {
  console.log(factory.get(key).operation(position));
}

console.log(`placements: ${placements.length}, flyweights: ${factory.count()}`);
console.log(`same object: ${factory.get("oak") === factory.get("oak")}`);
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
oak at 1,1
pine at 2,5
oak at 3,8
oak at 9,2
placements: 4, flyweights: 2
same object: true
`===` on two objects is true only when they are the same object.

Four placements needed two flyweights, and the last line confirms that the factory returned one object for both requests for oak. The positions never entered a flyweight; they stayed in the client's list and were passed in. With a million placements of four species, the factory would still hold four objects.