Summary
Pattern Name | Flyweight Pattern |
Pattern Type | Structural Pattern |
Scope | Object |
Tagline | Use shared objects in multiple contexts |
Use cases | 1. When the application uses a huge number of similar objects 2. When shared objects can be used to save memory footprint |
Related Patterns | Composite State Strategy |
Difficulty Level | Medium |
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
- Create a Flyweight Factory class to return the desired object.
- If the object already exists, then return it, else create a new object + cache it + return it.
- Separate intrinsic and extrinsic properties of the object.
- 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).
- 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.