
When working with a large number of (simple, similar) objects at once the amount of memory taken up by the objects can be considerable. Consider representing datapoints for a data series for a graph in objects. You might need to hold 2000 datapoints where many might be the same. Instead of wasting memory on a lot of identical objects you can use the Flyweight pattern.
In brief the pattern describes how to converve memory when working with a large number of objects by using a factory to construct objects rather than using the new keyword. By using the factory class you can do some checks before constructing a new object i.e. whether an existing object with the same properties already exists in a pool of already constructed objects. The example normally given, an the example used on Wikipedia, is that of representing a sentence where each letter is represented by an object. Using the Flyweight pattern a sentence will never use more than the 25 letter-objects (when talking ASCII) since there are no more than 25 unique letters. No need to represent each letter in the sentence as unique objects.
The sentence/letter example readily carries over to the graph example mentioned earlier.
Example
Let’s look at a simplified letter/sentence example in more detail. First the Letter class:
package com.lekkimworld.flyweight;
import java.util.HashMap;
import java.util.Map;
public class Letter {
private static Map letters = new HashMap();
private static int letter_count = 0;
private char letter = 0;
{
// increment counter
letter_count++;
}
public static Letter getInstance(char c) {
Character c_obj = new Character(c);
if (letters.containsKey(c_obj)) {
// reuse existing object
return (Letter)letters.get(c_obj);
} else {
// create new object
Letter l = new Letter(c);
letters.put(c_obj, l);
return l;
}
}
public static int getLetterCount() {
return letter_count;
}
private Letter(char c) {
this.letter = c;
}
public char getLetter() {
return this.letter;
}
}
…and then the Sentence class:
package com.lekkimworld.flyweight;
import java.util.LinkedList;
import java.util.List;
public class Sentence {
private List letters = new LinkedList();
public Sentence(String s) {
for (int i=0; i<s.length(); i++) {
letters.add(Letter.getInstance(s.charAt(i)));
}
System.out.println("Constructed sentence - used " + Letter.getLetterCount() +
" instead of " + s.length() + " objects.");
}
public static void main(String[] args) {
new Sentence("In the flyweight pattern, the data has no pointers " +
"to the data type methods, because these would consume too much space. " +
"Instead, the subroutines are called directly. In some cases, flyweight " +
"inheritance is performed by "shift-in" and "shift-out" data markers " +
"as a higher-level operation cycles through an array of flyweight data.");
}
}
The output of running the example is as follows:
Constructed sentence - used 28 instead of 330 objects.
Here we saved creating 302 objects – imagine if we have been dealing with a long sentence how many object creations we could have spared the JVM and how much memory we saved. The central points are in bold in the Letter class and is the static getInstance (factory) method to create new objects, the private constructor and the Map to hold already created objects.
Conclusion
The Flyweight is a pattern that can save a lot of memory when you start representing everything as objects and the scenarios where it can be used are plentiful. I know the example is a bit simplistic but it demonstrates the point of the Flyweight pattern just fine. Don’t blame the pattern for the lack implementation in the example – blame me… 🙂
Will you weigh in at Flyweight the next time you need to handle many objects?
Further reading
While ideally suited for Java development due to the classes need to implement the pattern (static variables, a Map and normally a list of some kind) it should be possible in LotusScript – maybe using the collection classes from Johan. If you are interested the design patterns I highly suggest the Design Patterns book by GoF (Gang of Four).