Design Pattern: Flyweight Pattern in Go

Flyweight pattern uses a shared object for multiple object generation, which has some common criteria, then populates the different properties while using that object. Using the shared object for generating multiple object output, saves resources and memory usage.

This article demonstrates Flyweight pattern implementations in GoLang. Check the implementation detail and examples below.

Implementation

Here is how to implement the Flyweight pattern-

  • Define the structs for the items.
  • Create an item factory struct for generating objects.
  • In the factory, maintain a list for storing the generated structs.
  • In the factory check if an object exists for the defined criteria.
  • If an object exists then return that, else create a new one and return that (save the new item in the list, so that can be used later).

Here is a simple example:

// Flyweight pattern in Go

package main

import (
	"fmt"
	"math/rand"
)

// Item struct
type Item struct {
	prop string
}

func NewItem() (item *Item) {
	item = &Item{}
	return
}

func (item *Item) SetProp(param1 string) {
	item.prop = param1
}

func (item *Item) Print() {
	fmt.Printf("Prop: %s", item.prop)
}

// Item factory
type ItemFactory struct {
	items map[string] Item
}

func NewItemFactory() (itemFactory *ItemFactory) {
	itemFactory = &ItemFactory{}
	itemFactory.items = make(map[string] Item)
	return
}

func (itemFactory *ItemFactory) GetItem(itemType string) Item {
	item, ok := itemFactory.items[itemType]

	if ok {
		return item
	}

	item = *NewItem()
	itemFactory.items[itemType] = item

	return item
}

func (itemFactory *ItemFactory) GetItemCount() int {
	return len(itemFactory.items)
}

// Demo
func main() {
	// lets generate and print 10,000 objects of different type
	types := [2]string{
		"TYPE_A",
		"TYPE_B",
	}
	itemFactory := NewItemFactory()

	for i := 0; i < 10000; i++ {
		for _, typeName := range types {
			item := itemFactory.GetItem(typeName)

			item.SetProp(fmt.Sprintf("val_%d\n", rand.Intn(1000))) // For demo purpose

			item.Print()
		}
	}

	fmt.Printf("Total number of objects used: %d", itemFactory.GetItemCount())
}

The output of the above code-

Prop: val_352
Prop: val_852
Prop: val_502
Prop: val_65
Prop: val_941
Prop: val_425
Prop: val_511
Prop: val_628
Prop: val_510
Prop: val_391
Prop: val_908
Prop: val_807
Prop: val_583
Prop: val_124
Prop: val_306
Prop: val_820
Prop: val_697
Prop: val_856

.
.
.
.
.
.

// -- more output here
.
.
.
.

Total number of objects used: 2

You can see the total number of objects used here is 2(two), though we have operated on thousands of items.

Examples

Here are a few examples of Flywieght pattern implementation in Go.

Example #1: Table Cells

In this example, we want to generate table cells. As the number of table cells is huge, so if each cell has its own object, then there will be a lot of memory usage.

To reduce memory and resource usage, we are creating an object for each column. As the basic criteria for all cells in a row are the same.

While using/printing the cells, we will populate the value of the properties which are different.

TableCell Struct [Item]

  • Create file “table_cell.go”.
  • Create struct “TableCell”.
  • Define fields – “width”, and “text”, for the struct.
  • Define method “NewTableCell” to generate and return a new table cell struct. Accept “with” as param, and set the width, as width is same for all cell for a column.
  • Define the method “SetText”, so that we can set the “text” value when required.
  • Define method “Draw” for the struct.
// table_cell.go

package main

import "fmt"

type TableCell struct {
	width int
	text  string
}

func NewTableCell(width int) (tableCell *TableCell) {
	tableCell = &TableCell{}
	tableCell.width = width
	return
}

func (tableCell *TableCell) Draw() {
	fmt.Printf("Drawing cell : width = %v | text = %v\n", tableCell.width, tableCell.text)
}

func (tableCell *TableCell) SetText(text string) {
	tableCell.text = text
}

