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

  1. Introduction
  2. Understanding the Problem
  3. The Flyweight Solution
  4. Implementation
  5. Common Use Cases
  6. Limitations
  7. Test your Knowledge
  8. 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?

By reusing existing objects.
By using a database to store all objects.
By compressing object data to save space.
By creating a new instance for each object.
The Flyweight Pattern optimizes performance and memory by reusing instances of existing and similar objects(intrinsic states) instead of creating a new instance for each object. This technique will drastically reduce the overall memory usage, especially when many similar objects are involved.

Question 2

In the context of the Flyweight Pattern, what are intrinsic and extrinsic states?

Intrinsic state refers to shared properties, while extrinsic state refers to properties that vary with context.
Intrinsic state is only applicable to graphic objects, while extrinsic state applies to data objects.
Intrinsic state is unique to each object, while extrinsic state is shared among all objects.
Intrinsic state is mutable, while extrinsic state is immutable.
The cardinal concept in the Flyweight Pattern is the separation of intrinsic state, shared among instances, from extrinsic state that is passed in as needed, hence achieving optimization of resource usage while reducing memory overhead.

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.