Light as a Feather: The Art of the Flyweight Pattern
Do not miss this exclusive book on Binary Tree Problems. Get it now for free.
In this OpenGenus article, we'll unravel the nuts and bolts of the Flyweight Pattern, exploring how it reduces memory footprint while optimizing performance.
Table of Contents
- Introduction
- Understanding the Problem
- The Flyweight Solution
- Implementation
- Common Use Cases
- Limitations
- Test your Knowledge
- Key Takeaways
Introduction
The flyweight pattern is a structural design pattern that focuses on reusing already existing objects rather than making new instances each time. This pattern empowers programs to minimize memory consumption while not compromising performance.
Understanding the Problem
In software development, it is a frequent scenario where a huge number of similar objects need to be instantiated. Dealing with thousands of objects and creating each of them individually leads to memory overhead and poor performance.
Consider a video game where you can spawn identical troops (ex. goblins, wizards) at your desired location throughout the game time. Just think of the memory usage if for each goblin you spawned, a new instance of the object was created. It would eventually cause performance degradation and latency in gameplay.
The Flyweight Solution
The aforementioned issue can be resolved by reusing existing objects instead of creating new ones. First, we have to identify what we can reuse and what we can not-
Intrinsic State:
- Shareable properties among instances of an object.
- Remains constant across all instances.
- Independent of the Object's context.
- Can be stored in the shared flyweight object.
Extrinsic State:
- Properties that are instance specific.
- Can not be shared among instances.
- Depends of the Object's context.
- Should be passed to methods at runtime.
Let's identify our intrinsic and extrinsic states. In our gaming scenario, intrinsic characteristics can be troop types (ex. goblins, wizards) and troop abilities (ex. hit points, health). Whereas troop position (x and y coordinates) can be considered as an extrinsic property.
Now that the states are identified, we will make a Troops class that will contain the intrinsic properties and implement the GameAction interface. A TroopFactory class will be responsible for returning instances based on intrinsic states. This class will ensure an object is instantiated only once and reused later on. Finally, we will enable the client to pass extrinsic properties at run time.
Code Implementation
We will implement our solution in Java.
GameAction.java
// GameAction interface defining a common method for game objects
public interface GameAction {
// Method to spawn a game object at specific coordinates (x, y)
void spawn(int x, int y);
}
Troops.java
public class Troops implements GameAction {
// The intrinsic property shared among all instances of the same type
private final String name;
// Constructor to set the intrinsic property for the Troops object
public Troops(String name) {
this.name = name;
}
@Override
public void spawn(int x, int y) {
// Extrinsic properties (x and y coordinates) are provided at runtime
// These values are unique to each method call and not stored within the object
System.out.println("Troops: Spawn() [Name : " + name + ", x-coordinates: " + x + ", y-coordinates :" + y + "]");
}
}
TroopsFactory.java
import java.util.HashMap;
public class TroopsFactory {
// A HashMap to store and reuse created Troops objects based on their names (intrinsic property).
private static final HashMap troopsMap = new HashMap<>();
// Method to get or create a Troops object.
public static GameObject getTroops(String name) {
// Check if a Troops object with the given name already exists in the map.
Troops troops = troopsMap.get(name);
// If no such object exists, create a new one and add it to the map.
if (troops == null) {
troops = new Troops(name);
troopsMap.put(name, troops);
System.out.println("Creating troop of type : " + name);
}
// Return the existing or newly created Troops object.
return troops;
}
}
Main.java
import java.util.Scanner;
public class Main {
// Array of available troop types (intrinsic properties).
private static final String[] NAMES = {"Barbarian", "Goblin", "Wizard", "Miner", "Valkyrie"};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ask the user for the number of troops they want to spawn.
System.out.println("How many troops do you wish to spawn?");
int n = Integer.parseInt(scanner.nextLine()); // Read the number of troops from user input.
// Spawn the desired number of troops.
for (int i = 0; i < n; i++) {
// Get or create a troop using the Flyweight factory.
GameObject troop = TroopsFactory.getTroops(getRandomTroops());
// Spawn the troop with random x and y coordinates (extrinsic properties).
troop.spawn(getRandomX(), getRandomY());
}
}
// Generate a random x-coordinate for the troop's position.
private static int getRandomX() {
return (int) (Math.random() * 100);
}
// Generate a random y-coordinate for the troop's position.
private static int getRandomY() {
return (int) (Math.random() * 100);
}
// Select a random troop type from the available names.
private static String getRandomTroops() {
return NAMES[(int) (Math.random() * NAMES.length)];
}
}
Common Use Cases
The flyweight pattern has many real-life use cases. Following are a few:
- Text Editors: Common fonts, styles and colors can be shared among multiple characters.
- Games: Game objects with shared attributes can be rendered once and reused later.
- Music Players: Albumn covers and artist names can be stored once and be used for multiple songs.
Limitations
As not all properties of an object can be shared, it becomes difficult to employ flyweight pattern where it is hard to separate intrinsic and extrinsic states. This can often lead to a complex code structure and difficulty in maintenance and understanding.
Test Your Knowledge
Question 1
How does the Flyweight Pattern help improving the performance and reduce memory usage in applications with numerous similar objects?
Question 2
In the context of the Flyweight Pattern, what are intrinsic and extrinsic states?
Key Takeaways
- Flyweight pattern is a structural design pattern that minimizes the use of memory for storing object data by sharing common states among objects.
- Intrinsic state can be defined as the shared properties of the instances, while extrinsic state is about the properties differentiating depending upon a context.
- The pattern suits the best when there is a need to deal with multiple similar objects possessing some common features.
Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.