TableCellFactory Class

  • Create file “table_cell_factory.go”.
  • Define struct “TableCellFactory”.
  • Define a field “tableCells” of type “map[int]TableCell”. This will be used to store the structs which are already generated.
  • Define method “NewTableCellFactory”. This method will create a new “TableCellFacory” struct, and initialize the “tableCells” field.
  • For the struct, define the method “GetTableCell”. Pass the column number and with for the column.
  • If a “TableCell” exists for the column then return it. Else create a new one and return that, also save that to the map for future use.
// table_cell_factory.go

package main

type TableCellFactory struct {
	tableCells map[int]TableCell
}

func NewTableCellFactory() (tableCellFactory *TableCellFactory) {
	tableCellFactory = &TableCellFactory{}
	tableCellFactory.tableCells = make(map[int]TableCell)
	return
}

func (tableCellFactory *TableCellFactory) GetTableCell(column int, width int) (tableCell TableCell) {
	tableCell, ok := tableCellFactory.tableCells[column]

	if ok {
		return tableCell
	}

	tableCell = *NewTableCell(width)
	tableCellFactory.tableCells[column] = tableCell

	return tableCell
}

// For demo purpose only, not required in actual implementation
func (tableCellFactory *TableCellFactory) GetCellObjectCount() int {
	return len(tableCellFactory.tableCells)
}

Demo

For checking the implementation, create a new “TableCellFctory” using “NewTableCellFactory” method.

Then use the “GetTableCell” method to get cell for the column. As for all cells for a column, it will return same object, then set the “text” value using the “SetText” method. Then we can call the “Draw” method of the cell.

// main.go

package main

import "fmt"

func main() {
	columnWidths := []int{3, 6, 2, 5, 10}
	tableCellFactory := NewTableCellFactory()

	for row := 0; row < 1000; row++ {
		for column := 0; column < len(columnWidths); column++ {
			tableCell := tableCellFactory.GetTableCell(column, columnWidths[column])
			tableCell.SetText(fmt.Sprintf("%d-%d", row, column))
			tableCell.Draw()
		}
	}

	fmt.Printf("Total number of objects: %v", tableCellFactory.GetCellObjectCount())
}

Output

Output will be as below-

We are operating on and generating thousands of cells here, but because of the flyweight pattern implementation, only 5 objects will be generated(one shared object for all cells of the column).

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
Drawing cell : width = 2 | text = 2-2
Drawing cell : width = 5 | text = 2-3
Drawing cell : width = 10 | text = 2-4
Drawing cell : width = 3 | text = 3-0
Drawing cell : width = 6 | text = 3-1
Drawing cell : width = 2 | text = 3-2
Drawing cell : width = 5 | text = 3-3
Drawing cell : width = 10 | text = 3-4
Drawing cell : width = 3 | text = 4-0
Drawing cell : width = 6 | text = 4-1
Drawing cell : width = 2 | text = 4-2
Drawing cell : width = 5 | text = 4-3
Drawing cell : width = 10 | text = 4-4
Drawing cell : width = 3 | text = 5-0
Drawing cell : width = 6 | text = 5-1
Drawing cell : width = 2 | text = 5-2
Drawing cell : width = 5 | text = 5-3
Drawing cell : width = 10 | text = 5-4
Drawing cell : width = 3 | text = 6-0
Drawing cell : width = 6 | text = 6-1
Drawing cell : width = 2 | text = 6-2
Drawing cell : width = 5 | text = 6-3
Drawing cell : width = 10 | text = 6-4
Drawing cell : width = 3 | text = 7-0
Drawing cell : width = 6 | text = 7-1
Drawing cell : width = 2 | text = 7-2
Drawing cell : width = 5 | text = 7-3
Drawing cell : width = 10 | text = 7-4
Drawing cell : width = 3 | text = 8-0
Drawing cell : width = 6 | text = 8-1
Drawing cell : width = 2 | text = 8-2
.
.
.
...// more output lines like this
.
.
.
.
Total number of objects: 5

Source Code

Use the following link to get the source code:

Other Code Implementations

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

Leave a Comment


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