Also known as Cache

Flyweight

Intent

Fit more objects into the available amount of RAM by sharing common parts of object state among multiple objects, instead of keeping it in each object.

Problem

To have some fun after long working hours, you decided to create a simple video game. Players will be moving around the map and shoot each other. You decided to implement a realistic particle system and make it a distinctive feature of the game. Bullets, missiles, and shrapnel from explosions should fly over in vast quantities and deliver a thrilling experience to a player.

After a while, you pushed the last commit and sent the game to your friends, hoping to play it right away. Though the game was running flawlessly on your machine, your friend was not able to play. The game kept crashing soon after launching on his computer.

After few hours of digging in logs, you have discovered that the game crashes because of insufficient RAM. And it looks that problem is related to your particle system.

Each particle is represented by an object that contains plenty of data. At some point, when the carnage on screen reaches its climax, newly created particles no longer fit into remaining RAM, and the program crashes.

Flyweight pattern problem

Solution

On a closer inspection of the Particle class, you may notice that color and sprite data consumes the most memory in each object. And worse, these fields store almost identical data across all particles. For example, all bullets have the same color and texture.

Flyweight pattern solution

The rest of particle data, such as coordinates, movement vector and speed are unique among all particles. Moreover, the values of these fields change over time. They look like a changeable context in which particle exists, while color and sprite remain constant.

Such variable context-specific object data is usually called "extrinsic state," because it's changed from the outside of the object. The rest of object state, the unchangeable data, is called "intrinsic state."

The Flyweight pattern suggests to stop storing the extrinsic state inside object fields, but rather pass it directly to methods that use it. This way you leave only unchangeable state inside an object, which allows you to reuse it in different contexts. And most importantly, you would need much less of these objects, since they only differ in intrinsic state, which does not vary that much.

For our game, only three particle objects would suffice (bullet, missile, and shrapnel). As you have probably guessed by now, such stripped objects are called flyweights.

Flyweight pattern solution

Extrinsic state storage

But where to does the extrinsic state move? Some classes should keep it, right? In most cases, it's convenient to move this data to the container, which used to aggregate objects before we applied the pattern.

In our case, that would the main game object. You could have created extra array-type fields for storing coordinates, vectors, and speeds. Additionally, you would require another array for storing references to specific flyweight objects that represent a particle.

But the more elegant solution would be to create a separate context class that would link extrinsic state with a particular flyweight object. Since context class would store all fields of extrinsic state, the container class would require just one array field for storing references to context objects.

Wait for a second! Won't we need to have the same amount of these contextual objects as we had at the very beginning? Technically, yes. But the thing is, these objects are much smaller than before. The most memory consuming fields are now living in just several flyweight objects. Thousands of contextual objects can link and reuse a single flyweight object instead of storing its duplicate state inside.

Flyweight pattern solution

Immutability

Since the same flyweight objects will be used in different contexts, you have to make sure that their state can't be modified. Flyweights should receive their intrinsic state only through constructor's parameters. They should not expose setters or public fields.

Flyweight factory

You can create a factory method that would manage a pool of existing flyweights objects. The method would accept desired intrinsic state from a client, look for an existing flyweight object matching this state, and return it if it was found. If not, it will create new flyweight and add it to the pool.

There are several options where this method could be placed. The usual choice is a flyweight container. Alternatively, a new factory class could be created. You could even make the factory method static and put it inside the main Flyweight class.

Structure

Flyweight pattern structure
  1. Flyweight interface defines methods common to all flyweights. Most importantly, it describes how methods receive chunks of extrinsic state from clients.

  2. Flyweight contains its own intrinsic state, independent from a context where objects are used. Flyweight gets extrinsic state from a client via method parameters.

    The same flyweight object can be used by different clients at the same time. Thus, flyweight should be immutable so that one client won't affect the others by changing something inside a flyweight.

  3. Unshared Flyweight stores full state, including extrinsic data. Even though it implements Flyweight interface that relies on getting extrinsic data from outside, Unshared Flyweights use their "internal" extrinsic state and ignore the one passed by clients.

    These objects are necessary in cases where intrinsic state varies just as much as extrinsic, so the separation is impossible. But thanks to the common Flyweight interface, they could still be used along with regular Flyweights by the same clients.

  4. Flyweight Factory manages a pool of existing flyweights. Clients don't create Flyweights directly. Instead, they call a flyweight factory and describe the desired flyweight by passing intrinsic state to factory's creational method.

    Factory looks over previously created flyweights and tries to find an existing object matching client's needs. If it can't be found, the factory creates a new flyweight, adds it to the pool and only then returns it to the client.

  5. Client calculates or stores the extrinsic state of flyweights. From a client's perspective, a flyweight is a template object that can be configured in runtime by passing contextual data through parameters of its methods.

