Design patterns: Flyweight

data: · 3 min
autore: Mauto
serie: Design Patterns/ parte 12/24

I pattern classici della Gang of Four, uno per puntata, con esempi pratici in PHP.

  1. Design Patterns
  2. Design patterns: Singleton
  3. Design patterns: Factory Method
  4. Design patterns: Abstract Factory
  5. Design patterns: Builder
  6. Design patterns: Prototype
  7. Design patterns: Adapter
  8. Design patterns: Bridge, cos’è e come usarlo (esempio PHP)
  9. Design patterns: Composite
  10. Design patterns: Decorator
  11. Design patterns: Facade, cos’è e come usarlo (esempio PHP)
  12. Design patterns: Flyweight
  13. Design patterns: Proxy
  14. Design patterns: Chain of Responsibility, cos’è (esempio PHP)
  15. Design patterns: Command
  16. Design patterns: Interpreter
  17. Design patterns: Iterator
  18. Design patterns: Mediator
  19. Design patterns: Memento
  20. Design patterns: Observer
  21. Design patterns: State, cos’è e come usarlo (esempio PHP)
  22. Design patterns: Strategy
  23. Design patterns: Template Method
  24. Design patterns: Visitor
tutta la guida >

Il Flyweight Pattern è un pattern strutturale utilizzato per ridurre il consumo di memoria e migliorare l’efficienza nelle applicazioni che devono gestire un gran numero di oggetti simili. L’idea principale è quella di condividere oggetti comuni (flyweight) tra più entità, evitando duplicazioni non necessarie. Gli oggetti “flyweight” immutabili sono condivisi mentre le parti variabili vengono gestite separatamente.

Caratteristiche del Flyweight pattern

  1. Riduzione dell’Uso della Memoria: Permette di ridurre drasticamente la quantità di memoria utilizzata, condividendo tra più entità gli oggetti che presentano proprietà comuni e immutabili.
  2. Efficienza: Il pattern migliora l’efficienza quando vengono creati e gestiti grandi volumi di oggetti, specialmente in contesti in cui gli oggetti hanno solo alcune differenze minori tra loro.
  3. Riuso degli Oggetti: Facilita il riuso di oggetti simili, minimizzando la necessità di ricreare gli stessi dati più volte all’interno del sistema.
  4. Separazione tra Stato Intrinseco ed Estrinseco: Lo stato intrinseco (immutabile e condiviso) è separato dallo stato estrinseco (specifico per l’istanza). Questo consente una gestione più efficiente degli oggetti.
  5. Compromesso tra memoria e complessità: Il risparmio di memoria ha un costo: separare lo stato intrinseco da quello estrinseco e gestire la condivisione tramite una factory aggiunge un po’ di complessità al codice.

Caso d’Uso

Supponiamo di dover gestire una vasta mappa di personaggi di un gioco, dove ogni personaggio ha un’icona ma poche informazioni variabili. Invece di creare una nuova istanza dell’icona per ogni singolo personaggio, possiamo utilizzare il Flyweight Pattern per condividere le icone tra tutti i personaggi simili.

Codice PHP

// Flyweight - Oggetto condiviso
class CharacterIcon {
    private $icon; // Stato intrinseco (condiviso)

    public function __construct($icon) {
        $this->icon = $icon;
    }

    public function render($x, $y) {
        echo "Disegno l'icona '{$this->icon}' in posizione ($x, $y)\n";
    }
}

// Flyweight Factory - Gestisce la condivisione degli oggetti
class CharacterIconFactory {
    private $icons = [];

    public function getIcon($icon) {
        if (!isset($this->icons[$icon])) {
            $this->icons[$icon] = new CharacterIcon($icon);
        }
        return $this->icons[$icon];
    }

    public function countIcons() {
        return count($this->icons);
    }
}

// Client - Usa gli oggetti flyweight con stato estrinseco
class Game {
    private $iconFactory;
    private $characters = [];

    public function __construct() {
        $this->iconFactory = new CharacterIconFactory();
    }

    public function addCharacter($icon, $x, $y) {
        // Flyweight condiviso + stato estrinseco (posizione del personaggio)
        $this->characters[] = [
            'icon' => $this->iconFactory->getIcon($icon),
            'x' => $x,
            'y' => $y,
        ];
    }

    public function render() {
        foreach ($this->characters as $character) {
            $character['icon']->render($character['x'], $character['y']);
        }
        echo "Personaggi: " . count($this->characters) . ", icone condivise (flyweight): " . $this->iconFactory->countIcons() . "\n";
    }
}

// Utilizzo
$game = new Game();
$game->addCharacter('knight', 10, 20);
$game->addCharacter('knight', 15, 25);
$game->addCharacter('dragon', 30, 40);
$game->addCharacter('knight', 50, 60);
$game->render();
/*
Output:
Disegno l'icona 'knight' in posizione (10, 20)
Disegno l'icona 'knight' in posizione (15, 25)
Disegno l'icona 'dragon' in posizione (30, 40)
Disegno l'icona 'knight' in posizione (50, 60)
Personaggi: 4, icone condivise (flyweight): 2
*/

Spiegazione

  • CharacterIcon rappresenta lo stato intrinseco (immutabile) che viene condiviso tra tutti i personaggi dello stesso tipo.
  • CharacterIconFactory gestisce la creazione e condivisione degli oggetti CharacterIcon per evitare di creare duplicati.
  • Game memorizza i personaggi: ognuno ha un riferimento all’icona condivisa e la propria posizione sulla mappa. Lo stato estrinseco (posizione) è variabile e non condiviso.
  • Il metodo render() disegna tutti i personaggi e mostra quante icone ha creato la factory: 4 personaggi condividono solo 2 oggetti flyweight.

Conclusione

Il Flyweight Pattern è ideale quando si ha a che fare con un numero elevato di oggetti simili che condividono molte proprietà comuni, consentendo un risparmio significativo di memoria e migliorando le performance dell’applicazione.

TORNA ALLA GUIDA

Copiato