🚨 ¡Nueva review! ¡Mi teclado ideal! ⌨️ Perfecto para programar, el Logitech MX Keys S . ¡Échale un ojo! 👀

El Patrón de diseño Flyweight

¡Ahorra memoria compartiendo objetos de forma inteligente!

Escrito por domin el 10 de noviembre de 2025

💾 El Patrón Flyweight: El gran ahorrador de memoria

El Patrón Flyweight (peso ligero) pertenece a la familia de los patrones estructurales y resuelve un problema muy común: ¿Qué haces cuando necesitas muchísimos objetos (cientos!, miles! millones!) que son muy parecidos y tu aplicación comienza a consumir muchísima memoria?

El objetivo principal de Flyweight es minimizar el uso de memoria o los costes de cpu compartiendo tanta información como sea posible entre varios objetos.

La respuesta es no crear copias de las partes idénticas. En su lugar, creamos una única instancia compartida (el Flyweight) y hacemos que todos los objetos que la necesiten, la referencien. Es decir, compartimos la memoria.

💾 1. Un ejemplo sencillo: Las matrículas de los coches

Imagina una fábrica que produce miles de coches:

Concepto (analogía)

Rol en Flyweight

Tarea principal

La imagen del logo de la marca

Estado intrínseco (Flyweight)

La información compartida e inmutable que ahorra memoria.

La matrícula y el GPS

Estado extrínseco

La información única que el cliente pasa al Flyweight.

La fábrica de Flyweights

Factoría del Flyweight

Asegura que solo se cree una instancia de cada Flyweight.

El secreto está en que el Flyweight solo guarda lo que es común. La información única se la tiene que dar el Cliente cuando llama a sus métodos.

🛠️ 2. Los tres pilares del patrón Flyweight

El Flyweight necesita tres roles principales para funcionar correctamente:

1. El Flyweight (El objeto compartido)

3. El cliente

🤔 1. ¿Cuál es el principal objetivo del Patrón Flyweight?

🎮 3. El ejemplo clásico: Un juego con muchos árboles

Imagina un juego de rol (RPG) donde hay miles de árboles. Todos los árboles “Roble” tienen la misma textura de tronco (una imagen de 20MB) y el mismo modelo 3D.

Clase/RolFunción (Ej. dibujar)
Árbol Roble FlyweightGuarda la textura (20MB) y el modelo 3D (Estado Intrínseco).
Cliente (El Motor del Juego)Guarda la posición (X, Y, Z) de cada árbol (Estado Extrínseco).

Clase/Rol

Función (Ej. dibujar)

Flyweight (objeto compartido)

Contiene la lógica de dibujar el Roble, que utiliza los datos compartidos (textura). Recibe la posición única.

Factoría de Flyweight

Asegura que si ya existe un Flyweight “Roble”, lo devuelve sin crear la instancia de 20MB de nuevo.

Si hay 1000 Robles, en lugar de 1000 copias de la textura (20.000 MB), solo hay una (20 MB), y 1000 objetos pequeños que guardan la posición y referencian a esa única textura. ¡Ahorro brutal!

🤔 2. ¿Qué tipo de información debe contener el objeto Flyweight (estado intrínseco)?

✅ 4. ¿Por qué usarlo?

El Flyweight es un patrón de optimización de rendimiento y memoria:

  1. Ahorro de memoria: Es el beneficio clave. Si tienes millones de objetos, este patrón puede hacer que tu aplicación funcione donde antes colapsaba.
  2. Optimización: Al tener menos instancias de objetos, el tiempo de inicialización de la aplicación y la gestión de la memoria por el sistema (recolección de basura) se reducen drásticamente.
  3. Transparencia: El cliente solo se preocupa por qué Flyweight quiere (por ejemplo, “un Roble”), no de cómo se crea o si ya existe. La Factoría se encarga de todo.

❌ 5. Desventaja a considerar

🤔 3. ¿Cuál es el rol de la Factoría del Flyweight?

💡 6. Conclusión

El Patrón Flyweight es una herramienta esencial cuando la escasez de memoria o el alto número de objetos son un problema. Es un patrón de alto nivel que no se usa tan a menudo como el Proxy o el Decorator, pero cuando realmente se necesita es cuando se nota su valor.

Si tienes una colección enorme de objetos con muchas propiedades que se repiten, no lo dudes e implementa el Flyweight para “volar ligero” y ganar rendimiento.

🧠 7. Ejemplo Práctico en PHP

Vamos a implementar el patrón Flyweight con el ejemplo de los árboles en un juego. La información del tipo de árbol (modelo, textura) será el estado intrínseco (Flyweight) y la posición será el estado extrínseco (gestionado por el cliente).

<?php

// 🔹 1. El Flyweight (El Objeto Compartido)
interface TipoDeArbol {
    public function dibujar(int $x, int $y): void;
}