Pseudocode

In this example, the Flyweight pattern helps to render a million trees on a canvas. Pattern extracts repeating intrinsic state from a main Tree class and puts it into a flyweight class TreeType.

Flyweight pattern example

Now instead of storing the same data in multiple objects, it will be kept in several flyweight objects and linked to appropriate Tree objects. The client code works with trees through a flyweight factory, which encapsulates the logic of reusing existing tree types in new tree objects.

// The Flyweight class contains only a portion of state that describes a tree.
// These field store values that hardly unique for each particular tree. You
// won't find here tree coordinates, however textures and colors shared between
// multiple are here. And since this data is usually BIG, you'd waste a lot of
// memory by keeping it in each tree object. That's why we extract texture,
// colors and other data to a separate flyweight class that can be referenced
// from all those similar trees.
class TreeType is
    field name
    field color
    field texture
    constructor Tree(name, color, texture) { ... }
    method draw(canvas, x, y) is
        Create a bitmap from type, color and texture.
        Draw bitmap on canvas at X and Y.

// Flyweight factory decides whether to re-use existing flyweight or create a
// new object.
class TreeFactory is
    static field treeTypes: collection of tree types
    static method getTreeType(name, color, texture) is
        result = treeTypes.find(name, color, texture)
        if (result == null)
            tree = new TreeType(name, color, texture)
            treeTypes.add(tree)
        return tree

// Context object contains extrinsic part of tree state. Application can create
// billions of these since they are pretty thin: just two integer coordinates
// and one reference.
class Tree is
    field x,y
    field type: TreeType
    constructor Tree(x, y, type) { ... }
    method draw(canvas) is
        type.draw(canvas, this.x, this.y)

// Tree and Forest classes are Flyweight's clients. You might merge them
// together if you don't plan to develop a Tree class any further.
class Forest is
    field trees: collection of Trees

    method plantTree(x, y, name, color, texture) is
        type = TreeFactory.getTreeType(name, color, texture)
        tree = new Tree(x, y, type);
        trees.add(tree)

    method draw(canvas) is
        foreach tree in trees
            tree.draw(canvas)

Applicability

When you have to support a huge amount of objects that hardly fit into given RAM.

The benefit from applying the Flyweight pattern relies heavily on how and where it is used. It's most useful when:

  • an application needs to spawn a large number of objects;
  • these objects consume all system RAM;
  • objects contain duplicate state, that can be extracted and shared.

How to Implement

  1. Divide fields of a class that should become a flyweight into two parts:

    • intrinsic state: fields that contain unchanging data, duplicate across many objects;
    • extrinsic state: fields that contain contextual data, unique for all objects.
  2. Leave the fields that represent the intrinsic state in the class, but make sure they are immutable. They should accept any values only inside the constructor.

  3. Turn the fields of the extrinsic state into the arguments of methods that referred to them.

  4. Create a flyweight factory class. It should check for existing flyweight before creating a new one. Clients must request flyweights from the flyweight factory. They should describe the desired flyweight by passing its intrinsic state as arguments to the factory method.

  5. Clients must store or calculate values of extrinsic state (context) to be able to call methods of flyweight objects.

Pros and Cons

  • Saves RAM, thus allowing a program to support much more objects.
  • Wastes CPU time on searching or calculating the context.
  • Increases overall code complexity by creating multiple additional classes.

Relations with Other Patterns

  • Flyweight is often combined with Composite to implement shared leaf nodes and save RAM.

  • Whereas Flyweight shows how to make lots of little objects, Facade shows how to make a single object represent an entire subsystem.

  • Flyweight looks almost like Singleton in cases where it's possible to reduce everything to just one flyweight object. But remember, there are two fundamental differences between these patterns:

    1. Singleton object can be mutable. Flyweight objects are immutable.
    2. There should be only one Singleton instance, whereas Flyweight class can have multiple instances with a different intrinsic state.

Implementations in Different Programming Languages

Java