Eccoci al secondo articolo di questa serie sui principi di sviluppo SOLID, in questo articolo vedremo per cosa sta e cosa dice il secondo principio definito con la "O" di Open/Closed principle
Open/Closed principle
Objects or entities should be open for extension but closed for modification.
Questo secondo principio afferma che le classi o qualsiasi entità software dovrebbero essere aperte per l'estensione e chiuse per la modifica in questo modo si sarà in grado di aggiungere nuove funzionalità e di estendere una classe senza cambiare il suo comportamento interno.
Fondamentalmente con queesto principio si vuole evitare di introdurre bug ed errori in genere alle funzionalità esistenti a seguito dell'aggiunta di altre funzionalità nella classe.
Facciamo ora un'esempio di cattiva pratica per capire meglio cose intendiamo, andremo a creare una classe ComuncoazioneAnimale che andrà a stampare il verso della classe animale passata, dovremo quindi anche avere le due classi una per ogni tipo di animale.
class Cane
{
public function abbaia()
{
return 'Bau bau bau';
}
}
class Anatra()
{
public function starnazza()
{
return 'Qua qua qua';
}
}
class ComunicazioneAnimale()
{
public function comunica($animale)
{
switch (true) {
case $animale instanceof Cane:
return $animale->abbaia();
case $animale instanceof Anatra:
return $animale->starnazza();
default:
return 'Animale sconosciuto';
}
}
}
$fido = new Cane();
$comunicazione = new ComunicazioneAnimale();
$comunicazione->comunica($fido);
Come vediamo ora il metodo comunica della classe ComunicazioneAnimale riceve come parametro un'istanza della classe Cane o Anatra, esegue un controllo sul tipo di oggetto passato e chiama la relativa funzione che ritornerà il verso dell'animale.
Cosa succede però se a un certo punto dobbiamo aggiungere uno o più animali? dovremo andare a modificare la classe ComunicazioneAnimale perchè gestisca anche questi N casi, questo viola il principio in oggetto di questo articolo perchè dovremo andare appunto a modificare la classe cosa che dobbiamo evitare, come fare dunque?
La risposta sta nell'operare delle astrazioni utilizzando classi astratte e interfacce tramite le quali andremo a separare l'interfaccia di un oggetto dalla sua implementazione, questo ci permetterà di mantenere affidabile e stabile la classe ComunicazioneAnimale perchè ci eviterà di doverla modifcare quando dovremo aggiungere nuovi tipi di animali.
Questo il codice che rispetta il nostro Open/Closed principle
interface Comunica
{
public function parla();
}
class Cane implements Comunica
{
public function parla()
{
return 'Bau bau bau';
}
}
class Anatra implements Comunica
{
public function parla()
{
return 'Qua qua qua';
}
}
class ComunicazioneAnimale()
{
public function comunica(Comunica $animale)
{
return $animale->parla();
}
}
$fido = new Cane();
$comunicazione = new ComunicazioneAnimale();
$comunicazione->comunica($fido);
Come vediamo è stata creata un interfaccia con un metodo parla(), l'interfaccia viene poi implementata dalla classi Cane e Anatra che così devono a loro volta definirlo e ritornare il rispettivo risultato.
Il metodo comunica della classe ComunicazioneAnimale accetta come parametro solo oggetti che implementano l'interfaccia Comunica quindi sappiamo che l'oggetto in questione avrà definito il metodo parla che possiamo chiamare tranquillamente sicuri della sua esistenza.
Facendo in questo modo ora anche se dovessimo aggiungere uno o cento ulteriori animali basterà che implementino l'interfaccia Comunica e potranno essere passati al metodo comunica della classe ComunicazioneAnimale senza alcuna necessità di andare a modificare quest'ultima rispettando così il nostro principio Open/Closed.
Se anche andassimo ad aggiungere metodi alle classi degli animali queste modifche non si rifletteranno sulle classi che usano queste classi in quanto l'interfaccia rimarrà invariata. Le eventuali aggiunte saranno completamente trasparenti alle classi che usano i metodi già definiti precedentemente dell'interfaccia senza causare problemi o conflitti.
Un esempio più concreto potrebbe essere ad esempio la definizione di una scontistica ad un prodotto, definendo una interfaccia comune a tutti i tipi di sconti ('primo acquisto', 'Giorno speciale' ecc).
Questo è tutto per quello che riguarda il secondo principio SOLID, nel prossimo articolo della serie vedremo il principio "Liskov substitution principle" o principio di sostituzione di Liskov.
Happy coding!
Lunga vita e prosperità