Introduction
A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context – it’s indistinguishable from an instance of the object that’s not shared. Flyweights cannot make assumptions about the context in which they operate.
When you consider flyweight pattern, you need to remember following points:
- The pattern is useful when you need a large number of similar objects that are unique in terms of only a few parameters and most of the stuffs are common in general.
- A flyweight is an object. It tries to minimize memory usage by sharing data as much as possible with other similar objects. Sharing objects may allow their usage at fine granularities with minimum costs.
- Two common terms are used in this context: extrinsic and intrinsic. An intrinsic state is stored/shared in the flyweight object, and it is independent of flyweight’s context. On the other hand, an extrinsic state varies with flyweight’s context, which is why they cannot be shared. Client objects maintain the extrinsic state, and they need to pass this to a flyweight. Note that, if required, clients can also compute the extrinsic state on the fly when using flyweights.
- Experts suggest that while implementing this pattern, we should make intrinsic states immutable.
The Flyweight design pattern is a structural pattern used to minimize memory usage or computational expenses by sharing as much data as possible with similar objects. It is particularly useful when dealing with a large number of objects that share common data.
Key Concepts of Flyweight Pattern
Intrinsic and Extrinsic State
- Intrinsic State: Shared, immutable data common to all instances. This is stored in the Flyweight object.
- Extrinsic State: Context-specific data unique to each instance. This is stored outside the Flyweight and passed to it when needed.
Benefits
- Reduces memory usage by sharing objects.
- Improves application performance when working with a large number of similar objects.
Example: Flyweight Pattern in Action
Let’s implement a Flyweight pattern example where we simulate a drawing application to render shapes like circles with shared properties.
Step 1: Define the Flyweight Interface
The interface declares a method for performing operations with extrinsic data.
public interface Shape { void draw(String color); // Color is extrinsic data }
Step 2: Implement Concrete Flyweight Classes
This class implements the Flyweight interface and contains intrinsic state.
public class Circle implements Shape { private final String type; // Intrinsic state public Circle() { this.type = "Circle"; // Shared state for all Circle objects } @Override public void draw(String color) { System.out.println("Drawing a " + type + " with color: " + color); } }
Step 3: Create the Flyweight Factory
The factory ensures that Flyweight objects are shared and reused.
import java.util.HashMap; import java.util.Map; public class ShapeFactory { private static final Map<String, Shape> shapes = new HashMap<>(); public static Shape getShape(String type) { Shape shape = shapes.get(type); if (shape == null) { switch (type) { case "Circle": shape = new Circle(); break; // Add cases for other shapes if needed default: throw new IllegalArgumentException("Unsupported shape type: " + type); } shapes.put(type, shape); } return shape; } }
Step 4: Use the Flyweight Pattern in a Client
The client code uses the factory to get Flyweight objects and provides extrinsic data during execution.
public class FlyweightDemo { public static void main(String[] args) { Shape circle1 = ShapeFactory.getShape("Circle"); circle1.draw("Red"); // Extrinsic state: color Shape circle2 = ShapeFactory.getShape("Circle"); circle2.draw("Blue"); // Extrinsic state: color Shape circle3 = ShapeFactory.getShape("Circle"); circle3.draw("Green"); // Extrinsic state: color // Verify that the same object is reused System.out.println("circle1 == circle2: " + (circle1 == circle2)); // true } }
How It Works
- The
ShapeFactory
ensures that only one instance ofCircle
is created and reused. - The
draw
method accepts the extrinsic state (color) as input and uses it without modifying the intrinsic state. - Memory usage is reduced by reusing the
Circle
object.
Advantages and Disadvantages
Advantages
- Reduces memory consumption by sharing objects.
- Improves application performance when dealing with many similar objects.
- Promotes immutability of shared objects.
Disadvantages
- Complexity increases due to the need for managing shared and unshared states.
- Not suitable for objects with significant differences in their intrinsic state.
When to Use the Flyweight Pattern
- When your application creates a large number of similar objects.
- When memory usage is a concern.
- When the objects can be divided into intrinsic and extrinsic states.
Real-World Examples of Flyweight Pattern
- Text Editors: Characters in a document can share font data (intrinsic) while maintaining their position and formatting (extrinsic).
- Game Development: Trees, buildings, or enemies with shared visual data but unique positions.
- Graphics Rendering: Shapes or icons with shared properties.