El patrón de diseño Flyweight es un patrón estructural que permite minimizar el uso de memoria al compartir tantos datos como sea posible entre objetos similares. Este patrón es ideal cuando una aplicación necesita crear una gran cantidad de objetos casi idénticos y es importante optimizar el uso de recursos, particularmente la memoria.
En este artículo, aprenderás qué es el patrón Flyweight, cómo funciona, cuándo usarlo y cómo implementarlo en PHP con un ejemplo práctico.
El patrón Flyweight se basa en la idea de compartir datos comunes entre objetos para evitar redundancia y ahorrar memoria. En lugar de crear una nueva instancia de un objeto para cada variación, se reutilizan las partes que son iguales. La clave es dividir el estado del objeto en dos categorías:
A continuación, veremos cómo implementar el patrón Flyweight en PHP a través de un ejemplo práctico de un sistema gráfico que maneja árboles en un bosque.
Imaginemos que tenemos que renderizar millones de árboles en un bosque, pero cada árbol comparte ciertos atributos como la textura, el color o la forma de la copa, que no cambian entre instancias. Podemos usar el patrón Flyweight para compartir esos atributos comunes y ahorrar memoria.
Flyweight
El Flyweight representa el estado intrínseco que se comparte entre los objetos. Este será un objeto ArbolFlyweight
que contiene datos como el tipo de árbol, color y textura, que son comunes para varios árboles.
<?php
class ArbolFlyweight {
private $tipo;
private $color;
private $textura;
public function __construct(string $tipo, string $color, string $textura) {
$this->tipo = $tipo;
$this->color = $color;
$this->textura = $textura;
}
// Este método dibuja el árbol usando tanto el estado intrínseco como el extrínseco
public function dibujar(int $x, int $y) {
echo "Dibujando un árbol tipo '{$this->tipo}' de color '{$this->color}' con textura '{$this->textura}' en las coordenadas ({$x}, {$y})\n";
}
}
FlyweightFactory
La fábrica FlyweightFactory
se encarga de gestionar y devolver instancias de ArbolFlyweight
, asegurándose de que las instancias compartidas se reutilicen en lugar de crear nuevas cada vez que se necesitan.
<?php
class ArbolFlyweightFactory {
private $flyweights = [];
// Devuelve un Flyweight (árbol) existente o crea uno nuevo si no está en el caché
public function obtenerFlyweight(string $tipo, string $color, string $textura): ArbolFlyweight {
$clave = $this->getClave($tipo, $color, $textura);
if (!isset($this->flyweights[$clave])) {
echo "Creando un nuevo Flyweight para: {$tipo}, {$color}, {$textura}\n";
$this->flyweights[$clave] = new ArbolFlyweight($tipo, $color, $textura);
}
return $this->flyweights[$clave];
}
// Genera una clave única para identificar un Flyweight existente
private function getClave(string $tipo, string $color, string $textura): string {
return md5($tipo . $color . $textura);
}
}
Contexto
El contexto maneja el estado extrínseco, que no es compartido entre los objetos Flyweight. En este ejemplo, la posición (x, y)
de cada árbol es el estado extrínseco, ya que varía entre las diferentes instancias.
<?php
class ContextoArbol {
private $x;
private $y;
private $flyweight;
public function __construct(int $x, int $y, ArbolFlyweight $flyweight) {
$this->x = $x;
$this->y = $y;
$this->flyweight = $flyweight;
}
// Dibujar el árbol utilizando el Flyweight y el estado extrínseco (coordenadas)
public function dibujar() {
$this->flyweight->dibujar($this->x, $this->y);
}
}
Veamos cómo se usa el patrón Flyweight en este ejemplo, donde se crean y dibujan múltiples árboles en diferentes posiciones, pero reutilizando los Flyweights para optimizar la memoria.
<?php
// Crear la fábrica de Flyweights
$fabrica = new ArbolFlyweightFactory();
// Crear y dibujar árboles con Flyweights
$contextos = [];
$contextos[] = new ContextoArbol(10, 20, $fabrica->obtenerFlyweight("Pino", "Verde", "Rugosa"));
$contextos[] = new ContextoArbol(15, 30, $fabrica->obtenerFlyweight("Pino", "Verde", "Rugosa"));
$contextos[] = new ContextoArbol(50, 80, $fabrica->obtenerFlyweight("Roble", "Marrón", "Lisa"));
$contextos[] = new ContextoArbol(60, 90, $fabrica->obtenerFlyweight("Pino", "Verde", "Rugosa"));
// Dibujar todos los árboles
foreach ($contextos as $contexto) {
$contexto->dibujar();
}
1. FlyweightFactory: La clase ArbolFlyweightFactory
se encarga de crear y almacenar los Flyweights. Si se solicita un Flyweight
con los mismos atributos que uno ya existente, simplemente devuelve el objeto existente en lugar de crear uno nuevo.
2. Flyweight: La clase ArbolFlyweight
representa el estado compartido de los árboles, como el tipo, el color y la textura.
3. Contexto: La clase ContextoArbol
mantiene el estado extrínseco, que es la posición específica del árbol en el bosque, y llama al Flyweight para que se encargue de la representación gráfica.
4. Dibujo: Los objetos ContextoArbol
son los encargados de dibujar los árboles en diferentes posiciones, mientras comparten el estado intrínseco a través de los objetos Flyweight.
El resultado al ejecutar este código sería algo como esto:
Creando un nuevo Flyweight para: Pino, Verde, Rugosa
Dibujando un árbol tipo 'Pino' de color 'Verde' con textura 'Rugosa' en las coordenadas (10, 20)
Dibujando un árbol tipo 'Pino' de color 'Verde' con textura 'Rugosa' en las coordenadas (15, 30)
Creando un nuevo Flyweight para: Roble, Marrón, Lisa
Dibujando un árbol tipo 'Roble' de color 'Marrón' con textura 'Lisa' en las coordenadas (50, 80)
Dibujando un árbol tipo 'Pino' de color 'Verde' con textura 'Rugosa' en las coordenadas (60, 90)
Como puedes ver, el Flyweight
para los árboles de tipo "Pino" con color "Verde" y textura "Rugosa" solo se crea una vez, pero se reutiliza para múltiples instancias de árboles en diferentes coordenadas.
El patrón Flyweight es una solución eficiente cuando necesitas crear un gran número de objetos que comparten la mayor parte de sus datos. Al separar el estado intrínseco (compartido) del extrínseco (no compartido), el patrón permite reutilizar objetos y ahorrar memoria, lo que lo hace ideal para aplicaciones de alto rendimiento que gestionan grandes cantidades de objetos similares.
En este artículo, hemos aprendido cómo implementar el patrón Flyweight en PHP usando un ejemplo de árboles en un bosque, lo que nos permitió optimizar el uso de memoria mediante la reutilización de Flyweights.
Jorge García
Fullstack developer