Flyweight Pattern

Introduction:

Flyweight pattern is a structural design pattern which helps to reduce the memory consumptions of a object by sharing some of its data with other similar objects. 

The flyweight pattern is useful when dealing with large numbers of objects with simple repeated elements that would use a large amount of memory if individually stored. It is common to hold shared data in external data structures and pass it to the objects temporarily when they are used.

One important feature of flyweight objects is that they are immutable. This means that they cannot be modified once they have been constructed.

The idea of sharing data structures is called hash consing. Hash consing is a technique used to share values that are structurally equal. When a value is constructed, such as a cons cell, the technique checks if such a value has been constructed before, and if so reuses the previous value, avoiding a new memory allocation. 

We have to understand intrinsic and extrinsic states before going further. 
source: geeksforgeeks

Intrinsic and Extrinsic States

Suppose in a text editor when we enter a character, an object of Character class is created, the attributes of the Character class are {name, font, size}. We do not need to create an object every time client enters a character since letter ‘B’ is no different from another ‘B’ . If client again types a ‘B’ we simply return the object which we have already created before. Now all these are intrinsic states (name, font, size), since they can be shared among the different objects as they are similar to each other.

Now we add to more attributes to the Character class, they are row and column. They specify the position of a character in the document. Now these attributes will not be similar even for same characters, since no two characters will have the same position in a document, these states are termed as extrinsic states, and they can’t be shared among objects.

Intrinsic states: shareable attributes of the object
Extrinsic states: Unique attributes of the object

When to use, 

When need to create large number of objects (example: 10^5), and those objects are similar to each other with few different attributes. 

Steps to design flyweight pattern.

  1. Divide the target class's state into: shareable (intrinsic) state, and non-shareable (extrinsic) state.
  2. Remove the non-shareable state from the class attributes, and add it the calling argument list of affected methods.
  3. Create a Factory that can cache and reuse existing class instances.
  4. The client must use the Factory instead of the new operator to request objects.
  5. The client (or a third party) must look-up or compute the non-shareable state, and supply that state to class methods.

UML Class Diagram:

source:wiki













Example:

You are developing the gaming program where you need to show lot of trees for decorative purpose. 

There are two types of the trees 1. Green 2. Brown, and height is 10 for all the trees.

Tree has the below attributes. 

1. Color (Green or Brown)
    2. Height (10)
    3.  Position (X, Y coordinates).

Color attribute takes "5' bytes to store the value ("Green" or "Brown"). Height takes 4 bytes (int value). X, Y coordinates takes 8 bytes (4 + 4 int value). 

One tree object takes 17 bytes. If we construct 100000 trees, it takes 1700 KB. Color and height consumes 900 KB. Here, we are storing same color and height value in all the objects, we are wasting 900KB to store duplicate values. Since Color and height values are common, it can be considered as the intrinsic states and can be shared with other objects. In this we can reduce the size of the 100000 trees as 834 bytes. Position (X, Y ) of the tree can't be same for all the trees. It can't be shareable.

Flyweight patterns helps to reduce the memory consumption of a object by storing the intrinsic state inside the object , and passing the extrinsic states by passing as the argument of the method or storing outside of the Tree object. Here, Tree object is a flyweight object.

Implementation:

Sourcecode: https://github.com/krishnaKSA/design_patterns_cplusplus/blob/main/FlyweightPattern/FlyweightPattern.hpp

Step 1: Identify the intrinsic and extrinsic states of the class

Step 2: Create Flyweight class.

Step 3: Create Flyweight factory method.

Step 4: Context class

Step 5: Client




Step 1: Identify the intrinsic and extrinsic states of the class.

This part has done in the above section. Color and height is same for all the objects (color may be Green or Brown, height is constant for all the trees 10 ). Position of the tree can't be same for all the trees. It can't be shareable.

Intrinsic states: Color , Height 
Extrinsic states :  Position (X, Y coordinates).

Step 2: Create Flyweight class.

Tree is a Flyweight class , it has color and height intrinsic states. Position (X,Y) value is passed through the draw method function. 

//flyweight
class Tree
{
    private:
    //intrinsic state
    string color;
    int height;

    public:
    Tree(string color)
    {
        this->color = color;
        height = 10;
    }
    void draw(int x_pos, int y_pos)
    {
        cout <<"Using object "<<this<< " Tree with color  " << this->color<<" and height "<< height << " placed in this X,Y position  " << x_pos << " " << y_pos << endl;
    }
};

Step 3: Create Flyweight factory method.

TreeFactory is a factory class which produces the tree object.
Note: Here, we are using hashmap to store the cached objects. 
Client should call only the TreeFactory::getTree() method to create the object of the Tree class. 
In this method, first checks whether the similar object is already created or not. If it is already created, return the object from the cache otherwise create a new Tree object with specific color and store it in the object pool. 

In this program, height is constant for all the trees. Only colors are different either Green or Brown. So created hashkey only using the color attribute. 


//Flyweight factory class
class TreeFactory
{
    private:
    std::unordered_map<string, Tree*> objectPools; //cache

    public:
    //constructor
    TreeFactory()
    {
        objectPools =  std::unordered_map<string, Tree*>();
    }
    Tree* getTree(string color)
    {
        Tree* object = nullptr;
        //check whether the tree is in object pool
        auto iterator = objectPools.find(color);
        if(iterator != objectPools.end())
        {
            object = iterator->second;
        }
        else{
            //create new Tree object with specific intrinsic states
            object = new Tree(color);
            objectPools[color] = object;
            return object;
        }
        return object;
    }
};

Step 4: Context class

Game class has TreeFactory object which get instantiated in the constructor call. When it wants to plant trees, it gets a tree object using getTree method, and place the object by passing X,Y coordinates in draw method.

class Game
{
    private:
    TreeFactory *factory;
    public:
    Game()
    {
        factory =new TreeFactory;
    }
    void plantTrees(string color, int X, int Y)
    {
        factory->getTree(color)->draw(X, Y);    
    }
};

client code:

        Game* game = new Game;
        game->plantTrees("green", 10, 20);
        game->plantTrees("brown", 34, 100);
        game->plantTrees("brown", 40, 89);
        game->plantTrees("green", 112, 56);
        game->plantTrees("green", 34, 78);
        game->plantTrees("green", 45, 23);
        game->plantTrees("brown", 89, 56);
        game->plantTrees("green", 12, 67);
        game->plantTrees("brown", 78, 90);


Output:

Using object 000002BBED1989A0 Tree with color green and height 10 placed in this X,Y position 10 20
Using object 000002BBED198D90 Tree with color brown and height 10 placed in this X,Y position 34 100 Using object 000002BBED198D90 Tree with color brown and height 10 placed in this X,Y position 40 89 Using object 000002BBED1989A0 Tree with color green and height 10 placed in this X,Y position 112 56 Using object 000002BBED1989A0 Tree with color green and height 10 placed in this X,Y position 34 78 Using object 000002BBED1989A0 Tree with color green and height 10 placed in this X,Y position 45 23 Using object 000002BBED198D90 Tree with color brown and height 10 placed in this X,Y position 89 56 Using object 000002BBED1989A0 Tree with color green and height 10 placed in this X,Y position 12 67 Using object 000002BBED198D90 Tree with color brown and height 10 placed in this X,Y position 78 90