Volver a la página principal
domingo 13 octubre 2024
2

Cómo implementar el patrón Flyweight en PHP

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.

¿Qué es el patrón Flyweight?

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:

  • Estado intrínseco: Parte inmutable o compartida por muchos objetos (almacenada dentro del objeto Flyweight).
  • Estado extrínseco: Parte mutable o específica de una instancia particular (almacenada fuera del objeto Flyweight y pasada cuando se necesita).

Ventajas del patrón Flyweight

  • Ahorro de memoria: Reduce drásticamente la cantidad de objetos en memoria.
  • Reutilización: Comparte datos comunes entre varios objetos, eliminando duplicaciones.

Cuándo usar el patrón Flyweight

  • Cuando necesitas crear un gran número de objetos similares.
  • Si los objetos contienen partes que pueden ser compartidas y otras que varían.
  • En situaciones donde el uso de memoria es crítico y debe optimizarse.

Implementación del patrón Flyweight en PHP

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.

1. Clase 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";
    }
}

2. Clase 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);
    }
}

3. Clase 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);
    }
}

4. Ejemplo de uso

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();
}

5. Explicación del flujo

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.

Conclusión

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.

Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer