Flyweight Design Pattern in Java

In this article, we will do an in-depth exploration of Flyweight Design Pattern in Java. So, let’s get started.

What is Flyweight Design Pattern in Java?

Flyweight Design Pattern in Java helps in reducing the cost of creation of large number of objects by reusing existing objects.

An object’s data can be divided into two parts.

Intrinsic: This makes this object instance unique. For example if we consider Character as a Object then the symbol for example “A” is the intrinsic property of the object. This intrinsic data can be shared with multiple clients.

Extrinsic: The data which can be passed by the clients or can be calculated at runtime. In the Character object, we can display different versions of the symbol “A” for different combinations of  font family,font size,colour etc. In this case, font family,font size,colour etc becomes the extrinsic properties of the Character object.

When to use Flyweight Design Pattern in Java?

  • Need to create large number of objects.
  • Memory cost is a constraint.
  • When most of the object attributes can be made external and shared.
  • Extrinsic state can be computed or passed by the client to the shared object.
  • A few shared objects would easily replace many unshared objects.
  • The identity of each object does not matter.

Class Diagram for Flyweight Design Pattern

Flyweight Design Pattern in Java

Components

  1. Flyweight Interface : It exposes an API wherein the client can pass on the extrinsic parameters.
  2. ConcreteFlyweight Class: There can be more than one concrete Flyweights. These flyweight  objects have intrinsic state and can be shared across multiple clients.
  3. FlyweightFactory Class: is responsible for the creation and management of the flyweights, ensuring that they are shared properly. If the desired Flyweight isn’t created yet it will create and return one. Otherwise, it will return one from the current pool of flyweights.
  4. Client Class: It acquires one of the flyweight objects from the FlyweightFactory and then passes on the extrinsic attributes to the flyweight for performing the required operation.

We will take an example wherein we draw different Shapes(Rectangles and Ovals) by reusing one Rectangle and one  Oval object. In this case, we have label “R” or “O” as intrinsic property and other attributes like Graphics,color,font,fill etc as extrinsic attributes which are passed by the Client class.

Class Diagram for our Example

flyweight

Java Code

// Shape Interface
package com.design.flyweight;

import java.awt.Color;
import java.awt.Graphics;

public interface Shape {
    public void draw(Graphics g, int x, int y, int width, int height,
        Color color, boolean fill, String font);
}

// Rectangle class

package com.design.flyweight;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

public class Rectangle implements Shape {

    private String label;

    public Rectangle(String label) {
        this.label = label;

    }

    public void draw(Graphics rectangle, int x, int y, int width, int height,
        Color color, boolean fill, String font) {
        rectangle.setColor(color);
        rectangle.drawRect(x, y, width, height);
        rectangle.setFont(new Font(font, 12, 12));
        rectangle.drawString(label, x + (width / 2), y);
        if (fill)
            rectangle.fillRect(x, y, width, height);
    }
}

// Oval Class

package com.design.flyweight;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

public class Oval implements Shape {

    private String label;

    public Oval(String label) {
        this.label = label;

    }

    public void draw(Graphics oval, int x, int y, int width, int height,
        Color color, boolean fill, String font) {
        oval.setColor(color);
        oval.drawOval(x, y, width, height);
        oval.setFont(new Font(font, 12, 12));
        oval.drawString(label, x + (width / 2), y);
        if (fill)
            oval.fillOval(x, y, width, height);
    }
}

// ShapeFactory Class

package com.design.flyweight;

import java.util.HashMap;

public class ShapeFactory {

    private static final HashMap < String, Shape > shapes = new HashMap < > ();

    public static Shape getShape(String label) {
        Shape concreteShape = (Shape) shapes.get(label);

        if (concreteShape == null) {
            if (label.equals("R")) {
                concreteShape = new Rectangle(label);
            } else if (label.equals("O")) {
                concreteShape = new Oval(label);
            }
            shapes.put(label, concreteShape);
        }
        return concreteShape;
    }
}

// FlyweightClient Class