// 🔹 2. El Flyweight Concreto (Estado Intrínseco - la parte compartida)
class ArbolFlyweight implements TipoDeArbol {
    private string $nombre;
    private string $textura; // Simula un objeto pesado, por ejemplo, un archivo de 20MB

    public function __construct(string $nombre, string $textura) {
        $this->nombre = $nombre;
        $this->textura = $textura;
        echo "   [FLYWEIGHT] Creando nuevo objeto: Tipo $this->nombre (Textura: $this->textura). Ahorrando memoria en futuras instancias." . PHP_EOL;
    }

    /**
     * El método del Flyweight que recibe el estado Extrínseco (posición)
     */
    public function dibujar(int $x, int $y): void {
        // Usa el estado intrínseco (textura) y el estado extrínseco (posición)
        echo "   [DIBUJO] Dibujando Arbol $this->nombre (Textura: $this->textura) en Posición ($x, $y)." . PHP_EOL;
    }
}

// 🔹 3. La Factoría del Flyweight (La que asegura la unicidad)
class FactoriaArbol {
    // Almacena las instancias Flyweight existentes, indexadas por el tipo de árbol (la clave).
    private array $flyweights = [];

    /**
     * Devuelve un Flyweight existente o crea uno nuevo si no existe.
     */
    public function getFlyweight(string $nombre, string $textura): TipoDeArbol {
        if (!isset($this->flyweights[$nombre])) {
            // El Flyweight no existe, lo creamos e indexamos.
            $this->flyweights[$nombre] = new ArbolFlyweight($nombre, $textura);
        }
        // El Flyweight ya existe, simplemente lo devolvemos para compartirlo.
        return $this->flyweights[$nombre];
    }

    public function getNumFlyweights(): int {
        return count($this->flyweights);
    }
}

// 🔹 4. Uso del patrón (El Cliente - El Motor del Juego)
$factoria = new FactoriaArbol();

// Creamos 3 Robles. Solo se crea 1 objeto Flyweight.
echo "--- Creando 3 Robles (Mismo Tipo, Diferente Posición) ---" . PHP_EOL;
// La Factoría crea el primer Flyweight 'Roble'
$roble1 = $factoria->getFlyweight('Roble', 'textura_roble.jpg');
$roble1->dibujar(10, 50);

// La Factoría DEVUELVE el mismo Flyweight 'Roble' (¡Ahorro!)
$roble2 = $factoria->getFlyweight('Roble', 'textura_roble.jpg');
$roble2->dibujar(20, 60);

// La Factoría DEVUELVE el mismo Flyweight 'Roble' (¡Ahorro!)
$roble3 = $factoria->getFlyweight('Roble', 'textura_roble.jpg');
$roble3->dibujar(30, 70);

echo PHP_EOL . "--- Creando 2 Abetos (Nuevo Tipo de Arbol) ---" . PHP_EOL;
// La Factoría crea el primer Flyweight 'Abeto'
$abeto1 = $factoria->getFlyweight('Abeto', 'textura_abeto.png');
$abeto1->dibujar(5, 10);

// La Factoría DEVUELVE el mismo Flyweight 'Abeto' (¡Ahorro!)
$abeto2 = $factoria->getFlyweight('Abeto', 'textura_abeto.png');
$abeto2->dibujar(8, 12);


echo PHP_EOL . "--- Resumen de Memoria ---" . PHP_EOL;
echo "Se han solicitado 5 árboles en total (3 Robles, 2 Abetos)." . PHP_EOL;
echo "Número REAL de objetos Flyweight creados y en memoria: " . $factoria->getNumFlyweights() . PHP_EOL;

// 🖥️ Salida del programa:
// --- Creando 3 Robles (Mismo Tipo, Diferente Posición) ---
//    [FLYWEIGHT] Creando nuevo objeto: Tipo Roble (Textura: textura_roble.jpg). Ahorrando memoria en futuras instancias.
//    [DIBUJO] Dibujando Arbol Roble (Textura: textura_roble.jpg) en Posición (10, 50).
//    [DIBUJO] Dibujando Arbol Roble (Textura: textura_roble.jpg) en Posición (20, 60).
//    [DIBUJO] Dibujando Arbol Roble (Textura: textura_roble.jpg) en Posición (30, 70).

// --- Creando 2 Abetos (Nuevo Tipo de Arbol) ---
//    [FLYWEIGHT] Creando nuevo objeto: Tipo Abeto (Textura: textura_abeto.png). Ahorrando memoria en futuras instancias.
//    [DIBUJO] Dibujando Arbol Abeto (Textura: textura_abeto.png) en Posición (5, 10).
//    [DIBUJO] Dibujando Arbol Abeto (Textura: textura_abeto.png) en Posición (8, 12).

// --- Resumen de Memoria ---
// Se han solicitado 5 árboles en total (3 Robles, 2 Abetos).
// Número REAL de objetos Flyweight creados y en memoria: 2

EA, ¡saluditos y nos vemos en los bares! 🍻