Observer Design Pattern

Observer Design Pattern

Un approccio per implementare la reattività tra oggetti in modo decoupled.

Introduzione

L’Observer Design Pattern è un design pattern comportamentale per la gestione degli eventi che definisce una relazione uno-a-molti tra oggetti.
L’ Observer Design Pattern definisce una relazione uno-a-molti tra oggetti: quando l’oggetto osservato (detto Subject) cambia stato, notifica automaticamente tutti i suoi osservatori (detti Observers).

Il pattern è composto da:

  • Subject: l’oggetto osservato, che mantiene una lista degli osservatori e li notifica di eventuali modifiche.
  • Observer: un’interfaccia o classe che deve essere implementata da chi desidera osservare il Subject.

Diagramma UML

Il seguente diagramma mostra la struttura del pattern:

Diagramma UML del pattern Observer

Nota: L’immagine rappresenta le relazioni tra le principali classi e interfacce:
Subject, ConcreteSubject, Observer e ConcreteObserver.

Esempio pratico in PHP

Implementiamo un esempio in PHP che simula un sistema di notifiche con due tipi di eventi: user registration
e order placement, ciascuno con relativi listener.

Codice PHP



// Interfaccia Observer
interface Observer {
    public function update(string $event, mixed $data): void;
}

// Classe Subject
class Subject {
    private array $observers = [];

    public function attach(string $event, Observer $observer): void {
        $this->observers[$event][] = $observer;
    }

    public function detach(string $event, Observer $observer): void {
        if (!isset($this->observers[$event])) return;

        $this->observers[$event] = array_filter(
            $this->observers[$event],
            fn($o) => $o !== $observer
        );
    }

    public function notify(string $event, mixed $data): void {
        if (!isset($this->observers[$event])) return;

        foreach ($this->observers[$event] as $observer) {
            $observer->update($event, $data);
        }
    }
}

// Listener per la registrazione utenti
class UserRegistrationListener implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Notifica ricevuta per evento '$event': Nuovo utente registrato con email {$data['email']}.\\n";
    }
}

// Listener per l'invio di ordini
class OrderPlacementListener implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Notifica ricevuta per evento '$event': Ordine effettuato con ID {$data['orderId']}.\\n";
    }
}

// Simulazione
$subject = new Subject();

// Creazione di listener
$userListener = new UserRegistrationListener();
$orderListener = new OrderPlacementListener();

// Registrazione ai rispettivi eventi
$subject->attach('user_registered', $userListener);
$subject->attach('order_placed', $orderListener);

// Generazione di eventi
$subject->notify('user_registered', ['email' => 'example@example.com']);
$subject->notify('order_placed', ['orderId' => '12345']);
                

Diagramma UML dell’implementazione

Diagramma UML dell'implementazione

Il diagramma sopra rappresenta le classi e le interazioni nel nostro esempio.

Conclusione

L’Observer Design Pattern è un approccio potente per implementare notifiche reattive tra oggetti,
consentendo una separazione delle responsabilità. È ampiamente utilizzato in sistemi di eventi, interfacce grafiche e molto altro.

Riferimenti

Il Decorator Design Pattern in PHP

Il Decorator Design Pattern è un pattern strutturale che permette di aggiungere nuove funzionalità a un oggetto esistente senza modificare il suo codice. Questo pattern è utile quando si desidera estendere la funzionalità di un oggetto in modo dinamico, permettendo così di evitare l’overloading del codice o l’uso di classi troppo complesse.

Invece di modificare il comportamento di una classe direttamente, il Decorator Pattern permette di “decorare” un oggetto con nuove responsabilità, creando una relazione di composizione tra oggetti, dove uno decoratore incapsula l’oggetto originario e ne estende le funzionalità.

Esempio di Implementazione in PHP

In questo esempio, implementeremo una classe base che rappresenta un oggetto semplice e successivamente aggiungeremo dei decoratori per estenderne le funzionalità.

Immaginiamo di avere una classe Pizza e di voler aggiungere vari ingredienti come decoratori (es. formaggio extra, pomodoro, ecc.).

1. Creazione dell’oggetto base

La classe base Pizza rappresenta un oggetto di base che può essere decorato da altri oggetti.


class Pizza {
    public function getDescription(): string {
        return "Pizza base";
    }

    public function cost(): float {
        return 5.00;
    }
}

2. Creazione del decoratore astratto

Il decoratore astratto estende la classe Pizza e rappresenta una base per i decoratori concreti. Ogni decoratore deve implementare i metodi getDescription e cost.


abstract class PizzaDecorator extends Pizza {
    protected $pizza;

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

    abstract public function getDescription(): string;
    abstract public function cost(): float;
}

3. Creazione dei decoratori concreti

Ogni decoratore concreto aggiungerà una funzionalità specifica all’oggetto Pizza originale, come ad esempio aggiungere un ingrediente.


class CheeseDecorator extends PizzaDecorator {
    public function getDescription(): string {
        return $this->pizza->getDescription() . ", con formaggio extra";
    }

    public function cost(): float {
        return $this->pizza->cost() + 2.00;
    }
}

class TomatoDecorator extends PizzaDecorator {
    public function getDescription(): string {
        return $this->pizza->getDescription() . ", con pomodoro";
    }

    public function cost(): float {
        return $this->pizza->cost() + 1.50;
    }
}

4. Utilizzo dei decoratori

Ora possiamo combinare i vari decoratori per creare una pizza personalizzata, aggiungendo funzionalità all’oggetto Pizza originale in modo dinamico.


$pizza = new Pizza();
$pizzaConFormaggio = new CheeseDecorator($pizza);
$pizzaConPomodoro = new TomatoDecorator($pizzaConFormaggio);

echo $pizzaConPomodoro->getDescription();  // Output: Pizza base, con formaggio extra, con pomodoro
echo $pizzaConPomodoro->cost();  // Output: 8.50

Conclusioni

Il Decorator Pattern consente di aggiungere comportamenti o funzionalità a oggetti esistenti in modo flessibile e senza modificare la classe originale. Questo è particolarmente utile quando si vuole evitare una proliferazione di classi per combinare diverse funzionalità. In PHP, l’uso di decoratori consente di estendere facilmente un oggetto senza dover cambiare il suo codice, seguendo il principio di Open/Closed Principle della SOLID.

Riferimenti