Home Design Patterns Flyweight

Flyweight

Structural Gang of Four

“Use sharing to support large numbers of fine-grained objects efficiently.”

Gamma, Helm, Johnson & Vlissides, p. 195

Purpose

Flyweight allows multiple objects to share data. This is typically done to make better use of available memory. It can also improve processor efficiency. An example of such speed increases would be the case of 3D textures pre-loaded into a graphics card. Without preloading this shared data, the GPU bus would be overloaded.

Design

Separate the object's state information into intrinsic and extrinsic data. Think about what state information is the same across a group of objects. Put the unchanging, shared information into the flyweight and reference that information from each of the individual objects that use it.

This only works if you have a large amount of state information that is shared among objects. If the intrinsic portion is small, or if almost every object has its own variant of it, the bookkeeping costs more than the sharing saves.

A factory is usually required, not optional. If callers can construct flyweights directly, nothing guarantees that two requests for the same intrinsic state return the same instance, and the entire benefit evaporates.

UML class diagram: FlyweightFactory pools Flyweight instances; ConcreteFlyweight holds intrinsic state while extrinsic state is passed in by the client.
Intrinsic state lives once, inside the flyweight. Extrinsic state stays with the client and is passed in on every call, which is why flyweight operations tend to have longer parameter lists than you would otherwise write.

Example

Autobot Robotics has begun experimenting in artificial intelligence. It quickly became apparent that even the simplest algorithms would bring a modern computer to its knees from memory requirements alone.

In order to reduce this problem, the designers chose to share as much state information as was possible between objects. The system keeps a history and creates an object for each event the machine encounters. Rather than storing the entire environment for each object that was discovered, they chose to store that state one time and have all of the objects that use that environment share that data.

UML class diagram: FlyweightFactory holds a table of FlyWeight instances, implemented by ConcreteEnvironment and EnvironmentItem.
Events reference a shared environment rather than owning a copy. The client goes through FlyweightFactory to get one, which is what guarantees two requests for the same environment come back as the same object.
class FlyweightFactory
{
    EnvState[] envTable = new EnvState[1000];

    public int getFlyweight(EnvState env)
    {
        int hashNum = GetHashCode(env);
        if (envTable[hashNum] != null)
            return hashNum;
        else
        {
            return createEnv();
        }
    }

    private int GetHashCode(EnvState env)
    {
        return 0;  // return the hashed env
    }

    private int createEnv() {
        int newHashNum = 0;
        // create an environment and load it into the hash table
        return newHashNum;
    }
}

class FlyWeight
{
    public bool checkAction(int key);
}

class ConcreteEnvironment : FlyWeight
{
    public bool checkAction(int key)
    {
        bool shouldDo = false;
        // check to see if we should do this
        return shouldDo;
    }
}

class EnvironmentItem : FlyWeight
{
    public bool checkAction(int key)
    {
        bool shouldDo = false;
        // check to see if we should do this
        return shouldDo;
    }
}
The implementation. envTable is the pool, and getFlyweight() is the gate that keeps it authoritative: callers receive a handle rather than constructing environments of their own.

Comparisons

  • Object Pool is the pattern people most often mean when they say Flyweight. A pool hands out exclusive objects and takes them back; a flyweight is used by many clients at once and is never returned. Pools save construction cost, flyweights save memory.
  • Composite pairs well with it, because shared leaf nodes let one object appear at many positions in a tree.
  • Multiton has nearly the same implementation, a keyed map of instances, but its purpose is guaranteeing one instance per key rather than reducing footprint.
  • Singleton is the degenerate case: a flyweight pool with exactly one key.

Why It Exists

My take

Flyweight exists because object-oriented modeling and memory efficiency pull in opposite directions. The natural OO instinct is one object per real-world thing, fully self-contained. That instinct is correct for hundreds of objects and catastrophic for tens of millions, and the pattern is the concession you make when the count crosses that line.

What is unusual about it is that it is the only structural pattern in the book whose justification is a hardware constraint rather than a design one. Every other pattern here is arguing about coupling and change. Flyweight is arguing about bytes. That gives it a very different character: you can measure whether it worked, and you should, because the answer is often that it did not.

The trap I would warn about is that the pattern is most tempting exactly when it is least appropriate. Splitting intrinsic from extrinsic state is genuinely interesting work, so it feels productive. But if you have not measured the footprint first, you are very likely adding indirection, a factory, a lifetime problem, and a thread-safety problem in exchange for saving memory you were never short of.

Criticisms

The flyweight breaks encapsulation by splitting the object into separate components. This can make the code more brittle. It can also make concurrency a greater challenge, because a shared object read by many threads must be genuinely immutable, and the pattern gives you no help enforcing that.

The concept of equality becomes confusing in a flyweight, since each of the objects is using exactly the same data. Two logically distinct things are now reference-identical, so any code that used identity to tell them apart is silently wrong.

There is also a lifetime question the pattern does not answer. The pool holds references, so flyweights are never garbage collected unless you actively evict them, and deciding when it is safe to evict means knowing something about clients the pool was designed not to know about.