Problema
Minimizar el uso de memoria cuando se necesita crear gran cantidad de objetos similares que comparten características comunes.
Propósito
Compartir objetos para ahorrar memoria. En lugar de crear 1000 objetos idénticos, crea 1 objeto y compártelo 1000 veces.
Concepto clave
Compartir lo común, separar lo único: Los datos que no cambian (intrínsecos) se guardan en el flyweight compartido. Los datos que sí cambian (extrínsecos) se pasan como parámetros.
Casos de uso comunes
- Juegos: 1000 balas del mismo tipo, solo cambia posición
- Texto: Miles de letras ‘A’, solo cambia posición y color
- Árboles: Bosque con 500 robles, solo cambia posición y tamaño
- Iconos: Mismos iconos en diferentes lugares de la UI
- Partículas: Efectos de fuego, humo, lluvia
¿Quién es quién en Flyweight?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Flyweight | Interfaz que define operation(extrinsicState) |
TreeFlyweight - define cómo renderizar |
“Molde” (interfaz) |
ConcreteFlyweight | Objeto compartido con estado intrínseco | TreeType - almacena nombre y sprite (inmutable) |
Molde de galleta (se reutiliza) |
FlyweightFactory | Administrador que crea y reutiliza | TreeFactory - crea y reutiliza TreeTypes |
Cocinero (reutiliza moldes) |
Context | Objeto que usa flyweight + estado extrínseco | Tree - posición, tamaño (variable) |
Galleta individual (posición, decoración) |
Diagrama
classDiagram
namespace FlyweightPattern {
class Flyweight {
<<interface>>
+operation(extrinsicState)
}
class ConcreteFlyweight {
-intrinsicState
+operation(extrinsicState)
}
class FlyweightFactory {
-flyweights: Map
+getFlyweight(key) Flyweight
}
class Context {
-extrinsicState
-flyweight: Flyweight
+operation()
}
}
Flyweight <|.. ConcreteFlyweight
FlyweightFactory --> Flyweight
Context --> Flyweight
Ejemplo práctico
classDiagram
namespace TreeExample {
class TreeFlyweight {
<<interface>>
+render(x, y, size)
}
class TreeType {
-name: String
-sprite: Image
+render(x, y, size)
}
class TreeFactory {
-flyweights: Map
-getTreeType(name) TreeFlyweight
+createTree(x, y, size, typeName) Tree
}
class Tree {
-x: int
-y: int
-size: int
-flyweight: TreeFlyweight
+render()
}
}
TreeFlyweight <|.. TreeType
TreeFactory --> TreeFlyweight
TreeFactory --> Tree
Tree --> TreeFlyweight
Flujo de operación
sequenceDiagram
participant Client
participant Factory as TreeFactory
participant Flyweight as TreeType
participant Tree1 as Tree #1
participant Tree2 as Tree #2
Client->>Factory: createTree(10, 20, 5, "Roble")
Factory->>Factory: getTreeType("Roble")
Note over Factory: Crea nuevo TreeType("Roble")
Factory->>Flyweight: new TreeType("Roble", sprite)
Factory->>Tree1: new Tree(10, 20, 5, flyweight)
Factory-->>Client: Tree #1
Client->>Factory: createTree(15, 25, 6, "Roble")
Factory->>Factory: getTreeType("Roble")
Note over Factory: Reutiliza TreeType existente
Factory->>Tree2: new Tree(15, 25, 6, flyweight)
Factory-->>Client: Tree #2
Client->>Tree1: render()
Tree1->>Flyweight: render(10, 20, 5)
Note over Flyweight: Usa datos intrínsecos + extrínsecos
Flyweight-->>Client: árbol renderizado
Client->>Tree2: render()
Tree2->>Flyweight: render(15, 25, 6)
Note over Flyweight: MISMO flyweight, datos diferentes
Flyweight-->>Client: árbol renderizado
Ahorro de memoria
graph LR
subgraph "Sin Flyweight"
A1["Tree #1<br/>name='Roble'<br/>sprite=roble.png<br/>x=10, y=20, size=5"]
A2["Tree #2<br/>name='Roble'<br/>sprite=roble.png<br/>x=15, y=25, size=6"]
A3["Tree #3<br/>name='Roble'<br/>sprite=roble.png<br/>x=20, y=30, size=4"]
end
subgraph "Con Flyweight"
F["TreeType<br/>name='Roble'<br/>sprite=roble.png<br/>(COMPARTIDO)"]
T1["Tree #1<br/>x=10, y=20, size=5"]
T2["Tree #2<br/>x=15, y=25, size=6"]
T3["Tree #3<br/>x=20, y=30, size=4"]
T1 --> F
T2 --> F
T3 --> F
end
Ventajas
- Memoria: Reduce significativamente el uso de memoria
- Performance: Menos objetos = menos presión en el Garbage Collector
- Escalabilidad: Permite manejar gran cantidad de objetos
- Compartición: Reutiliza objetos inmutables eficientemente
Desventajas
- Complejidad: Separar estado intrínseco del extrínseco puede ser complejo
- Cálculos: Puede introducir overhead de cálculos para el estado extrínseco
- Referencias circulares: Problemas con objetos que se referencian mutuamente
- Inmutabilidad: Los flyweights deben ser inmutables
Cuándo usar
- Necesitas crear gran cantidad de objetos similares
- El costo de almacenamiento es alto debido a la cantidad de objetos
- El estado extrínseco puede ser calculado o pasado como parámetro
- Los objetos pueden ser agrupados por características compartidas
Cuándo NO usar
- Tienes pocos objetos
- Los objetos no comparten características significativas
- El estado extrínseco es difícil de separar o calcular
- La aplicación no tiene restricciones de memoria