What is the 'Flyweight' pattern?
Answer from AI
sobes.tech AI
"Flyweight" is a structural design pattern that allows efficient use of a large number of small objects, significantly reducing memory consumption. It is achieved by dividing the object's state into intrinsic (shared among multiple objects and stored within the flyweight) and extrinsic (unique to each specific context and passed from outside). The pattern is used when there are many objects of the same class with nearly identical states.
Key components:
- Flyweight: Interface or abstract class defining methods that can accept external state.
- ConcreteFlyweight: Implementation of Flyweight containing the intrinsic state.
- FlyweightFactory: Creates and manages ConcreteFlyweight objects. It ensures that flyweights with the same intrinsic state are reused rather than recreated.
- Client: Uses Flyweight objects and passes external state to them.
The working principle is that the Client requests a flyweight from the Factory. The Factory searches for an existing flyweight with the required intrinsic state. If found, it returns it; if not, it creates a new one, caches it, and returns it. When using a flyweight, the Client passes the necessary external state as method arguments.
Advantages:
- Reduces memory consumption, especially with a large number of objects.
- Improves performance by reducing overhead for object creation and garbage collection.
Disadvantages:
- Introducing the pattern complicates the code.
- The client must explicitly manage external state.
Example usage: displaying a large number of similar graphical elements in a game or editor. Instead of creating a separate object for each element (with the same texture but different positions), one flyweight object for the texture can be created, and external state (coordinates, rotation) can be passed during rendering.
# Example of flyweight interface
class TextCharacter:
def display(self, position):
pass # Method accepting external state
# Example of a concrete flyweight (internal state - the character itself)
class ConcreteTextCharacter(TextCharacter):
def __init__(self, char):
self._char = char
def display(self, position):
# Using internal and external states (position)
print(f"Displaying character '{self._char}' at position {position}")
# Example of a flyweight factory
class CharacterFactory:
_characters = {} # Cache of flyweights
def get_character(self, char):
if char not in self._characters:
self._characters[char] = ConcreteTextCharacter(char)
print(f"Created new flyweight for character '{char}'")
else:
print(f"Reusing existing flyweight for character '{char}'")
return self._characters[char]
# Client usage example
if __name__ == "__main__":
factory = CharacterFactory()
# Client gets flyweights from the factory and passes external state
char_a1 = factory.get_character('A')
char_a1.display((1, 1))
char_b = factory.get_character('B')
char_b.display((2, 2))
char_a2 = factory.get_character('A') # Reusing existing flyweight
char_a2.display((3, 3))
# All 'A' objects use the same ConcreteTextCharacter instance
print(f"Are a1 and a2 identical? {char_a1 is char_a2}")