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
- Flyweight: Define una interfaz a través de la cual los objetos Flyweight pueden recibir datos extrínsecos.
- ConcreteFlyweight: Implementa la interfaz Flyweight y almacena el estado intrínseco.
- 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
- Flyweight Interface: Define el método
render
que toma el estado extrínseco. - ConcreteFlyweight: Implementa la interfaz Flyweight y almacena el estado intrínseco (el carácter).
- 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.
- 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.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
Módulo 3: Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Diseño en Desarrollo Ágil