The Flyweight design pattern is a structural pattern that aims to reduce memory usage by sharing objects that have the same state. The idea is to create a pool of shared objects and reuse them instead of creating new objects every time.
In this article, we will explore the Flyweight design pattern in detail, including its structure, working, and implementation in Java.
Structure of Flyweight Pattern
The Flyweight design pattern consists of the following elements:
Flyweight: Defines the interface for the flyweight objects.
Concrete Flyweight: Implements the flyweight interface and stores the intrinsic state.
Unshared Concrete Flyweight: Implements the flyweight interface but does not share the state with other objects.
Flyweight Factory: Manages the flyweight objects and creates them as necessary.
How Flyweight Design Pattern Works
The Flyweight design pattern works by separating the object’s intrinsic state (shared state) from its extrinsic state (non-shared state). The intrinsic state is the data that is common to all objects, while the extrinsic state is the data that varies between objects.
The Flyweight Factory creates the flyweight objects and stores them in a pool. When the client requests a flyweight object, the Flyweight Factory checks if an object with the required intrinsic state already exists in the pool. If yes, it returns the existing object; otherwise, it creates a new object and adds it to the pool.
The client can use the flyweight object to manipulate the extrinsic state, which is not shared with other objects. When the client is done, it returns the flyweight object to the pool for reuse.
Example Implementation of Flyweight Design Pattern in Java
The Flyweight Design Pattern is used to minimize the memory usage by sharing as much data as possible with other similar objects. The shared data is known as intrinsic state, while the unique data is known as extrinsic state.
Let’s say we have a game where we need to create a lot of characters that can have different colors, sizes, and positions. Creating a separate object for each character can take up a lot of memory, and this is where the Flyweight Design Pattern comes into play. We can create a shared object pool of character flyweights, which can be reused whenever a new character is needed.
Here is an example implementation:
First, let’s define the flyweight interface:
public interface Character {
void draw(String color, int size, int x, int y);
}
This interface defines the draw method that takes in the intrinsic state (color, size, x, and y) as parameters.
Next, let’s create the concrete flyweight class that implements the Character interface:
public class CharacterFlyweight implements Character {
private String character;
private String font;
public CharacterFlyweight(String character, String font) {
this.character = character;
this.font = font;
}
@Override
public void draw(String color, int size, int x, int y) {
System.out.println("Drawing " + character + " with font " + font + " with color " + color + ", size " + size + ", at (" + x + ", " + y + ")");
}
}
This class has two fields – the character and the font, which are intrinsic properties. It implements the draw method and uses the intrinsic properties and extrinsic properties passed in as parameters to draw the character.
Now, let’s create a flyweight factory that manages the shared flyweight objects:
public class CharacterFlyweightFactory {
private Map characterFlyweights = new HashMap<>();
public CharacterFlyweight getCharacter(String character, String font) {
String key = character + font;
CharacterFlyweight flyweight = characterFlyweights.get(key);
if (flyweight == null) {
flyweight = new CharacterFlyweight(character, font);
characterFlyweights.put(key, flyweight);
}
return flyweight;
}
}
This factory class has a map of character flyweights, where the key is a combination of the character and font. It has a getCharacter method that returns a flyweight object if it exists in the map or creates a new one if it doesn’t exist.
Finally, let’s use the flyweight objects to draw characters in our game:
public class Game {
private CharacterFlyweightFactory characterFlyweightFactory;
public Game() {
characterFlyweightFactory = new CharacterFlyweightFactory();
}
public void drawCharacter(String character, String font, String color, int size, int x, int y) {
CharacterFlyweight flyweight = characterFlyweightFactory.getCharacter(character, font);
flyweight.draw(color, size, x, y);
}
}
In this class, we create a CharacterFlyweightFactory object in the constructor. We have a drawCharacter method that takes in the intrinsic state properties and uses the factory to get a flyweight object. It then calls the draw method on the flyweight object to draw the character.
With this implementation, we can reuse the same flyweight objects whenever we need to draw a character with the same intrinsic properties, minimizing the memory usage and improving performance.
Real life implementation of flyweight design patterns
Text editors: Text editors need to display large amounts of text and use a lot of memory. The Flyweight pattern can be used to store the formatting information for each character once, and then share it among all instances of that character.
Graphic design software: Graphic design software like Adobe Photoshop or CorelDRAW use a lot of memory to store images. The Flyweight pattern can be used to share common image data, like color information, between multiple instances of an image.
Gaming: Games need to manage a lot of objects, such as characters, weapons, and environments. The Flyweight pattern can be used to share common data between multiple instances of these objects, reducing the amount of memory needed to run the game.
Web applications: Web applications often need to store large amounts of data in memory, such as user sessions or cached data. The Flyweight pattern can be used to share common data between multiple instances of an object, reducing the amount of memory needed to run the application.
Database connections: Applications that use a database often need to create a new connection for each request. The Flyweight pattern can be used to share database connection objects between multiple requests, reducing the overhead of creating a new connection for each request.
Overall, the Flyweight pattern is a powerful tool for reducing memory usage in applications that deal with large amounts of data or objects. By sharing common data between multiple instances of an object, you can significantly reduce the amount of memory needed to run your application, leading to better performance and scalability.