Flyweight Design Pattern in Java

This article explains Flyweight design pattern in java with UML class diagram. It then takes an example scenario in java and explains it with class diagram and code.
Introduction Flyweight Design Pattern is a structural design pattern among the Gang Of Four(GOF) Design PatternsClick to read: Gang Of Four(GOF) Design Patterns. Being a structural design pattern, the Flyweight pattern deals with how objects are composed - here, by sharing one object among many others to save memory. What is Flyweight Design Pattern The Flyweight design pattern uses sharing to support large numbers of fine-grained objects efficiently. When an application needs a very large number of similar objects - characters in a document, trees in a game world, cells in a spreadsheet - creating each one with all its data can use an enormous amount of memory. The Flyweight pattern splits an object's state in two -
  • Intrinsic state - data which is the same for many objects and does not depend on context, such as a tree's species, colour and texture. It is stored once, in a shared flyweight object, which must be immutable.
  • Extrinsic state - data which is different for each object, such as a tree's position. It is stored outside the flyweight, or passed to it when it is used.
A flyweight factory makes sure that each distinct flyweight is created only once and then shared. Scenarios in which Flyweight Design Pattern can be used
  1. A very large number of objects: The application uses so many objects that storing all their data separately is costly in memory.
  2. Most of the state can be made extrinsic: Most of an object's data is shared with many other objects, and the part which is unique can be kept outside it or computed when needed.
  3. Object identity does not matter: The application does not depend on each object being a distinct instance, since many logical objects will share one flyweight.
Class Diagram for Flyweight Design Pattern
Flyweight Design Pattern Class Diagram
Explanation of Flyweight Design Pattern's Class Diagram
  • Flyweight is the interface through which flyweights receive their extrinsic state, as a parameter of operation(extrinsicState).
  • ConcreteFlyweight stores only intrinsic state, and is shared. It must be immutable, since many clients use the same instance.
  • UnsharedConcreteFlyweight is an optional class which implements the interface but is not shared - for objects whose state is all unique.
  • FlyweightFactory keeps a pool of flyweights. getFlyweight(key) returns an existing flyweight for the key, or creates, stores and returns a new one.
  • Client obtains flyweights only from the factory, and keeps or computes the extrinsic state, passing it in when it uses a flyweight.
Flyweight Design Pattern in Java - Class Diagram
Flyweight Design Pattern in Java Class Diagram
The example plants a forest of a million trees, as a game or a map renderer might. Each tree has a position, but there are only a few kinds of tree, and each kind's name, colour and texture are the same for all trees of that kind.
Code for the classes shown in Java Example's Class Diagram
TreeType.java - the flyweight (intrinsic state)
//TreeType.java
public final class TreeType {
 private final String name;
 private final String color;
 private final String texture;      // imagine a large texture image here

 TreeType(String name, String color, String texture) {
  this.name = name;
  this.color = color;
  this.texture = texture;
 }

 String draw(int x, int y) {        // extrinsic state passed in
  return name + " (" + color + ") at (" + x + ", " + y + ")";
 }
}
TreeTypeFactory.java - the flyweight factory
//TreeTypeFactory.java
import java.util.HashMap;
import java.util.Map;

public class TreeTypeFactory {
 private static final Map<String, TreeType> types = new HashMap<>();

 static TreeType getTreeType(String name, String color, String texture) {
  String key = name + "|" + color + "|" + texture;
  return types.computeIfAbsent(key, k -> new TreeType(name, color, texture));
 }

 static int typesCreated() { return types.size(); }
}
Tree.java - one tree: extrinsic state plus a shared TreeType
//Tree.java
public class Tree {
 private final int x;
 private final int y;
 private final TreeType type;       // shared, not copied

 Tree(int x, int y, TreeType type) {
  this.x = x;
  this.y = y;
  this.type = type;
 }

 String draw() { return type.draw(x, y); }
 TreeType type() { return type; }
}
Forest.java (with the main() method)
//Forest.java
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Random;

public class Forest {
 private final List<Tree> trees = new ArrayList<>();

 void plantTree(int x, int y, String name, String color, String texture) {
  trees.add(new Tree(x, y, TreeTypeFactory.getTreeType(name, color, texture)));
 }

 public static void main(String[] args) {
  String[][] kinds = {{"Oak", "green", "oak.png"}, {"Pine", "dark green", "pine.png"}, {"Birch", "white", "birch.png"}};
  Forest forest = new Forest();
  Random random = new Random(42);
  for (int i = 0; i < 1_000_000; i++) {
   String[] k = kinds[random.nextInt(kinds.length)];
   forest.plantTree(random.nextInt(10_000), random.nextInt(10_000), k[0], k[1], k[2]);
  }
  System.out.println("Trees planted      : " + forest.trees.size());
  System.out.println("TreeType objects   : " + TreeTypeFactory.typesCreated());

  IdentityHashMap<TreeType, Boolean> distinct = new IdentityHashMap<>();
  forest.trees.forEach(t -> distinct.put(t.type(), true));
  System.out.println("Distinct TreeTypes referenced by trees: " + distinct.size());
  System.out.println("First tree         : " + forest.trees.get(0).draw());
 }
}
 OUTPUT on running Forest.java
Trees planted      : 1000000
TreeType objects   : 3
Distinct TreeTypes referenced by trees: 3
First tree         : Birch (white) at (2763, 1248)
Explanation of Java Example's Class Diagram & Code
  • TreeType is the flyweight. It holds the intrinsic state - name, colour and texture - and is immutable, so one instance can safely be shared by any number of trees.
  • TreeTypeFactory is the flyweight factory. computeIfAbsent() creates a TreeType only the first time a combination is requested, and returns the cached one afterwards.
  • Tree holds only the extrinsic state - its position - plus a reference to its shared TreeType, which it passes its position to when drawn.
  • Forest, the client, planted a million trees, but only 3 TreeType objects exist, and every tree references one of them. If each tree held its own copy of the type data - and a real texture is many kilobytes - memory use would grow a million-fold for that data instead of staying constant.
Note - The JDK uses the Flyweight pattern for small values: Integer.valueOf() returns shared, cached objects for values from -128 to 127 (and similarly for Long, Short, Byte and Character), Boolean.valueOf() returns one of two shared objects, and string literals are shared through the string poolClick to read: string pool.
Summary In the above tutorial we understood what is Flyweight design pattern, the difference between intrinsic and extrinsic state, and the main scenarios in which it is applicable. We then looked at the UML class diagram for Flyweight Design Pattern & its explanation, a Java use case which planted a million trees using three shared tree types, with its class diagram, code and output, followed by an explanation of both the class diagram & code. This concludes the tutorial on Flyweight design pattern.