2.6 Flyweight Pattern - Structural Design Pattern

The Structural Flyweight Pattern is a structural design pattern used in software design that shares similar data among several objects in an effort to reduce memory usage and enhance performance. When working with numerous similar objects that share a lot of shared state, it is quite helpful. This pattern optimizes application efficiency and lowers memory consumption by sharing data rather than copying it.

Overview of the Flyweight Pattern

The Flyweight Pattern achieves its goal by splitting object state into intrinsic and extrinsic parts.

  • Intrinsic state represents the data shared across multiple objects and can be shared. This state is stored in the flyweight objects.

  • Extrinsic state represents the context-dependent state that varies between objects and cannot be shared. This state needs to be supplied externally whenever a flyweight object is used.

By separating intrinsic and extrinsic state, the Flyweight Pattern enables efficient memory usage and allows a large number of lightweight objects to share common state.

Use Cases

The Flyweight Pattern comes in handy especially when:

  • There are a lot of objects that must be made.

  • The abundance of comparable objects results in high storage costs.

  • One can make the majority of the object state external.

    Graphical applications are common examples, where a huge number of comparable items, such as text editor characters, image pixels, or graphical primitives, need to be managed effectively.

Implementation in Java

Let's illustrate the Flyweight Pattern with a simple example. Consider a text editor where we want to display different characters efficiently.

import java.util.HashMap;
import java.util.Map;
// Flyweight interface
interface Character {
    void display(String font);
}
// Concrete flyweight
class CharacterImpl implements Character {
    private final char symbol;

    public CharacterImpl(char symbol) {
        this.symbol = symbol;
    }
    @Override
    public void display(String font) {
        System.out.println("Character: " + symbol + ", Font: " + font);
    }
}
// Flyweight factory
class CharacterFactory {
    private final Map<Character, Character> characters = new HashMap<>();
    public Character getCharacter(char symbol) {
        Character character = characters.get(symbol);
        if (character == null) {
            character = new CharacterImpl(symbol);
            characters.put(symbol, character);
        }
        return character;
    }
}
// Client
public class TextEditor {
    private final CharacterFactory characterFactory = new CharacterFactory();
    public void displayCharacter(char symbol, String font) {
        Character character = characterFactory.getCharacter(symbol);
        character.display(font);
    }
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        editor.displayCharacter('A', "Arial");
        editor.displayCharacter('B', "Times New Roman");
        editor.displayCharacter('A', "Verdana");
        editor.displayCharacter('C', "Courier New");
    }
}

In this example, Character is the flyweight interface, CharacterImpl is the concrete flyweight class representing a character, and CharacterFactory is the flyweight factory responsible for creating and managing flyweight objects. The TextEditor class acts as the client and demonstrates how to use flyweight objects efficiently.

  1. Intrinsic State (Character Symbol):

    • The character symbol (e.g., 'A', 'B', 'C') is intrinsic state as it is shared across multiple objects.

    • This state is stored within the flyweight objects (CharacterImpl objects) since it is common and can be shared among them.

    • In the CharacterImpl class, the symbol variable holds this intrinsic state.

  2. Extrinsic State (Font):

    • The font (e.g., "Arial", "Times New Roman", etc.) is extrinsic state as it varies between different objects.

    • This state cannot be shared among different flyweight objects; it needs to be supplied externally whenever a flyweight object is used.

    • In the TextEditor class, the displayCharacter method supplies the font as a parameter whenever a character is displayed. This font is extrinsic state that varies between different invocations of the method.

Output:

Character: A, Font: Arial

Character: B, Font: Times New Roman

Character: A, Font: Verdana

Character: C, Font: Courier New

Conclusion

An effective method for maximizing memory use and enhancing performance in applications handling a high volume of comparable objects is the structural flyweight pattern. It allows for more effective resource use without compromising flexibility or functionality by separating intrinsic and extrinsic state and sharing common state among multiple objects. The Flyweight Pattern can significantly increase the scalability and performance of applications when used wisely.