Tuesday, 13 October 2015

Flyweight Pattern

Description

Flyweight pattern is one of the fundamental design patterns described in the volume “Design Patterns: elements of reusable object-oriented software”.
It falls in the category of structural patterns.


Purpose

Suppose that, in your app, you have to create and use many objects, and this process requires a lot of memory and/or computational resources. The purpose is to reduce the proliferation of objects as much as possible.Solution. In a class we can identify two different states:

  • internal state: internal state corresponds to the features of the object that are constant, regardless of the context in which it is used; the internal state can therefore be shared (there is no need to create different objects with the same internal state: we can re-use the already existing ones);
  • external state: external state contains the features of the object that depend on the context in which the object is created and used.
Flyweight pattern helps us to reduce the proliferation of unnecessary objects within our apps, reusing the existing objects if possible.

Implementation

In the following example we create 20 circles of different colors (5 different colors) with random location and size; using the Flyweight pattern we can create only 5 objects at most, corresponding to the 5 different colors (the color represents here the internal state of the object).

Flyweight
public interface Shape {
   void draw(int x, int y, int size);
}
This interface represents a simple geometric shape with a draw method to draw itself. The draw method takes some parameters (x, y, size) corresponding to the external state of the object (parameters set “on the fly” by the client when needed).
When defining the Flyweight interface you therefore have to find out the parameters corresponding to the external state of the object: in our simplified example the location and size of each shape.


ConcreteFlyweight
public class Circle implements Shape {
   private String color;

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


   @Override
   public void draw(int x, int y, int size) {
      System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + size);
   }
}
Circle implements Shape interface and represents a specific geometric shape: a circle. Each circle has a color (set with the constructor) and implements the draw method taking x, y and size as parameters.
Notice that Circle has an instance variable, color, which is the internal state of the object (the part of the object that can be shared). 

The external state, depending on the context (x, y and size), is set by the client “on the fly” when the circle must be drawn and passed as a parameter to the draw method.
When defining the ConcreteFlyweight you therefore have to identify the parameters corresponding to the internal state of the object; these parameters are stored inside the object itself so that they can be shared.


FlyweightFactory
public class ShapeFactory {
   private static final HashMap 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);
         System.out.println("Creating circle of color : " + color);
      }
      return circle;
   }
}
ShapeFactory is a factory class creating Circle objects requested by the clients through its getCircle(String color) method.
This class keeps an internal HashMap of Circle objects, where the key is the color of the circle.
The getCircle(String color) method tries to retrieve an already existing Circle from the HashMap with the requested color. If a circle of the specified color already exists in the HashMap, getCircle simply returns it (it is not created anew); otherwise a new Circle object is created, put in the HashMap and returned to the client.
The discriminating element for deciding whether to create a new circle or not is the color, that is the internal state of the object.

Internal state of the object. Parameters that don’t vary with the context in which the object is used (in our example, the color). These parameters can be shared: there is no need to create a new object if the internal state stays the same.
External state of the object. Parameters that vary with the context in which the object is used (in our example, the location and size of each circle). These parameters are set by the client “on the fly” before invoking the required method (in our example, the draw method).


Client
public class FlyweightPatternDemo {
   private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" };
   public static void main(String[] args) {

      for(int i=0; i < 20; ++i) {
         Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
         circle.draw(getRandomX(),getRandomY(),100);
      }
   }
   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);
   }
}
In our example the client draws 20 circles with different location (x, y) and size, randomly chosen. Notice, however, that we don’t create 20 different objects but 5 at most! Circles of the same color are infact retrieved from the HashMap; the external state (x, y and size) is simply set by the client before invoking the draw method.

The discriminating element for deciding whether to create a new circle or not is the color: if the color varies a new object must be created; if the location and size vary we can re-use a circle of the same color and set these parameters by invoking the draw method.
The client doesn’t create the objects directly, but uses the factory class ShapeFactory, which is responsible for creating a new object or retrieving an already existing one.
Notice that the external state of the object (x, y and size) is not stored inside the object itself, but managed and set by the client.


Structure

UML Diagram
Notice the UnsharedConcreteFlyweight class. UnsharedConcreteFlyweight is a class whose state must not be shared (unlike the ConcreteFlyweight); at the same time UnsharedConcreteFlyweight uses the same interface of the Flyweight (in our example, a draw method with x, y and size as parameters).
Consider as an example a text editor software:
  • ConcreteFlyweight: in a text editor software the ConcreteFlyweight is a single Character, whose internal state must be shared (for example the letter represented by that charcter, ‘a’, ‘r’, ‘d’, etc.);
  • UnsharedConcreteFlyweight: a Row is a good example of an UnsharedConcreteFlyweight. Each row must be drawn according to a specific external state (size, font, etc.): it therefore implements the Flyweight interface. At the same time there is not an internal state that can be shared (each row is different from each other). UnsharedConcreteFlyweight usually have Flyweight objects as children: a Row is composed by different Characters (that are Flyweight objects).


Insights


Applications

Web browsers generally use this pattern to display images in a web page. Suppose that the same image must be displayed in the same page in different locations and sizes. The Flyweight pattern can be used here to avoid donwloading the same image more times from the web (internal state: the image to display; external state: location and size).

War game. An object of type "soldier" must be created many times because an army is composed of different soldiers. However some features are common (graphic representation, movements, etc.), while others vary depending on the context (location, health, etc.). The Flyweight pattern can be applied here to avoid the proliferation on unnecesary objects.

Text editor. In a text editor app we could use an object, let's call it Character, to represent each character in the document. Each character has some properties, like font, size, the character displayed, etc. A document contains a lot of characters, so this approach causes un unnecesary proliferation of objects. To avoid this we could apply the Flyweight pattern as follows:
  • internal state: the internal state is the character displayed ('a', 'b', 'c', etc.). For example, if the document contains several letters 'a' we don't have to create an object for each one; instead we can re-use the same object as previously described;
  • external state: the external state could be the font, the size, the location of each character.

State and Strategy pattern
State and Strategy objects are generally implemented as Flyweights.

Singleton pattern
In our example of the Flyweight pattern ConcreteFlyweights are created using a factory class. For each type of shape, that is a shape with a given color, only one instance is created: the same instance is re-used when necessary.
This is an example of application of the Singleton pattern.

No comments:

Post a Comment