package com.design.flyweight;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class FlyweightClient extends JFrame {

    private static final int WIDTH = 400;
    private static final int HEIGHT = 400;

    private static final String shapes[] = {
        "R",
        "O"
    };
    private static final Color colors[] = {
        Color.red,
        Color.green,
        Color.blue
    };
    private static final boolean fill[] = {
        true,
        false
    };
    private static final String font[] = {
        "Arial",
        "Courier"
    };

    public FlyweightClient() {
        Container contentPane = getContentPane();

        JButton startButton = new JButton("Draw Shapes");
        final JPanel panel = new JPanel();

        contentPane.add(panel, BorderLayout.CENTER);
        contentPane.add(startButton, BorderLayout.SOUTH);
        setSize(WIDTH, WIDTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                Graphics g = panel.getGraphics();
                for (int i = 0; i < 100; ++i) {
                    Shape shape = ShapeFactory.getShape(getRandomShape());
                    shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(),
                        getRandomHeight(), getRandomColor(),
                        getRandomFill(), getRandomFont());
                }
            }
        });
    }

    private String getRandomShape() {
        return shapes[(int)(Math.random() * shapes.length)];
    }

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

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

    private int getRandomWidth() {
        return (int)(Math.random() * (WIDTH / 7));
    }

    private int getRandomHeight() {
        return (int)(Math.random() * (HEIGHT / 7));
    }

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

    private boolean getRandomFill() {
        return fill[(int)(Math.random() * fill.length)];
    }

    private String getRandomFont() {
        return font[(int)(Math.random() * font.length)];
    }

    public static void main(String[] args) {
        new FlyweightClient();
    }
}

Output

Screen Shot 2016-08-22 at 10.46.40 am

Pros:Flyweight Design Pattern in Java

  1. Memory Savings: Reduces memory consumption by sharing common intrinsic state among multiple objects.
  2. Performance Improvement: Minimizes computational overhead by reusing shared state, enhancing execution speed.
  3. Resource Efficiency: Efficiently manages resources, especially in scenarios with a large number of similar objects.
  4. Scalability: Works well for systems requiring the creation of many instances of similar objects.
  5. Reduced Object Creation: Decreases the need for creating new objects, resulting in fewer instances.
  6. Simplified Code: Simplifies client code by externalizing and sharing state.

Cons:Flyweight Design Pattern in Java

  1. Complexity: Can introduce complexity when dealing with shared state and extrinsic state.
  2. Limited Applicability: Not suitable for objects with unique states, or when the state is frequently changing.
  3. Maintenance Challenges: Changes to shared state management might impact multiple parts of the application.

Best Practices: Flyweight Design Pattern in Java

  1. Identify Intrinsic and Extrinsic State: Clearly differentiate between shared intrinsic state and non-shared extrinsic state.
  2. Use a Factory: Implement a factory to manage object creation and ensure proper sharing of flyweight objects.
  3. Immutable Intrinsic State: Make intrinsic state immutable to ensure safe sharing among multiple clients.
  4. Consider Thread Safety: Address thread safety concerns, as shared state might be accessed concurrently.
  5. Limit State Variation: Minimize the number of distinct intrinsic states to optimize memory savings.
  6. Profile for Performance: Profile the application to ensure memory and performance benefits are realized.
  7. Keep Extrinsic State Local: Avoid storing extrinsic state within the flyweight objects to maintain their reusability.

Conclusion: Flyweight Design Pattern in Java

In this comprehensive exploration of the Flyweight Design Pattern in Java, we’ve covered its essence and various facets.

We’ve examined its purpose, the scenarios where it finds optimal use, and the components that constitute it, namely the Flyweight Interface, ConcreteFlyweight, FlyweightFactory, and Client.

We’ve also delved into its advantages, such as memory savings, performance enhancement, and resource efficiency, while acknowledging its limitations in terms of complexity and limited applicability. To ensure effective implementation,

we’ve highlighted best practices including clear state definition, using a factory, ensuring immutable intrinsic state, addressing thread safety, minimizing state variations, profiling for performance, and keeping extrinsic state local. By following these practices, developers can harness the power of the Flyweight Design Pattern to optimize memory usage, streamline execution, and enhance code manageability in Java applications.

Must Read Design Pattern Articles: 

Leave a Reply

Your email address will not be published. Required fields are marked *