TypeScript Flyweight Design Pattern

Ibrahim sengun
3 min readJan 16, 2024

--

What is flyweight design pattern?

The Flyweight design pattern is a structural design pattern. It provides the ability to manipulate data across RAM. With this design pattern, the amount of space that objects occupy in the RAM can be altered by separating the common parts of the objects across all objects.

There are several terminologies in the flyweight design pattern. These are:

  • Flyweight: It stores all the common shared data across the system.
  • Context: It is the class that originally stored the shared data by default; however, now it only references the single common shared data ‘flyweight’ and adds its own unique properties once again to work normally.
  • Flyweight Factory: It handles the flyweight objects, and upon client request, it provides the appropriate flyweight if it exists. If it does not, it creates a new one.
  • Client: This refers to the application or function that communicates with the system.

When should the flyweight design pattern be used?

The Flyweight design pattern should be used when the application being developed is consuming a large amount of space in RAM, and the objects in the system responsible for increasing RAM usage share a constant common state.

It should be noted that, even though the Flyweight design pattern offers an optimization of RAM usage, it inevitably increases code complexity.

How to implement flyweight design pattern in TypeScript

Let’s apply the Flyweight design pattern to TypeScript. First, let’s imagine a scenario where we are dealing with a session management system in our e-commerce application. In our application, we attempt to record and track our users’ actions with sessions. Initially, we added the session properties to each user object. However, we failed to notice that as our user base increased, there would be a corresponding increase in our RAM usage. Consequently, we received reports from different users indicating that in their environments, our application started to crash.

Upon receiving these reports, we began debugging our code but encountered no errors that could explain the situation. We then checked our application’s RAM usage and noticed that, even though the usage was negligible during the development phase, it spiked when we went live and our user base increased. As we delved into the core of this problem, we discovered that the issue stemmed from the user objects. Each user object was storing the same session data, leading to increased RAM usage.

To address this problem, we decided to employ the Flyweight design pattern. We reworked the user objects by extracting the common, shared data ‘session’ from all of them to reduce RAM usage. With contexts and Flyweight factories, we then referenced these session objects and adjusted the sessions for users.

Flyweight design pattern diagram

Flyweight desing pattern diagram

Flyweight design pattern code

//Flyweight factory
class SessionManager {
private sessions: { [key: string]: Session } = {};

getSession(userId: string): Session {
if (!this.sessions[userId]) {
this.sessions[userId] = new Session(userId);
}
return this.sessions[userId];
}
}

//Flyweight
class Session {
private userId: string;
private actions: string[] = [];

constructor(userId: string) {
this.userId = userId;
}

addAction(action: string): void {
this.actions.push(action);
}

getActions(): string[] {
return this.actions;
}

getUserId(): string {
return this.userId;
}
}

//Context
class ShoppingCart {
private session: Session;

constructor(session: Session) {
this.session = session;
}

addItem(item: string): void {
console.log(`Item '${item}' added to the cart for user ${this.session.getUserId()}`);
this.session.addAction(`Added item: ${item}`);
}
}

// Client
const sessionManager = new SessionManager();

/*
Item 'Laptop' added to the cart for user user1
Item 'Headphones' added to the cart for user user1
User 1 Actions: [ 'Added item: Laptop', 'Added item: Headphones' ]
*/
const user1Session = sessionManager.getSession("user1");
const shoppingCartUser1 = new ShoppingCart(user1Session);
shoppingCartUser1.addItem("Laptop");
shoppingCartUser1.addItem("Headphones");
console.log("User 1 Actions:", user1Session.getActions());

/*
Item 'Smartphone' added to the cart for user user2
User 2 Actions: [ 'Added item: Smartphone' ]
*/
const user2Session = sessionManager.getSession("user2");
const shoppingCartUser2 = new ShoppingCart(user2Session);
shoppingCartUser2.addItem("Smartphone");
console.log("User 2 Actions:", user2Session.getActions());

--

--