Flyweight pattern


In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory usage by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the objects temporarily when they are used.
A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document; only the position of each character would need to be stored internally.
Another example is string interning.
In other contexts the idea of sharing identical data structures is called hash consing.

Overview

The Flyweight
design pattern is one of the twenty-three well-known
GoF design patterns
that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.
What problems can the Flyweight design pattern solve?
When representing large text documents, for example, creating an object for each character in the document would result in a huge number of objects that could not be processed efficiently.
What solution does the Flyweight design pattern describe?
Define Flyweight objects that
This enables clients to reuse Flyweight objects
and pass in extrinsic state when they invoke a Flyweight operation.
This greatly reduces the number of physically created objects.
Intrinsic state is invariant and therefore can be shared
.
Extrinsic state is variant and therefore can not be shared and must be passed in
.
See also the UML class and sequence diagram below.

History

According to the textbook Design Patterns: Elements of Reusable Object-Oriented Software, the flyweight pattern was first coined and extensively explored by Paul Calder and Mark Linton in 1990 to efficiently handle glyph information in a WYSIWYG document editor, although similar techniques were already used in other systems, e.g., an application framework by Weinand et al..

Structure

UML class and sequence diagram

In the above UML class diagram,
the Client class refers to the FlyweightFactory class to create/share Flyweight objects and to the Flyweight interface to perform an operation by passing in extrinsic state
. The Flyweight1 class
implements the Flyweight interface and stores intrinsic state that can be shared.
The sequence diagram shows the run-time interactions:
The Client object
calls getFlyweight on the
FlyweightFactory that creates and returns a Flyweight1 object.
After calling operation
on the returned Flyweight1 object,
the Client again calls getFlyweight
on the FlyweightFactory, which now shares and returns
the already existing Flyweight1 object.

Immutability and equality

To enable safe sharing, between clients and threads, Flyweight objects must be immutable. Flyweight objects are by definition value objects. The identity of the object instance is of no consequence therefore two Flyweight instances of the same value are considered equal.
Example in C# :

public class CoffeeFlavour

Concurrency

Special consideration must be made in scenarios where Flyweight objects are created on multiple threads.
If the list of values is finite and known in advance the Flyweights can be instantiated ahead of time and retrieved from a container on multiple threads with no contention. If Flyweights are instantiated on multiple threads there are two options:
  1. Make Flyweight instantiation single threaded thus introducing contention and ensuring one instance per value.
  2. Allow concurrent threads to create multiple Flyweight instances thus eliminating contention and allowing multiple instances per value. This option is only viable if the equality criterion is met.

    Example in C#


using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
public interface ICoffeeFlavourFactory
public class ReducedMemoryFootprint : ICoffeeFlavourFactory
public class MinimumMemoryFootprint : ICoffeeFlavourFactory

Simple implementation

Flyweight allows you to share bulky data that are common to each object. In other words, if you think that same data is repeating for every object, you can use this pattern to point to the single object and hence can easily save space. Here the FlyweightPointer creates a static member Company, which is used for every object of MyObject.

// Defines Flyweight object that repeats itself.
public class FlyWeight
public static class FlyWeightPointer
public class MyObject

Example in Java


import java.util.ArrayList;
import java.util.WeakHashMap;
class CoffeeFlavour
@FunctionalInterface
interface Order
class CoffeeShop
public class FlyweightExample
The execution of this code will give the following :
Serving Cappuccino to table 2
Serving Frappe to table 1
Serving Espresso to table 1
Serving Frappe to table 897
Serving Cappuccino to table 97
Serving Frappe to table 3
Serving Espresso to table 3
Serving Cappuccino to table 3
Serving Espresso to table 96
Serving Frappe to table 552
Serving Cappuccino to table 121
Serving Espresso to table 121
CoffeeFlavor objects in cache: 3

Example in Scala


/*
run as a script using `scala flyweight.scala`
expected output:
Serving CoffeeFlavour to table 121
Serving CoffeeFlavour to table 121
Serving CoffeeFlavour to table 552
Serving CoffeeFlavour to table 96
Serving CoffeeFlavour to table 3
Serving CoffeeFlavour to table 3
Serving CoffeeFlavour to table 3
Serving CoffeeFlavour to table 97
Serving CoffeeFlavour to table 897
Serving CoffeeFlavour to table 1
Serving CoffeeFlavour to table 1
Serving CoffeeFlavour to table 2
total CoffeeFlavour objects made: 3
  • /
/* the `private` constructor ensures that only interned
* values of type `CoffeeFlavour` can be obtained. */
class CoffeeFlavour private
object CoffeeFlavour
case class Order
object CoffeeShop
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.takeOrder
CoffeeShop.service
println

