Flyweight Design Pattern

Flyweight Design Pattern | Flyweight means removing or reducing or decreasing the weight. This design pattern says to use JVM memory of RAM effectively while creating a huge number of objects so that less memory can be used to maintain more objects. 

According to GOF, the Flyweight design pattern intent is “Use sharing to support large numbers of fine-grained objects efficiently.” 

The flyweight design pattern is a structural design pattern like Facade pattern, Adapter pattern, and Decorator pattern. The Flyweight design pattern is used when we need to create a lot of objects of a class. Since every object consumes memory space that can be crucial for low memory devices, such as mobile devices or embedded systems. The Flyweight design pattern can be applied to reduce the load on memory by sharing objects. 

For example, the IOS RAM size is small but the performance of the iPhone is much better compared to the Andriod mobile with huge RAM size because of the algorithm that is there for memory management. 

In the flyweight design pattern, instead of creating a large number of similar objects, we can create one object and it can be reused to save memory. This pattern is especially useful when memory is a key concern

Mobiles are small devices that come with a limited set of resources. Memory capacity in a smartphone is very less and should be used very efficiently. In this case, if we try to represent one object for every operation then the entire mobile memory will be filled up with these objects and makes our mobile run quickly out of memory. 

Let us understand it through an example. The phone has an application that is similar to the paint application. A user can draw as many shapes in it as circles, triangles, squares, and e.t.c.

Normal Application,

  • 3 Circles: 3 objects for Circle class
  • 3 Squares: 3 objects for Square class

Flyweight design pattern application,

  • 3 Circles: 1 object for Circle class and keep that object in cache and use it for 3 times by passing radius as the argument value to draw(-) method.
  • 3 Squares: 1 object for Square class and keep that object in cache and use it for 3 times by passing side length as the argument value to draw(-) method.

When Flyweight Design Pattern Should be Used?

We should use the flyweight design pattern in the following situation:-

  • The number of objects to be created by the application should be huge.
  • The object creation is heavy on memory and it can be time-consuming too.
  • The object properties can be divided into intrinsic and extrinsic properties. The extrinsic properties of an object should be defined by the client program.

Let us understand intrinsic and extrinsic properties. 

  • Intrinsic properties:- The properties that are declared inside the class as member variables and common for all objects created for the class are called intrinsic (sharable) properties/data. 
  • Extrinsic properties:- The data/properties that are coming to object from the user/client app as the Java method argument value and it is not common for all objects of that class is called extrinsic (non-sharable) properties/data.

Circle class,

  • label: “circle” (same for all, intrinsic) 
  • radius: 30/50/80 (vary for different objects, extrinsic) 
  • fillColor: (extrinsic)
  • lineStyle: (extrinsic)
  • lineColor: (extrinsic)
  • defaultId: (intrinsic)

Here “label” is intrinsic property because all objects of circle class can have the same label “circle”. Similarly, defaultId also will be the same for all objects therefore it is an intrinsic property. Whereas the radius is extrinsic property because every time the radius of the circle drawing will be changed by the end-user. Similarly, the fillColor, lineStyle, lineColor properties are also intrinsic properties.

According to flyweight, first, we should identify intrinsic (sharable) and extrinsic (non-sharable) data or properties of the object/class. While designing class we should declare intrinsic properties as member variables (non-static) and we should use extrinsic data/properties as Java method parameter values as shown below:-

public class Circle {
    private String label = "circle"; // intrinsic data

    public Circle() {
    }

    // method taking extrinsic data/property
    public void draw(double radius, 
                     String fillColor, String lineStyle) { 
    }
}

After designing classes by identifying and keeping intrinsic and extrinsic properties now we need to take FlyWeightFactory class to create the single object for these classes to keep them in the cache for reusability so that for every class, only a single object can be used multiple times to complete the tasks.

Spring IOC container acting as Flyweight design pattern based Container while working with singleton scope beans because it creates a single object for Singleton scope spring bean and keeps that object in the internal cache of IOC container for reusability.

ServletContainer is also designed based on Flyweight design pattern because Servlet is a single instance, multiple threads component and ServletContainer will create an object only once for a given Servlet component and keep it in the cache which will be used multiple times. 

Singleton Java class vs Flyweight Design Pattern

You may have thought that what is the difference between the Singleton Java class and FlyWeight design pattern? Both are saying to create only one object and reuse it, then what is the difference among them?

Singleton Java class allows the creation of only one object in any situation, and its object will not be placed in any cache (the private static <class> INSTANCE property itself acts as cache in the case of singleton Java class). Within the same JVM, the instance variable itself acts as a cache. 

In the Flyweight design pattern, the classes are normal classes, if needed we can create multiple objects for those classes having different intrinsic states but we generally create one object and keep that object in the internal cache for reusability. 

