Flyweight Design Pattern


Here, we will see how to create Flyweight Design Pattern with example.

Flyweight Design Pattern

The Flyweight design pattern is a structural pattern used to minimize memory usage by sharing as much data as possible with similar objects. It is particularly useful when a large number of objects are created, but many share common internal data. The key idea is to split object data into intrinsic (shared) and extrinsic (unique) states, with shared states being reused to reduce memory consumption.

Example - Chess Game

In a chess game, you have two types of pieces, black and white. However, you don't need to create separate objects for each piece on the board. You can reuse the objects for similar pieces (e.g., black pawns) by only differentiating their positions on the board. The shared intrinsic state (type, color, movement rules) can be reused, while extrinsic state (position) can be passed in dynamically. We will explain in detail lator part of the article with code example.

FlyWeight Design Pattern

Advantages of the Flyweight Pattern

  1. Memory Efficiency - By reusing shared objects, the Flyweight pattern significantly reduces memory usage, especially when many objects share the same state.
  2. Performance Improvement - Reducing the number of objects leads to better performance, as memory and processing requirements are lowered.
  3. Modular Code - Separating shared and unique states improves code clarity and allows easier maintenance.
  4. Scalability - It allows applications that deal with a large number of objects to scale better by avoiding duplication of shared data.

When to Use the Flyweight Pattern

  • Large Number of Similar Objects - When your system creates a large number of similar objects and you notice redundant data that could be shared.
  • Limited Memory Resources - When memory is a critical resource, and you need to optimize its usage.
  • Identifiable Shared Data - When objects have intrinsic data that can be shared across instances and extrinsic data that can be passed as parameters.

Examples

  • Text Editors - Characters in a document share the same font, style, and color, but their positions and content differ.
  • Graphic Systems - Rendering many similar objects, like trees or buildings in a game, where most properties can be shared.

Problems in a System Without the Flyweight Pattern

  1. Excessive Memory Usage - Without Flyweight, systems that create many similar objects will experience memory bloat due to the unnecessary duplication of shared data.
  2. Slower Performance - Creating, managing, and storing large numbers of objects with redundant data leads to increased processing overhead.
  3. Complexity in Object Management - As the system grows, managing a large number of objects becomes challenging, potentially leading to maintenance issues and bugs.

Real-World Example of the Flyweight Pattern - Chess Game

In a chess game, you have two types of pieces, black and white. However, you don't need to create separate objects for each piece on the board. You can reuse the objects for similar pieces (e.g., black pawns) by only differentiating their positions on the board. The shared intrinsic state (type, color, movement rules) can be reused, while extrinsic state (position) can be passed in dynamically.

Using the Flyweight pattern allows us to optimize memory usage by sharing the intrinsic (common) state of the pieces and handling the extrinsic (unique) state separately.

Intrinsic State (Shared)

  • Type - The kind of piece (Pawn, Rook, Knight, Bishop, Queen, King).
  • Color - Black or White.
  • Movement Rules - How the piece moves on the board.

These properties are shared among all instances of the same type and color. For example, all black pawns share the same type, color, and movement rules.

Extrinsic State (Unique)

  • Position - The current location of the piece on the board (e.g., E4, D5).

This property is unique for each piece and is not shared.

Benefits of Using Flyweight in Chess Game

  • Memory Efficiency - Instead of creating separate objects for each piece on the board, we reuse shared objects for pieces of the same type and color.
  • Performance Improvement - Reduces the number of objects created, leading to lower memory consumption and potentially faster execution.
  • Scalability - Easier to manage a large number of pieces without duplicating shared data.

C# Implementation of Flyweight Pattern for Chess Game

Below is a comprehensive C# example demonstrating the Flyweight pattern applied to a chess game.

using System;
using System.Collections.Generic;

// Flyweight Interface
public interface IChessPiece
{
    void Display(Position position);
}

// Concrete Flyweight
public class ChessPiece : IChessPiece
{
    private readonly string _type;    // Intrinsic State
    private readonly string _color;   // Intrinsic State

