Introduzione ai Principi SOLID: Liskov Substitution Principle (LSP)
scritto tempo fa
Il Liskov Substitution Principle (LSP) è uno dei principi SOLID, fondamentali per la programmazione orientata agli oggetti. Introdotto da Barbara Liskov, il principio stabilisce che le classi derivate devono poter sostituire le classi base senza alterare la correttezza del programma. Questo principio promuove un design del codice che favorisca la manutenibilità e l'estensibilità, evitando comportamenti inaspettati o errori.
In questo articolo, esploreremo il significato dell'LSP, vedremo esempi pratici e capiremo come implementarlo in modo corretto. Per chi non avesse letto i primi due principi puoi leggerli qui:
🔑 Concetti chiave
LSP afferma che:
"Se
Sè un sottotipo diT, allora gli oggetti di tipoTdevono poter essere sostituiti con oggetti di tipoSsenza alterare le proprietà desiderabili del programma."
In altre parole, le classi derivate devono rispettare il contratto della classe base.
Perché è importante?
Le violazioni dell'LSP possono portare a:
🧐 Esempio di violazione dell'LSP
Consideriamo un esempio classico con una gerarchia di classi per i quadrilateri:
class Quadrato {
protected $lato;
public function setLato($lunghezza) {
$this->lato = $lunghezza;
}
public function getArea() {
return $this->lato * $this->lato;
}
}
class Rettangolo extends Quadrato {
protected $larghezza;
public function setLato($lunghezza) {
$this->lato = $lunghezza;
$this->larghezza = $lunghezza; // Violazione!
}
public function setLarghezza($larghezza) {
$this->larghezza = $larghezza;
}
public function getArea() {
return $this->lato * $this->larghezza;
}
}
Problema
Questa gerarchia viola l'LSP perché un Rettangolo, derivato da Quadrato, non si comporta come un vero quadrato. Se utilizziamo il setLato per un rettangolo, l'area calcolata potrebbe risultare sbagliata.
Correzione
Dovremmo ridefinire la gerarchia per riflettere correttamente le differenze tra quadrati e rettangoli:
interface Quadrilatero {
public function setDimensioni($larghezza, $altezza);
public function getArea();
}
class Quadrato implements Quadrilatero {
private $lato;
public function setDimensioni($larghezza, $altezza) {
if ($larghezza !== $altezza) {
throw new InvalidArgumentException("Lati di un quadrato devono essere uguali.");
}
$this->lato = $larghezza;
}
public function getArea() {
return $this->lato * $this->lato;
}
}
class Rettangolo implements Quadrilatero {
private $larghezza;
private $altezza;
public function setDimensioni($larghezza, $altezza) {
$this->larghezza = $larghezza;
$this->altezza = $altezza;
}
public function getArea() {
return $this->larghezza * $this->altezza;
}
}
Ora ogni classe rispetta il proprio comportamento specifico senza violare l'LSP.
🎯 Migliorare il codice con l'LSP
Linee guida per rispettare l'LSP
-
Evita la sostituzione impropria: Assicurati che le classi figlie implementino comportamenti compatibili con le classi genitrici.
-
Evita eccezioni non previste: Le classi derivate non devono introdurre eccezioni o comportamenti non definiti che non esistono nella classe base.
-
Scrivi test per la sostituibilità: Usa test automatici per verificare che le classi derivate funzionino correttamente nei contesti della classe base.
Un esempio concreto: Gestione degli ordini
Immaginiamo di avere una classe base Ordine e due classi derivate: OrdineDigitale e OrdineFisico.
abstract class Ordine {
protected $totale;
public function __construct($totale) {
$this->totale = $totale;
}
abstract public function calcolaSpedizione();
}
class OrdineDigitale extends Ordine {
public function calcolaSpedizione() {
return 0; // Nessuna spedizione per prodotti digitali
}
}
class OrdineFisico extends Ordine {
public function calcolaSpedizione() {
return $this->totale > 50 ? 5 : 10; // Costi di spedizione variabili
}
}
Test per verificare l'LSP
Scriviamo un test per confermare che entrambe le classi rispettano l'LSP:
function calcolaCostoSpedizione(Ordine $ordine) {
return $ordine->calcolaSpedizione();
}
$ordineDigitale = new OrdineDigitale(100);
$ordineFisico = new OrdineFisico(100);
echo calcolaCostoSpedizione($ordineDigitale); // Output: 0
echo calcolaCostoSpedizione($ordineFisico); // Output: 5
Entrambi i tipi di ordine funzionano correttamente nel contesto della classe base, rispettando l'LSP.
Il Liskov Substitution Principle è essenziale per progettare sistemi robusti e sostenibili. Implementando classi che rispettano i contratti delle loro classi base, possiamo scrivere codice più modulare, riutilizzabile e meno incline agli errori. L'LSP non è solo una regola teorica, ma una pratica che porta a un codice migliore e più manutenibile.
❓Hai mai riscontrato problemi di violazione dell'LSP nei tuoi progetti? Condividi la tua esperienza nei commenti!
Ricevi le ultime novità
Circa l'autore
Salve il mio nome come avrete notato e Giueppe Alessandro De Blasio e questo è il mio blog, tutto nasce dalla passione per la tecnologia e per il web in generale, ed oggi lo utilizzo come mezzo di condivisione. Spero che la vostra permanenza sul mio blog vi sia utile e che decidiate di diventare miei affezionati lettori.
_1733903623.gif)
 2_1733903666.gif)