Flyweight Design Pattern in Java

1. Definition

The Flyweight Design Pattern is a structural pattern that allows programs to support vast quantities of objects efficiently by sharing common parts of state among multiple objects instead of keeping the entire state in each object.

2. Problem Statement

Imagine an application that requires a large number of similar objects which consume a large amount of memory due to repetitive information. The problem arises when these objects contain redundant information, which can lead to inefficient memory usage.

3. Solution

Flyweight aims to minimize memory use by sharing as much data as possible with similar objects. It does this by storing common data externally and referencing this data in the flyweight objects.

4. Real-World Use Cases

1. Text editors or word processors that represent each character as a flyweight object. The character's font, size, and other styling attributes can be shared.

2. Online gaming platforms that have a large number of identical objects, like trees, buildings, or guns.

5. Implementation Steps

1. Identify the common state (intrinsic) and the unique state (extrinsic).

2. Create a flyweight interface which enforces methods to receive and act on extrinsic state.

3. Implement concrete flyweight classes.

4. Use a factory to manage flyweight objects and ensure sharing.

6. Implementation

// Flyweight Interface
interface Shape {
    void draw(int x, int y, int width, int height, String color);
}

// Concrete Flyweight
class Circle implements Shape {
    private String color;

    public Circle(String color){
       this.color = color;
    }

    @Override
    public void draw(int x, int y, int width, int height, String color) {
       System.out.println("Drawing Circle with Color: " + this.color + ", X: " + x + ", Y: " + y + ", Width: " + width + ", Height: " + height);
    }
}

// Flyweight Factory
class ShapeFactory {
    private static final HashMap<String, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(String color) {
        Circle circle = (Circle) circleMap.get(color);

        if(circle == null) {
           circle = new Circle(color);
           circleMap.put(color, circle);
        }
        return circle;
    }
}

// Client code
public class FlyweightPatternDemo {
    private static final String[] colors = {"Red", "Blue", "Yellow", "Green"};

    public static void main(String[] args) {
        for(int i=0; i<10; i++) {
           Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
           circle.draw(getRandomX(), getRandomY(), 50, 50, circle.color);
        }
    }

    private static String getRandomColor() {
       return colors[(int)(Math.random()*colors.length)];
    }

    private static int getRandomX() {
       return (int)(Math.random()*100);
    }

    private static int getRandomY() {
       return (int)(Math.random()*100);
    }
}

Output:

Drawing Circle with Color: Red, X: 43, Y: 29, Width: 50, Height: 50
Drawing Circle with Color: Blue, X: 77, Y: 24, Width: 50, Height: 50
(Additional lines may vary based on random values...)

Explanation

In this example, only a few Circle objects have been created, and each of these objects is being shared among the clients. So, whenever a client requests a Circle, the ShapeFactory first checks if it has a Circle of that particular color. If it exists, that object is returned; otherwise, a new Circle is created, stored in the HashMap, and returned.

7. When to use?

Use the Flyweight pattern when:

1. A program uses a large number of similar objects, and the storage costs are high.

2. Most of an object's state can be made extrinsic.

3. Many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed.

4. The application does not depend on object identity.


Comments