If there is a need of taking a class without a state or read-only state or sharable state then we can go for a singleton Java class. Whereas if there is a need of taking multiple classes of the same family and reusing their single objects by keeping them in the cache then go for a flyweight design pattern. 

If Spring IOC container and ServletContainer are not given based on Flyweight design pattern then developing multiple or all servlet classes, multiple or all spring bean classes as singleton Java classes manually will be a very complex process for the programmers. 

Flyweight Design Pattern Example

We want to develop an application that is similar to a paint application where users can draw as many shapes as they like circles, squares, and e.t.c. First, we will see the problem part. See FlyWeight design pattern problem code at GitHub:- Flyweight-Problem Example

Problem:- To draw 500 circles this application is using 500 objects for the Circle class which may lead to memory issues and the system may hang up. Currently, we have taken simple Circle objects so this issue may look very small but if circle object creation is a complex process and dependent on various object creation at that time this issue will be critical, we can think in that direction. 

Solution:- Place caching logic of FlyweightFactory class (nothing but ShapeFactory). 

Let’s see the solution part. The package, interface, and class will remain the same as the problem project, only ShapeFactory.java code will change in the solution part.

FlyWeightDP
  => src
     => com.kp.comp
        => Shape.java (I)
        => Circle.java
        => Square.java
     => com.kp.factory
        => ShapeFactory.java
     => com.kp.test
        => FlyWeightProblem.java

Shape.java

package com.kp.comp;

public interface Shape {
   public void draw(float arg0, 
                    String fillColor, String lineStyle);
}

Circle.java

package com.kp.comp;

public class Circle implements Shape {
   private String label = "circle";

   public Circle() {
      System.out.println("Circle.Circle(): 0-Param constructor");
   }

   @Override
   public void draw(float radius, 
                    String fillColor, String lineStyle) {
      System.out.println("Drawing cicle with radius: " 
                + radius + " having fillColor: " + fillColor 
                + " , lineStyle: " + lineStyle);
   }
}

Square.java

package com.kp.comp;

public class Square implements Shape {
   private String label = "square";

   public Square() {
      System.out.println("Square.Square(): 0-Param constructor");
   }

   @Override
   public void draw(float sideLength, 
                    String fillColor, String lineStyle) {
      System.out.println("Drawing Square with side length: " 
          + sideLength + " having fillColor: " 
          + fillColor + " , lineStyle: " + lineStyle);
   }
}

The flyweight factory will be used by client programs to instantiate the object, so we need to keep a map(cache) of objects in the factory that should not be accessible by the client application. Whenever the client program makes a call to get an instance of the object, it should be returned from the HashMap, if not found then create a new object and put it in the Map, and then return it. We need to make sure that all the intrinsic properties are considered while creating the object. 

ShapeFactory.java

package com.kp.factory;

import java.util.HashMap;
import java.util.Map;

import com.kp.comp.Circle;
import com.kp.comp.Shape;
import com.kp.comp.Square;

public class ShapeFactory {
   // cache
   private static Map<String, Shape> cacheMap = new HashMap<>();

   public static Shape getShape(String type) {
      if (!cacheMap.containsKey(type)) {
         Shape shape = null;
         if (type.equalsIgnoreCase("square")) {
            shape = new Square();
         } else if (type.equalsIgnoreCase("circle")) {
            shape = new Circle();
         } else {
            throw new IllegalArgumentException("Invalid Shape Type");
         }

         cacheMap.put(type, shape); // store to cache
      }

      return cacheMap.get(type); // return from cache
   }
}

FlyweightTest.java

package com.kp.test;

import com.kp.comp.Shape;
import com.kp.factory.ShapeFactory;

public class FlyweightTest {
   public static void main(String[] args) {
      try {
         for (int i = 0; i <= 500; ++i) {
            Shape shape = ShapeFactory.getShape("circle");
            System.out.println(shape.hashCode());
            shape.draw(i + 10, "blue", "dotted");
         }
      } catch (Exception e) {
         e.printStackTrace();
      }

      System.out.println("=======================");

      try {
         for (int i = 0; i <= 500; ++i) {
            Shape shape = ShapeFactory.getShape("square");
            System.out.println(shape.hashCode());
            shape.draw(i + 10, "green", "dotted");
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

See the Fly Weight Design Pattern Solution code at GitHub:- FlyWeightDP-Solution

cacheMap (Map Collection),

Keys (String)Values (Shape object)
CircleCircle class object reference
SquareSquare class object reference

Here there will not be any multithreading issues through multiple threads are acting on the single object because only sharable/common intrinsic data will be there inside the object. The entire extrinsic data is coming to the object from outside the object.

Pre-defined Methods Behaving Like Flyweight Design Pattern

  • Java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short, Long and BigDecimal)
Integer i1 = Integer.valueOf(10);
Integer i2 = Integer.valueOf(10);
Integer i3 = Integer.valueOf(10);

Here it will create only one Integer wrapper class object having value 10 and reuses for multiple times.

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

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