Growing with the Web

Flyweight design pattern

Published , updated
Tags:

The flyweight design pattern aims to minimise the memory usage of a collection of items by promoting re-use and deferring initialisation.

To achieve this it uses a “factory” that utilises lazy loading to initialise its items only at the point in which they are required. A reference to the item is then cached by the factory in a hash map for fast retrieval at a later time.

Benefits

  • Reduces overall memory usage particularly when not all items are used or when items are used by multiple consumers
  • Reduces initial strain on the system caused by initialising all the objects in the collection

Drawbacks

  • Can cause bottlenecks that may be better off happening during the initial load if a significant number of items is required at once

Intrinsic vs extrinsic state

A flyweight object is different from a regular object by the categorisation of its data into intrinsic and extrinsic state. Intrinsic which the flyweight tracks and extrinsic which some other object tracks, the idea being that the memory it takes to store the extrinsic state need not be duplicated for every object.

An good example of this categorisation is a graphical component such as a tooltip, whose intrinsic data is the text to be displayed and the extrinsic data is the x and y coordinates on the screen.

UML diagram

Flyweight UML diagram

Code

public interface Flyweight<S> {
    public int operation(S extrinsicState);
}

public class ConcreteFlyweight implements Flyweight<Integer> {
    private Integer intrinsicState;

    public ConcreteFlyweight(Integer intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    public int operation(Integer extrinsicState) {
        return intrinsicState * extrinsicState;
    }
}

public class FlyweightFactory {
    private Map<Integer, ConcreteFlyweight> flyweights = new HashMap<Integer, ConcreteFlyweight>();

    public ConcreteFlyweight get(Integer key) {
        ConcreteFlyweight flyweight = flyweights.get(key);

        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, flyweight);
        }

        return flyweight;
    }
}
// file: flyweight.js

'use strict';

var Flyweight = function (intrinisicState) {
  this.intrinisicState = intrinisicState;
};

Flyweight.prototype.operation = function (extrinsicState) {
  // Perform some action using intrinsic and extrinsic state
  return this.intrinisicState * extrinsicState;
};

module.exports = Flyweight;



// file: flyweight-factory.js

'use strict';

var Flyweight = require('./flyweight.js');

var FlyweightFactory = function () {
  this.flyweights = {};
};

FlyweightFactory.prototype.get = function (key) {
  if (!(key in this.flyweights)) {
    this.flyweights[key] = new Flyweight(key);
  }
  return this.flyweights[key];
};

module.exports = FlyweightFactory;

Usage examples

  • A classic example of the flyweight pattern is the graphical representation of a character in a text editor. Instead of loading the font glyph and other data every time the character is used, it is loaded into memory once and shared between consumers.
  • Java’s Integer object keeps a cache of Integer objects that are boxed around the int primitive using the flyweight pattern.

Like this article?
Subscribe for more!