Introducción

El patrón Flyweight es un patrón estructural que se utiliza para minimizar el uso de memoria al compartir la mayor cantidad de datos posible con objetos similares. Este patrón es especialmente útil cuando se necesita crear un gran número de objetos similares, ya que permite reducir significativamente el consumo de memoria.

Objetivos del Patrón Flyweight

  • Reducir el uso de memoria: Compartir datos comunes entre múltiples objetos para minimizar el consumo de memoria.
  • Mejorar el rendimiento: Reducir la sobrecarga de memoria puede mejorar el rendimiento de la aplicación.

Conceptos Clave

Estado Intrínseco vs. Estado Extrínseco

  • Estado Intrínseco: Parte del estado del objeto que es compartida entre todos los objetos similares. Este estado es inmutable y se almacena en el objeto Flyweight.
  • Estado Extrínseco: Parte del estado del objeto que varía entre los objetos. Este estado no se almacena en el objeto Flyweight y se pasa al método que lo utiliza.

Estructura del Patrón Flyweight

  1. Flyweight: Define una interfaz a través de la cual los objetos Flyweight pueden recibir datos extrínsecos.
  2. ConcreteFlyweight: Implementa la interfaz Flyweight y almacena el estado intrínseco.
  3. FlyweightFactory: Crea y gestiona los objetos Flyweight. Asegura que los objetos Flyweight se compartan adecuadamente.

Ejemplo Práctico

Imaginemos que estamos desarrollando un editor de texto que necesita manejar una gran cantidad de caracteres. En lugar de crear un objeto para cada carácter, podemos utilizar el patrón Flyweight para compartir los objetos de caracteres comunes.

Implementación en Python

Flyweight Interface

from abc import ABC, abstractmethod

class Flyweight(ABC):
    @abstractmethod
    def render(self, extrinsic_state):
        pass

ConcreteFlyweight

class CharacterFlyweight(Flyweight):
    def __init__(self, intrinsic_state):
        self.intrinsic_state = intrinsic_state

    def render(self, extrinsic_state):
        print(f"Character: {self.intrinsic_state}, Position: {extrinsic_state}")

FlyweightFactory

class FlyweightFactory:
    def __init__(self):
        self.flyweights = {}

    def get_flyweight(self, intrinsic_state):
        if intrinsic_state not in self.flyweights:
            self.flyweights[intrinsic_state] = CharacterFlyweight(intrinsic_state)
        return self.flyweights[intrinsic_state]

Uso del Patrón Flyweight

if __name__ == "__main__":
    factory = FlyweightFactory()

    # Crear y usar flyweights
    flyweight_a = factory.get_flyweight('A')
    flyweight_a.render(1)

    flyweight_b = factory.get_flyweight('B')
    flyweight_b.render(2)

    # Reutilizar flyweight existente
    flyweight_a2 = factory.get_flyweight('A')
    flyweight_a2.render(3)

    # Verificar que se reutiliza el mismo objeto
    print(flyweight_a is flyweight_a2)  # True

Explicación del Código

  1. Flyweight Interface: Define el método render que toma el estado extrínseco.
  2. ConcreteFlyweight: Implementa la interfaz Flyweight y almacena el estado intrínseco (el carácter).
  3. FlyweightFactory: Gestiona la creación y el almacenamiento de objetos Flyweight. Si un objeto Flyweight con el estado intrínseco solicitado ya existe, lo reutiliza.
  4. Uso del Patrón Flyweight: Se crean y utilizan objetos Flyweight para los caracteres 'A' y 'B'. Se demuestra que el objeto Flyweight para 'A' se reutiliza.

Ejercicio Práctico

Ejercicio

Implementa un sistema de gestión de árboles en un bosque utilizando el patrón Flyweight. Cada árbol tiene un tipo (estado intrínseco) y una posición (estado extrínseco).

Solución

class TreeFlyweight(Flyweight):
    def __init__(self, tree_type):
        self.tree_type = tree_type

    def render(self, position):
        print(f"Tree type: {self.tree_type}, Position: {position}")

class TreeFactory:
    def __init__(self):
        self.trees = {}

    def get_tree(self, tree_type):
        if tree_type not in self.trees:
            self.trees[tree_type] = TreeFlyweight(tree_type)
        return self.trees[tree_type]

if __name__ == "__main__":
    tree_factory = TreeFactory()

    # Crear y usar flyweights
    oak_tree = tree_factory.get_tree('Oak')
    oak_tree.render((10, 20))

    pine_tree = tree_factory.get_tree('Pine')
    pine_tree.render((15, 25))

    # Reutilizar flyweight existente
    oak_tree2 = tree_factory.get_tree('Oak')
    oak_tree2.render((30, 40))

    # Verificar que se reutiliza el mismo objeto
    print(oak_tree is oak_tree2)  # True

Retroalimentación

  • Error Común: No diferenciar entre estado intrínseco y extrínseco. Asegúrate de que el estado intrínseco es compartido y el extrínseco es pasado en tiempo de ejecución.
  • Consejo: Utiliza un FlyweightFactory para gestionar la creación y reutilización de objetos Flyweight.

Conclusión

El patrón Flyweight es una poderosa herramienta para optimizar el uso de memoria en aplicaciones que manejan un gran número de objetos similares. Al compartir el estado intrínseco y gestionar adecuadamente el estado extrínseco, se puede reducir significativamente el consumo de memoria y mejorar el rendimiento de la aplicación.

En el siguiente módulo, exploraremos el patrón Proxy, que proporciona una forma de controlar el acceso a objetos.

© Copyright 2024. Todos los derechos reservados