Skip to content

Flyweight

Let's assume that the application we are working on is installed directly on the client's server. After some time, the client informs us that the application stops working over time due to insufficient RAM available. After analyzing the problem, it turns out that there are many (very many) objects of the same type in the memory.

Design

In order to solve the problem described above, to limit the application memory, we can use the Flyweight structural pattern. Having a large number of objects of the same type, we can try to isolate the common part (or several) of these objects.

Then we modify each of the original objects as follows:

  • instead of having a separate copy of the "common" fields, it has a reference to the object that represents that common part.

flyweight

Example

The pattern construction does not impose the method of creating the object, and the example uses a simple factory. The following example creates 1000 objects of type Car. Each of these objects has several fields (unique to each car, such as VIN), including an engine field of type Engine, which has been extracted as a repeating intersection of many cars. The Flyweight pattern was used for this field. We only create 4 Engine objects for 1000 Car objects.

class Engine:
    instances = 0

    def __init__(self, identifier, volume, engine_type):
        Engine.instances += 1
        self._identifier = identifier
        self._volume = volume
        self._engine_type = engine_type
class Car:
    def __init__(self, producer, vin, model_name, engine):
        self._producer = producer
        self._vin = vin
        self._model_name = model_name
        self._engine = engine
class CarFactory:
    engines = [Engine('polo', 1.6, 'DIESEL'),
               Engine('poloGTI', 2.0, 'GASOLINE'),
               Engine('golf', 1.5, 'GASOLINE'),
               Engine('e', 0.0, 'ELECTRIC')]

    @staticmethod
    def create_vw_polo(vin):
        return Car('VW', vin, 'Polo', CarFactory.engines[0])

    @staticmethod
    def create_vw_polo_gti(vin):
        return Car('VW', vin, 'Polo GTI', CarFactory.engines[1])

    @staticmethod
    def create_vw_golf(vin):
        return Car('VW', vin, 'Golf', CarFactory.engines[2])

    @staticmethod
    def create_skoda_citigo(vin):
        return Car('Skoda', vin, 'CityGO', CarFactory.engines[3])
def main():
    produced_cars = []
    for i in range(1000):
        engine_type = random.randint(0, 3)
        if engine_type == 0:
            produced_cars.append(CarFactory.create_vw_polo(random.randint(1000000000, 9999999999)))
        elif engine_type == 1:
            produced_cars.append(CarFactory.create_vw_polo_gti(random.randint(1000000000, 9999999999)))
        elif engine_type == 2:
            produced_cars.append(CarFactory.create_vw_golf(random.randint(1000000000, 9999999999)))
        else:
            produced_cars.append(CarFactory.create_skoda_citigo(random.randint(1000000000, 9999999999)))

    print(f"I created {len(produced_cars)}, but only {Engine.instances} references to Engine objects")


if __name__ == '__main__':
    main()

When using Flyweight, it is important to remember that by changing the field value of a common object, we are changing it in many objects.