Design Pattern: Flyweight Pattern

Summary

Pattern NameFlyweight Pattern
Pattern TypeStructural Pattern
ScopeObject
TaglineUse shared objects in multiple contexts
Use cases1. When the application uses a huge number of similar objects
2. When shared objects can be used to save memory footprint
Related PatternsComposite
State
Strategy
Difficulty LevelMedium
Implementations

Definition

Before understanding what the Flyweight pattern is, first be familiar with the following two words (as you will see these two words several times while discussing Flyweight pattern).

Intrinsic – required or essential element of something.
Extrinsic – not an essential or required element of something. [not intrinsic]

Flyweight comes into play when –

  • We have to generate a large number of objects of a class.
  • There are one or more extrinsic properties of those objects that can be passed while generating the objects. (there may or may not be any intrinsic properties).

As we need to generate a huge number of objects of the same class, so it will occupy a big amount of memory. To avoid that, we can create objects with only the required elements and keep that in a cache. As only the required properties are there, so the number of objects will be smaller now.

As the only difference between those objects is their extrinsic properties, we will pass those properties while using that specific object.

A factory class will be responsible for generating the objects.

Use Cases

Here are the use cases of Flyweight pattern-

  • When the application uses a large number of objects of a certain class.
  • When we need to optimize and reduce the number of objects to reduce resource usage.
  • When we can use shared object instead of a group of objects.

Implementation

  1. Create a Flyweight Factory class to return the desired object.
  2. If the object already exists, then return it, else create a new object + cache it + return it.
  3. Separate intrinsic and extrinsic properties of the object.
  4. From the client (where you want to use the object) use the Factory to get the object, do not generate an object directly(using new).
  5. Provide the extrinsic properties of the client.

Examples

Example #1: Table Cells

Say we have a class for printing table cells, which is represented below. The width is an essential property and will be the same for all cells in a column.

Table Cell Class

Let’s create a class for table cells. There is a method to draw the table cell. Also, there is a method for setting text (setText()) so that we can set the text before printing the cell.

class TableCell
    var width
    var text

    // Constructor
    function TableCell(width_param)
       width = width_param
    end function

    function setText(text_param)
       text = text_param
    end function

    // dummy draw function
    function draw()
       print("Drawing cell : width = " + width + " | text = " + text)
    end function
end class

Table Cell Factory

We need to create a factory for TableCell, lets’s create TableCellFactory. We need a HashMap to store the already existing objects. Here we are generating one object for each column. The object is stored in the HashMap and when next time when an object is requested for a column, then the cached object is returned (instead of generating a new object).

class TableCellFactory
    var static Map<Integer, TableCell> tableCells

    static function getTableCell(int column, int width)
       tableCell = tableCells.get(column)

        if (null != tableCell) return tableCell

        tableCell = new TableCell(width)
        tableCells.put(column, tableCell)

        return tableCell
    end function

    // For demo purpose only, not required in actual implementation
    function static getCellObjectCount()
        return tableCells.size()
    end function 
end class

Demo

Here is the code to demo the implementation. We are printing 1000 rows and for each row, there are 5 columns.

If we generate the object without using Flyweight then it would generate 5000 objects in total. But by using Flyweight this will generate only 5 objects, are we are generating one object for each row.

// Let's consider 5 columns with width 3, 6, 2, 5, 10 of some standard unit
var columnWidths[] = {3, 6, 2, 5, 10};

// Print 1000 rows
loop row = 0; row < 1000; row++
    loop column = 0; column < columnWidths.length; column++
        tableCell = TableCellFactory.getTableCell(column, columnWidths[column])
            // for demo purpose, text can come from any other sources
            tableCell.setText(row + "-" + column)

            tableCell.draw()
     end loop
end loop

print("Total number of table cell objects: " + TableCellFactory.getCellObjectCount())

Output

The output of the above demo code will be like below.

Drawing cell : width = 3 | text = 0-0
Drawing cell : width = 6 | text = 0-1
Drawing cell : width = 2 | text = 0-2
Drawing cell : width = 5 | text = 0-3
Drawing cell : width = 10 | text = 0-4
Drawing cell : width = 3 | text = 1-0
Drawing cell : width = 6 | text = 1-1
Drawing cell : width = 2 | text = 1-2
Drawing cell : width = 5 | text = 1-3
Drawing cell : width = 10 | text = 1-4
Drawing cell : width = 3 | text = 2-0
Drawing cell : width = 6 | text = 2-1
.
.
.
...// more output lines like this
.
.
.
.
Total number of tree objects: 5

Code Implementations

Use the following links to check Flyweight pattern implementation in specific programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.