×

Flyweight Design Pattern with Python

Flyweight Design Patterns Python Feature

 The flyweight software design pattern suggests creating an object that minimizes memory usage by sharing some of its data with other similar objects. 

Flyweight Design Pattern aims to minimize the number of objects required in the program during the runtime. A flyweight object has no visible difference from a normal object.

  • Flyweight objects are immutable, once constructed, cannot be modified.
  • In Python, we use Dictionary or List as a object pool, that stores reference to the object which has already been created.

Instantiating many heavy objects can be one of the very common reasons for the low system performance. This can be rectified by the flyweight pattern successfully.

Flyweight pattern introduces a new object called ‘Flyweight’ to avoid the creation of a large number of heavy and high memory consumption objects.

The Flyweight pattern describes how to share objects to allow their usage. Each “flyweight” object is divided into two pieces: the state-dependent (extrinsic) part, and the state-independent (intrinsic) part.

  • Extrinsic traits — Data(properties and funtionalities) that is unique to each individual object.
  • Intrinsic traits – Data that is common to several objects. 

Intrinsic traits are stored (shared) in the Flyweight object. Extrinsic traits are stored or computed by client objects and passed to the Flyweight when its operations are invoked.

Example:

Let’s say we are working on a video game having Bows and Arrows. Here, each arrow will be a separate object. Arrows have some unique properties such as their coordinates and velocity and also have some shared properties – like shape and texture.

class Arrow:
    def __init__(self, x, y, z, velocity):
        self.x = x
        self.y = y
        self.z = z
        self.velocity = velocity
        self.shape = 'cone'

There can be dozens of arrows firing at the same time in the game. Since each arrow is a separate object it would take up a considerable amount of memory, resulting in low performance.

The Flyweight Pattern calls for a common pool (List or Dictionary) where many instances of an object with the same value could exist. The pool consists of a Flyweight class to store all intrinsic traits and a Context class for extrinsic traits.

Often Flyweight factory method is implemented to check whether an object with the same properties exists or not, If yes factory method returns the existing object(arrow), else it generates a new object.

  • Flyweight class
class ArrowFlyweight:
    def __init__(self):
        self.shape = 'cone'
        self.arrows = []
        
    def arrow_factory(self, x, y, z, velocity):
        arr = []
        for b in self.arrows:
            if b.x==x and b.y==y and b.z==z and b.velocity==velocity:
                arr.append(b)
        if not arr:
            arr = Arrow(x,y,z,velocity)
            self.arrows.append(arr)
        else:
            arr = arr[0]
            
        return arr
  • Context Class
class ArrowContext:
    def __init__(self, x, y, z, velocity):
        self.x = x
        self.y = y
        self.z = z
        self.velocity = velocity

Full implementation will be like this:

class Arrow:
    def __init__(self, x, y, z, velocity):
        self.x = x
        self.y = y
        self.z = z
        self.velocity = velocity
        self.shape = 'cone'

class ArrowContext:
    def __init__(self, x, y, z, velocity):
        self.x = x
        self.y = y
        self.z = z
        self.velocity = velocity
        
class ArrowFlyweight:
    def __init__(self):
        self.shape = 'cone'
        self.arrows = []
        
    def arrow_factory(self, x, y, z, velocity):
        arr = []
        for b in self.arrows:
            if b.x==x and b.y==y and b.z==z and b.velocity==velocity:
                arr.append(b)
        if not arr:
            arr = Arrow(x,y,z,velocity)
            self.arrows.append(arr)
        else:
            arr = arr[0]
            
        return arr
        
    def print_arrows(self):
        print('Arrows:')
        for arrow in self.arrows:
            print(str(arrow.x)+' '+str(arrow.y)+' '+str(arrow.z)+' '+str(arrow.velocity))

 Let’s use the arrow_factory() to instantiate a few arrows and print their values:

obj = ArrowFlyweight()

obj.arrow_factory(1, 1, 1, 1)
obj.arrow_factory(1, 2, 5, 1)
obj.arrow_factory(1, 1, 1, 1)
obj.print_arrows()

Two arrows with the same coordinates(i.e. 1, 1, 1, 1) are getting called.

Output:

Arrows:
1 1 1 1
1 2 5 1

Clearly, similar objects are getting replaced by exting once.

Pros and Cons of Flyweight Design Pattern

Pros

  • Flyweight Pattern reduces RAM usage.
  • Improves overall computer system performance because Flyweight object combines several heavy objects.

Cons

  • Complexity of code may increase since we combining the objects.
  • Flyweight Pattern may result in an unfriendly solution.