dcsimg
August 25, 2019
Hot Topics:

Implementing Flyweight Patterns in Java

  • January 28, 2019
  • By Manoj Debnath
  • Send Email »
  • More Articles »

The flyweight pattern is suitable in a situation where we need a number of same types of object. Here, the design is laid out in a manner that one can reduce the creation of multiple objects. Objects at runtime consume resources, so it's better to have a lesser number of them in memory as possible. It decreases memory footprint and leverages overall performance of the program. This article delves into exploring this pattern with the help of a simple example in Java.

Overview

A flyweight pattern reduces the use of repeated data. It is a common practice to design objects into fine granularity to leverage flexibility of their use. But, the thing is that objects consume resources when they run. Granularity can adversely affect the performance of the application because there is need to accommodate runtime objects in a larger set of memory. Each object not only consumes memory but also CPU cycles. In such a situation, the flyweight pattern can be a rescue by reducing repeated instantiations of fine-grained objects.

Flyweight Pattern

A flyweight object is divided into two parts:

  • Intrinsic: This is the independent part where the data is stored in the object, independent of the flyweight context. As a result, it becomes sharable with all intrinsic data that are same and hence replaceable with the flyweight object.
  • Extrinsic: This is the dependent part where a flyweight object's contextual information is preserved. This is non-shareable. The state is stored by the client object and passed on to the flyweight object as per requirements.

Use of the Flyweight Pattern

This is useful in a situation where we have many objects representing the same value. Therefore, it makes the immutable object's value shareable. For instance, observe in the following code snippet how in the valueOf() method of the Integer wrapper class of the Java Standard Library checks the value of given parameter.

public static Integer valueOf(int i) {
   if (i >= IntegerCache.low && i <= IntegerCache.high)
      return IntegerCache.cache[i + (-IntegerCache.low)];
   return new Integer(i);
}

If the value was cached previously, the method returns the constructor instance rather than creating a new object. Because the cache is initialized within a static block, an Integer instance is created only on the first invocation. Note that the IntegerCache class is a private static class defined within the Integer class.

private static class IntegerCache {
   // ...
   static {
      // ...
      cache = new Integer[(high - low) + 1];
      int j = low;
      for (int k = 0; k < cache.length; k++)
         cache[k] = new Integer(j++);
      // Range [-128, 127] must be interned (JLS7 5.1.7)
      assert IntegerCache.high >= 127;
   }
   // ...
}

Now, we can test whether the Integer class creates two distinct objects if the value is same. Compare the output in Figure 1 with the following code.

public class JustForTesting {
   public static void main(String[] args){
      final Integer intObj1 = Integer.valueOf(10);
      final Integer intObj2 = Integer.valueOf(10);
      System.out.println("First Case");
      if (intObj1 == intObj2){
         System.out.println("If the values are same " +
            "then the objects are also same.");
      } else {
         System.out.println("If the values are different " +
            "then the objects are also distinct.");
      }
      // Now, if we change the same value to a different
      // value, it becomes two distinct objects

      final Integer intObj3 = Integer.valueOf(10);
      final Integer intObj4 = Integer.valueOf(20);
      System.out.println("Second Case");
      if (intObj3 == intObj4){
         System.out.println("If the values are same " +
            "then the objects are also same.");
      } else {
         System.out.println("If the values are different " +
            "then the objects are also distinct.");
      }
   }
}

Output

Output of the preceding code
Figure 1: Output of the preceding code

Implementing Flyweight Patterns

Here is a simple implementation of a flyweight pattern. Observe how the objects of the same value are restricted to create a new object instead of supplying a copy of the object that already exists in the pool. The object pool in the factory acts as a cache that preserves flyweight instances.

package testpattern;
public class Car {
   private String color;
   public Car(String color){
      this.color = color;
      System.out.println("Painted with "+color+" color.");
   }
   public String getColor() {
      return color;
   }
}
package testpattern;
import java.util.ArrayList;
import java.util.List;
public class CarFactory {
   private List<Car> carpool = new ArrayList<>();
   public Car getFlyweightCar(String color) {
      for (Car c: carpool) {
         if (c.getColor().equals(color)){
            System.out.println(color +
               " car is already in the pool!");
            return c;
         }
      }
      Car car = new Car(color);
      carpool.add(car);
      return car;
   }
}
package testpattern;
public class App {
   public static void main(String[] args) {
      CarFactory cf = new CarFactory();
      cf.getFlyweightCar("RED");
      cf.getFlyweightCar("BLUE");
      cf.getFlyweightCar("GREEN");
      cf.getFlyweightCar("PURPLE");
      cf.getFlyweightCar("RED");
      cf.getFlyweightCar("BLUE");
      cf.getFlyweightCar("BLACK");
   }
}

Output

Output of the flyweight pattern implementation
Figure 2: Output of the flyweight pattern implementation

Conclusion

Flyweight is a common pattern used in many cases, sometimes unknowingly by the programmer. This pattern shows how we can use many objects with a low memory footprint. As cited earlier, we must always take advantage of the valueOf method of the immutable Integer class whenever possible because when we call new, a new instance is created even if the cache contains the same object. The valueOf method works otherwise, as we have discussed. This is the advantage of using the flyweight pattern.







Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.


Thanks for your registration, follow us on our social networks to keep up-to-date