Flyweight Pattern
Introduction
The Flyweight pattern is a structural design pattern that focuses on sharing object instances to reduce memory usage and improve performance for applications with a large number of objects.
Purpose
- To reduce the memory footprint and increase performance by sharing as much data as possible with related objects.
- To use sharing to support a large number of fine-grained objects efficiently.
Use Cases
- When an application uses a large number of objects that have part of their internal state in common where the other part of the state can vary.
- When the overhead of object storage is too high due to a high quantity of objects.
Advantages/Disadvantages
Advantages:
- Reduces memory usage and object creation overhead.
- Improves application performance due to fewer memory allocations.
Disadvantages:
- Increases complexity by introducing layers of shared state management.
- Can lead to a trade-off between time and space where saving memory space might result in additional time overhead.
Implementation in JavaScript
class Flyweight {
constructor(sharedState) {
this.sharedState = sharedState;
}
operation(uniqueState) {
return `Flyweight: Displaying shared (${JSON.stringify(
this.sharedState,
)}) and unique (${JSON.stringify(uniqueState)}) state.`;
}
}
class FlyweightFactory {
constructor() {
this.flyweights = {};
}
getFlyweight(sharedState) {
const key = JSON.stringify(sharedState);
if (!(key in this.flyweights)) {
console.log('FlyweightFactory: Creating new flyweight.');
this.flyweights[key] = new Flyweight(sharedState);
} else {
console.log('FlyweightFactory: Reusing existing flyweight.');
}
return this.flyweights[key];
}
}
// Usage
const factory = new FlyweightFactory();
const flyweight1 = factory.getFlyweight({ a: 1, b: 2 });
const flyweight2 = factory.getFlyweight({ a: 1, b: 2 });
console.log(flyweight1.operation({ c: 3 }));
console.log(flyweight2.operation({ d: 4 }));
Practical Example
Consider a backend system for a book library where each book object might have repetitive data (like genre, author). We can use Flyweight to share common data among different book instances.
class Book {
constructor(title, author, genre) {
this.title = title;
this.author = author;
this.genre = genre; // Shared state
}
}
class BookFactory {
constructor() {
this.books = {};
}
getBook(genre) {
if (!this.books[genre]) {
this.books[genre] = new Book(null, null, genre);
}
return this.books[genre];
}
}
// Usage
const bookFactory = new BookFactory();
const thrillerBook1 = bookFactory.getBook('Thriller');
thrillerBook1.title = 'Book One';
thrillerBook1.author = 'Author A';
const thrillerBook2 = bookFactory.getBook('Thriller');
thrillerBook2.title = 'Book Two';
thrillerBook2.author = 'Author B';
console.log(thrillerBook1, thrillerBook2); // Both books share the same genre instance
In this example, BookFactory
creates a flyweight Book
object for each genre. When a new book is created, it shares the genre flyweight if it already exists. This pattern efficiently manages memory for large numbers of book objects with shared data.