jobs4timesLogo jobs4timesLogo

FlyWeight Design Pattern


  • The FlyWeight is a structural design pattern. In flyweight pattern, instead of creating large number of similar objects, those are reused to save memory.
  • This pattern is especially useful when memory is a key concerned.

  • For e.g. smart mobile comes with applications. Let's consider it has an application which is similar to paint application.
  • A user can draw as many shapes in it like circles, triangles and squares etc.,

  • Mobiles are the small devices which come with limited set of resources; memory capacity in a smart phone is very less and should use it efficiently.
  • In this case if we try to represent one object for every shape that user draws in the app, the entire mobile memory will be filled up with these objects and makes your mobile run quickly out of memory.
Let's understand this by taking a sample code here.

I have an interface IShape which represents several shapes which I have to draw.
package design;

public interface IShape {

 void draw();
	
}
Now I have Circle & Rectangle as implementation classes for the above interface.
package design;

public class Circle implements IShape{
	
	private String label;
	private int radius;
	private String fillColor;
	private String lineColor;
	
	public Circle() {
		label="circle";
	}

	//setters and getters
	
	@Override
	public void draw() {
	  System.out.println("drawing "+label+" with radius : "+radius+" fillColor : "+fillColor+" lineColor : "+lineColor);		
	}
}
package design;

public class Rectangle implements IShape {
	
	private String label;
	private int length;
	private int breath;
	private String fillStyle;
	
	public Rectangle() {
		label="rectangle";
	}

	//setters and getters
	
	@Override
	public void draw() {
	  System.out.println("drawing "+label+" with length : "+length+" breath : "+breath+" fillStyle : "+fillStyle);		
	}
}

Now in my mobile application I want to 100 circles and rectangles.
To do this I need to create 100 circles/rectangle objects,
I need to set the properties like radius,
length and breath to draw these shapes as shown below.

package design;

public class PaintApp {
	
 public void render(int noOfShapes){
   IShape[] shapes=new IShape[noOfShapes+1];
	
  for(int i=1;i<=noOfShapes;i++){
	if(i%2==0.0f){
		shapes[i]=new Circle();
		((Circle) shapes[i]).setRadius(i);
		((Circle) shapes[i]).setLineColor("red");
		((Circle) shapes[i]).setFillColor("white");
		shapes[i].draw();
	}else {
		shapes[i]=new Rectangle();
		((Rectangle) shapes[i]).setLength(i+i);
		((Rectangle) shapes[i]).setBreath(i*i);
		((Rectangle) shapes[i]).setFillStyle("dotted");
		shapes[i].draw();
	}
  }
 }//render
}
  • Let's say I want to draw 1000 shapes, do I need to create 1000 shape implemented class objects.
  • Creating 1000 shape objects consumes more amount of memory and yields in performance issues.

  • Now think to draw 1000 shapes do we need to create really 1000 shapes objects.
    If we observe here our circle or rectangle objects contains attributes.
  • These attributes represent state of the class.
    Out of which few attributes are common across all the circles/rectangles we draw.
  • For e.g. the label of a circle will be "circle" even we draw 1000 circles also, similarly the label of the rectangle will also be "rectangle".
  • This type of attributes or state contained in a class is called intrinsic state, which can be shared across the objects of the class.
  • But if we look at the radius, lineColor, fillColor or fillStyle these are the values based on which we need to draw the shape, which are going to change from one circle/rectangle to other.
    This type of attributes or state in a class is called extrinsic state (non-sharable).

  • So, flyweight pattern encourages developers to identity intrinsic and extrinsic state in an object, and recommends passing extrinsic state as dynamic values while calling the methods rather storing it as state.
  • This makes object to be reusable, making it to draw several shapes with less number of objects.

  • Designing an object down to the lowest levels of granularity makes the flexible.
  • But makes it more overweight and performance gets effected.
  • Let's re-design our classes based on the recommendations provided by flyweight to again optimal utilization of memory.

First separate the extrinsic data and pass them as parameters to the methods.

package design;

public abstract class IShape {

 public void draw(int radius,String fillColor,String lineColor){
	 //no-op
 }
 
 public void draw(int length,int breath,String fillStyle){
	 //no-op
 }
	
}
package design;

public class Circle extends IShape{
	
	private String label;
		
	public Circle() {
		label="circle";
	}	
	
	@Override
	public void draw(int radius,String fillColor,String lineColor) {
	  System.out.println("drawing "+label+" with radius : "+radius+" fillColor : "+fillColor+" lineColor : "+lineColor);		
	}
	
}
package design;

public class Rectangle extends IShape {
	
	private String label;
		
	public Rectangle() {
		label="rectangle";
	}

	@Override
	public void draw(int length,int breath,String fillStyle) {
	  System.out.println("drawing "+label+" with length : "+length+" breath : "+breath+" fillStyle : "+fillStyle);		
	}
	
}
  • Now to draw 1000 circles/rectangles we don't need to use 1000 shape objects rather than we can reuse the same object to draw any number.
  • In order to reuse the objects we need a factory. The factory class here stores the objects and allows us to track the objects to reuse.
  • It contains a map of key as shapeType and an object for that shape.

If we want to draw a circle rather than creating an object for circle, we can go to the factory and ask for a circle,
it checks and create/return the existing object in case one exists.

package design;

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

public class ShapeFactory {
	
 private volatile static Map<String,IShape> shapes;
 
 static{
	 shapes=new HashMap<String,IShape>();
 }
 
 public synchronized static IShape getShape(String type){
   IShape shape=null;
   
   //if exists return the existing object
   if(shapes.containsKey(type)){
	   shape=shapes.get(type);
   }else{
	  //if shape not found create and store
	 if(type.equals("circle")){
		 shape=new Circle();
	 }else if(type.equals("rectangle")){
		 shape=new Rectangle();
	 }
	 shapes.put(type, shape);
   }
   
	
   return shape;
 }
 
}

Now in my mobile app rather than creating shape objects we can request for the objects from factory,
which is going to either create one new or returns the existing for if already an object for the shape exists as shown below.

package design;

public class PaintApp {
	
 public void render(int noOfShapes){
   IShape shape=null;
	
  for(int i=1;i<=noOfShapes;i++){
	if(i%2==0.0f){
		shape=ShapeFactory.getShape("circle");
		shape.draw(i, "red", "white");
	}else {
		shape=ShapeFactory.getShape("rectangle");
		shape.draw(i+i, i*i, "dotted");
	}
  }//for
  
 }//render
 
 
 public static void main(String[] args){
	 PaintApp pa=new PaintApp();
	 pa.render(2);
 }
 
}
output :
drawing rectangle with length : 2 breath : 1 fillStyle : dotted
drawing circle with radius : 2 fillColor : red lineColor : white

If we see the advantage here with only 2 objects of the shape class we can manage to draw lakhs of shapes also.
We can achieve higher performance with little amount of memory.

The UML representation of FlyWeight Design Pattern :
flyweight-design-pattern

Key Points :
  • If the object overhead ia an issue, where need to reduce the memory footprint. With little amount of design changes we should be able to achieve this, but client should be notified with the impact.
  • Remove the extrinsic (non-sharable) state of the class and pass it as arguments to the parameters.
  • Create a factory through which we can reuse the objects that creating new.
  • The clients must use factory instead of creating the objects out of new operator.


BACK