    public ChessPiece(string type, string color)
    {
        _type = type;
        _color = color;
    }

    public void Display(Position position)
    {
        Console.WriteLine($"{_color} {_type} at {position}");
    }
}

// Flyweight Factory
public class ChessPieceFactory
{
    private readonly Dictionary _pieces = new Dictionary();

    public IChessPiece GetChessPiece(string type, string color)
    {
        string key = $"{color}_{type}";
        if (_pieces.ContainsKey(key))
        {
            return _pieces[key];
        }
        else
        {
            IChessPiece piece = new ChessPiece(type, color);
            _pieces.Add(key, piece);
            Console.WriteLine($"Created new {color} {type}.");
            return piece;
        }
    }

    public int GetTotalPieces()
    {
        return _pieces.Count;
    }
}

// Position Class (Extrinsic State)
public class Position
{
    public string Row { get; }
    public string Column { get; }

    public Position(string column, string row)
    {
        Column = column;
        Row = row;
    }

    public override string ToString()
    {
        return $"{Column}{Row}";
    }
}

// Client
public class ChessBoard
{
    private readonly ChessPieceFactory _factory;
    private readonly List<(IChessPiece, Position)> _board = new List<(IChessPiece, Position)>();

    public ChessBoard(ChessPieceFactory factory)
    {
        _factory = factory;
    }

    public void AddPiece(string type, string color, string column, string row)
    {
        IChessPiece piece = _factory.GetChessPiece(type, color);
        Position position = new Position(column, row);
        _board.Add((piece, position));
    }

    public void DisplayBoard()
    {
        foreach (var (piece, position) in _board)
        {
            piece.Display(position);
        }
    }
}

// Example Usage
public class Program
{
    public static void Main()
    {
        ChessPieceFactory factory = new ChessPieceFactory();
        ChessBoard board = new ChessBoard(factory);

        // Adding Black Pawns
        for (int i = 1; i <= 8; i++)
        {
            board.AddPiece("Pawn", "Black", ((char)('A' + i - 1)).ToString(), "7");
        }

        // Adding White Pawns
        for (int i = 1; i <= 8; i++)
        {
            board.AddPiece("Pawn", "White", ((char)('A' + i - 1)).ToString(), "2");
        }

        // Adding Black Rook
        board.AddPiece("Rook", "Black", "A", "8");
        board.AddPiece("Rook", "Black", "H", "8");

        // Adding White Rook
        board.AddPiece("Rook", "White", "A", "1");
        board.AddPiece("Rook", "White", "H", "1");

        // Display all pieces
        board.DisplayBoard();

        Console.WriteLine($"\nTotal unique chess piece objects created: {factory.GetTotalPieces()}");
    }
}

Explanation

  • Creating Pieces - Only four unique ChessPiece objects are created: Black Pawn, White Pawn, Black Rook, and White Rook. This is because all pawns of the same color share the same intrinsic state, and similarly for rooks.
  • Displaying Pieces - Each piece's position is unique, but they reuse the shared ChessPiece instances.
  • Memory Efficiency - Instead of creating 16 pawn objects (8 black and 8 white) and 4 rook objects (2 black and 2 white), only 4 unique objects are created, significantly reducing memory usage.

Output

FlyWeight Design Pattern

Application of the Flyweight Design Pattern

  • Game Development - When developing games where many entities like enemies, NPCs, or environmental objects have shared characteristics (e.g., rendering trees in a forest).
  • Text Processing - In applications like word processors or HTML renderers, where characters or symbols share common formatting but differ in content.
  • GUI Systems - In systems where many buttons, icons, or other elements share a common appearance but differ in behavior or positioning.
  • Data Caching - When caching frequently accessed data objects (like database connections or configuration objects) that can share common states but differ in specific details.

Prev Next

Top Articles

  1. What is JSON
  2. How to convert a javaScript object in JSON object
  3. Some Important JSON Examples
  4. Common JSON Interview Question