Example in Ruby


  1. Flyweight Object
class Lamp
attr_reader :color
#attr_reader makes color attribute available outside
#of the class by calling.color on a Lamp instance
def initialize
@color = color
end
end
class TreeBranch
def initialize
@branch_number = branch_number
end
def hang
puts "Hang # lamp on branch #"
end
end
  1. Flyweight Factory
class LampFactory
def initialize
@lamps =
end
def find_lamp
if @lamps.has_key?
# if the lamp already exists, reference it instead of creating a new one
lamp = @lamps
else
lamp = Lamp.new
@lamps = lamp
end
lamp
end
def total_number_of_lamps_made
@lamps.size
end
end
class ChristmasTree
def initialize
@lamp_factory = LampFactory.new
@lamps_hung = 0
dress_up_the_tree
end
def hang_lamp
TreeBranch.new.hang
@lamps_hung += 1
end
def dress_up_the_tree
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
hang_lamp
puts "Made # total lamps"
puts "Hung # total lamps"
end
end

Example in Python

Attributes can be defined at the class-level instead of only for instances in Python because classes are first-class objects in the language—meaning there are no restrictions on their use as they are the same as any other object. New-style class instances store instance data in a special attribute dictionary instance.__dict__. By default, accessed attributes are first looked-up in this __dict__, and then fallback to the instance's class attributes next. In this way, a class can effectively be a kind of Flyweight container for its instances.
Although Python classes are mutable by default, immutability can be emulated by overriding the class's __setattr__ method so that it disallows changes to any Flyweight attributes.

  1. Instances of CheeseBrand will be the Flyweights
class CheeseBrand:
def __init__ -> None:
self.brand = brand
self.cost = cost
self._immutable = True # Disables future attributions
def __setattr__:
if getattr: # Allow initial attribution
raise RuntimeError
else:
super.__setattr__

class CheeseShop:
menu = # Shared container to access the Flyweights

def __init__ -> None:
self.orders = # per-instance container with private attributes
def stock_cheese -> None:
cheese = CheeseBrand
self.menu = cheese # Shared Flyweight
def sell_cheese -> None:
self.orders.setdefault
self.orders += units # Instance attribute
def total_units_sold:
return sum)

def total_income:
income = 0
for brand, units in self.orders.items:
income += self.menu.cost * units
return income
shop1 = CheeseShop
shop2 = CheeseShop
shop1.stock_cheese
shop1.stock_cheese
  1. Now every CheeseShop have 'white' and 'blue' on the inventory
  2. The SAME 'white' and 'blue' CheeseBrand
shop1.sell_cheese # Both can sell
shop2.sell_cheese # But the units sold are stored per-instance
assert shop1.total_units_sold 3
assert shop1.total_income 3.75 * 3
assert shop2.total_units_sold 8
assert shop2.total_income 3.75 * 8

Example in Swift


// Instances of CoffeeFlavour will be the Flyweights
struct CoffeeFlavor : CustomStringConvertible
// Menu acts as a factory and cache for CoffeeFlavour flyweight objects
struct Menu
struct CoffeeShop
var coffeeShop = CoffeeShop
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.takeOrder
coffeeShop.serve

Example in Crystal


  1. Instances of CoffeeFlavor will be the Flyweights
class CoffeeFlavor
def initialize
@name = new_flavor
end
def to_s
io << @name
end
end
  1. Menu acts as a factory and cache for CoffeeFlavor flyweight objects
class Menu
def initialize
@flavors = of String => CoffeeFlavor
end
def lookup
@flavors ||= CoffeeFlavor.new
end
def total_flavors_made
@flavors.size
end
end
  1. Order is the context of the CoffeeFlavor flyweight.
class Order
private getter table_number : Int32, flavor : CoffeeFlavor
def initialize
end
def serve
puts "Serving # to table #"
end
end
class CoffeeShop
private getter orders
private getter menu
def initialize
@orders = of Order
@menu = Menu.new
end
def take_order
flavor = menu.lookup
order = Order.new
@orders << order
end
def service
orders.each do |order|
order.serve
end
end
def report
"Total CoffeeFlavor made: #"
end
end
  1. Program
shop = CoffeeShop.new
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.take_order
shop.service
puts shop.report

Output

Serving Cappuchino to table 2
Serving Frappe to table 1
Serving Espresso to table 1
Serving Frappe to table 897
Serving Cappuccino to table 97
Serving Frappe to table 3
Serving Espresso to table 3
Serving Cappuccino to table 3
Serving Espresso to table 96
Serving Frappe to table 552
Serving Cappuccino to table 121
Serving Espresso to table 121
Total CoffeeFlavor made: 4