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.
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.