Appearance
Flyweight
The Flyweight pattern uses sharing to efficiently support a large number of fine-grained objects.
Core Idea
The core idea of the Flyweight pattern is simple: if an object instance is immutable once created, there's no need to create the same instance repeatedly. Instead, a shared instance can be returned to the caller, saving memory and reducing the overhead of creating objects, thereby improving performance.
Flyweight in Java Standard Library
There are many examples of the Flyweight pattern in the Java standard library. For instance, wrapper types such as Byte
and Integer
are immutable, so creating instances with the same value repeatedly is unnecessary. When creating an Integer
using the static factory method Integer.valueOf()
, if the int value is between -128 and +127, a cached instance will be returned:
java
// Flyweight pattern example
public class Main {
public static void main(String[] args) {
Integer n1 = Integer.valueOf(100);
Integer n2 = Integer.valueOf(100);
System.out.println(n1 == n2); // true
}
}
In the case of Byte
, since there are only 256 possible values, all instances created with Byte.valueOf()
are cached objects.
Using Factory Methods
The Flyweight pattern can be achieved by using factory methods to create objects. Inside the factory method, a cached instance is often returned instead of creating a new one, enabling the reuse of immutable instances.
Tip: Always use factory methods instead of the new
operator to create instances to take advantage of the Flyweight pattern.
Real-world Applications
In practice, the Flyweight pattern is mainly used in caching. When a client repeatedly requests certain objects, there's no need to query the database or read files every time. Instead, the data can be returned directly from memory.
Let's consider an example using a Student
class with a static factory method that returns cached objects:
java
public class Student {
// Cache for holding instances:
private static final Map<String, Student> cache = new HashMap<>();
// Static factory method:
public static Student create(int id, String name) {
String key = id + "\n" + name;
// Check the cache first:
Student std = cache.get(key);
if (std == null) {
// Not found, create a new instance:
System.out.println(String.format("Creating new Student(%s, %s)", id, name));
std = new Student(id, name);
// Add to the cache:
cache.put(key, std);
} else {
// Found in the cache:
System.out.println(String.format("Returning cached Student(%s, %s)", std.id, std.name));
}
return std;
}
private final int id;
private final String name;
private Student(int id, String name) {
this.id = id;
this.name = name;
}
}
In real-world applications, mature caching libraries like Guava's Cache
are often used, as they provide useful features such as limiting the maximum cache size and expiring entries after a specified duration.
Exercise
Implement caching using the Flyweight pattern.
Summary
The Flyweight pattern's design philosophy is to reuse existing objects as much as possible, often applied as an optimization within factory methods.