PHP: Il Principio di Sostituzione di Liskov (Liskov Substitution Principle, LSP)

PHP: Il Principio di Sostituzione di Liskov (Liskov Substitution Principle, LSP)

Il Principio di Sostituzione di Liskov (Liskov Substitution Principle, LSP) è uno dei cinque principi SOLID della programmazione orientata agli oggetti (OOP). Questo principio, formulato da Barbara Liskov nel 1987, stabilisce le regole che una classe derivata deve seguire per garantire la corretta ereditarietà e l'intercambiabilità degli oggetti all'interno di una gerarchia. In questo articolo, esploreremo il Principio di Sostituzione di Liskov in PHP, concentrandoci su come applicarlo correttamente per sviluppare codice robusto e manutenibile.

Fondamenti del Principio di Sostituzione di Liskov

Il Principio di Sostituzione di Liskov afferma che gli oggetti di una classe derivata devono essere in grado di sostituire gli oggetti della classe base senza influire sul corretto funzionamento del programma. In altre parole, se una classe B è una sottoclasse di una classe A, un oggetto di tipo A può essere sostituito con un oggetto di tipo B senza alterare la coerenza dell'applicazione.

Per rispettare il Liskov Substitution Principle, una classe derivata deve:

  1. Mantenere la stessa firma dei metodi della classe base.
  2. Rispettare il contratto implicito della classe base.
  3. Estendere, ma non modificare, il comportamento della classe base.

Esempio pratico in PHP

Consideriamo un esempio di applicazione pratica del Principio di Sostituzione di Liskov in PHP. Supponiamo di avere una classe Rectangle che rappresenta un rettangolo e una classe Square che estende Rectangle. Un rettangolo ha due proprietà: larghezza e altezza, mentre un quadrato ha un solo lato.


class Rectangle {
    protected $width;
    protected $height;

    public function setWidth($width) {
        $this->width = $width;
    }

    public function setHeight($height) {
        $this->height = $height;
    }

    public function area() {
        return $this->width * $this->height;
    }
}

class Square extends Rectangle {
    public function setWidth($width) {
        $this->width = $width;
        $this->height = $width;
    }

    public function setHeight($height) {
        $this->width = $height;
        $this->height = $height;
    }
}

In questo esempio, la classe Square estende la classe Rectangle. Tuttavia, viola il Principio di Sostituzione di Liskov. Se proviamo a sostituire un oggetto di tipo Rectangle con uno di tipo Square, potremmo ottenere risultati inaspettati. Ad esempio:


function printArea(Rectangle $rectangle) {
    $rectangle->setWidth(5);
    $rectangle->setHeight(4);

    echo "Area: " . $rectangle->area() . PHP_EOL;
}

$rectangle = new Rectangle();
$square = new Square();

printArea($rectangle); // Output: Area: 20
printArea($square);    // Output: Area: 25 (invece di 16)

Il metodo printArea assume che, date le stesse dimensioni di larghezza e altezza, il calcolo dell'area sia corretto. Tuttavia, quando si passa un oggetto di tipo Square, le dimensioni vengono adattate, e l'area calcolata sarà diversa da quella prevista.

Correzione del Principio di Sostituzione di Liskov

Per rispettare il Liskov Substitution Principle, è necessario riconsiderare la gerarchia delle classi. Invece di far sì che Square estenda Rectangle, è preferibile utilizzare una relazione di composizione o creare una gerarchia più adatta. Ad esempio:


interface Shape {
    public function area();
}

class Rectangle implements Shape {
    protected $width;
    protected $height;

    public function setWidth($width) {
        $this->width = $width;
    }

    public function setHeight($height) {
        $this->height = $height;
    }

    public function area() {
        return $this->width * $this->height;
    }
}

class Square implements Shape {
    protected $side;

    public function setSide($side) {
        $this->side = $side;
    }

    public function area() {
        return $this->side * $this->side;
    }
}

In questo modo, entrambe le classi implementano l'interfaccia Shape, garantendo che ciascuna classe possa essere sostituita senza compromettere la corretta esecuzione del programma.


function printArea(Shape $shape) {
    echo "Area: " . $shape->area() . PHP_EOL;
}

$rectangle = new Rectangle();
$rectangle->setWidth(5);
$rectangle->setHeight(4);

$square = new Square();
$square->setSide(4);

printArea($rectangle); // Output: Area: 20
printArea($square);    // Output: Area: 16

Ora, rispettando il Principio di Sostituzione di Liskov, possiamo sostituire gli oggetti di tipo Rectangle e Square senza rischi di comportamenti imprevisti.

Conclusione

Il Principio di Sostituzione di Liskov è cruciale per garantire la corretta progettazione delle gerarchie di classi in un'applicazione orientata agli oggetti. Nel contesto di PHP, rispettare questo principio può prevenire bug e semplificare la manutenzione del codice nel tempo. Assicurarsi che le classi derivate possano essere sostituite senza alterare il comportamento atteso è fondamentale per sviluppare software robusto e flessibile.

Torna su