Principi SOLID cosa sono i 5 principi della programmazione
S.O.L.I.D. è un acronimo, riferito ai cinque principi di progettazione orientata agli oggetti (OOD o OOP).
Sono delle linee guida che gli sviluppatori possono utilizzare per creare software in modo semplice da gestire, mantenere ed estendere.
La comprensione di questi concetti ti renderà uno sviluppatore migliore e ti consentirà di evitare problemi nella gestione del software.
Cosa significa essere un buon programmatore?
7 minuti
In questo articolo vedremo un esempio pratico di applicazione dei principi solid, e in particolare Single Responsability Principle
I principi solid
I solid principi, sono linee guida per uno sviluppo ottimale del software. Chiunque abbia un po’ di esperienza nella programmazione software, giudica codice software scritto da altri, utilizzando parametri di giudizio basati sul proprio percorso di crescita professionale.
Nel corso della mia carriera professionale, ho conosciuto molti sviluppatori, e ho visto migliaia di righe di codice e quando ho bisogno di valutare l’abilità di uno sviluppatore guardo principalmente due fattori:
- Semplicità nella lettura del codice;
- Quanto è probabile che il loro codice funzioni ed evolverà nel tempo.
Fortunatamente, ci sono alcuni fondamenti o principi che rendono facile essere migliori nella codifica.
l’acronimo S.O.L.I.D. sta per:
S: principio di responsabilità unica
O: principio aperto-chiuso
L: Principio di sostituzione di Liskov
I: Principio di segregazione dell’interfaccia
D: Principio di inversione delle dipendenze
Iniziamo con approfondire una applicazione dei principi solid, il primo
Single Responsibility Principle: primo dei solid principi
Una classe (o modulo) dovrebbe avere un solo motivo per cambiare, per evolversi.
Il concetto di per se è molto semplice, però per raggiungere questa semplicità il percorso di attuazione può essere molto complicato. Una classe dovrebbe avere un solo motivo per cambiare.
Ma perché?
Perché è così importante avere una sola ragione per cambiare?
Ad esempio se ci sono due diversi motivi per cambiare, è concepibile che due diversi team possano lavorare sullo stesso codice per due diversi motivi. Ciascuno dovrà implementare la propria soluzione, che nel caso di un linguaggio compilato (come C ++, C # o Java), potrebbe portare a moduli incompatibili con altri team o altre parti dell’applicazione.
Altro esempio, se si usa un linguaggio interpretato, si potrebbe dover ripetere il test della stessa classe o modulo per motivi diversi. Questo implica più lavoro, tempo e impegno per il controllo qualità.
Andare a identificare l’unica feature che una classe o un modulo dovrebbe avere è molto più complesso che guardare semplicemente una checklist per eseguire i test.
Per approfondire, con esempi, il quarto dei principi solid laravel della segregazione dell’interfaccia, puoi leggere il nostro articolo con esempi: Principio di segregazione dell’interfaccia (ISP), quarto principio S.O.L.I.D.
Vantaggi Singola Responsabilità
Ma proviamo a ragionare da un punto di vista meno tecnico, e cioè proviamo ad analizzare l’utenza della nostra classe o modulo, cioè chi andrà ad utilizzarla. Un aspetto fondamentale che dobbiamo sempre tenere in considerazione, è il fatto che gli utenti dell’applicazione o del sistema che sviluppiamo che sono serviti da un particolare modulo saranno quelli che richiedono modifiche allo stesso. Quelli serviti chiederanno il cambiare la classe o il modulo.
Alcuni esempi di moduli e relativa utenza:
- Modulo manutenzione: l’utenza è composta da amministratori di database e architetti software.
- Modulo di reporting: l’utenza è composta da impiegati, contabili e produzione.
- Modulo di calcolo dei pagamenti per un sistema di gestione buste paga: l’utenza può includere avvocati, manager e contabili.
- Modulo di ricerca testi per un sistema di gestione delle biblioteche: l’utenza può essere rappresentata dal bibliotecario oppure dai visitatori e clienti della biblioteca stessa.
Quindi se il primo passo consiste nel ricercare gli attori o l’attore che ha il ruolo di interlocutore con il modulo, associare persone fisiche a tutti i ruoli può essere difficile. In una piccola azienda una sola persona può svolgere più ruoli mentre in una grande azienda possono esserci più persone che hanno un unico ruolo.
Identificazione dei Ruoli
Sembra più ragionevole identificare i ruoli, invece che le persone o gli utenti.
Quindi:
- l’utenza del sistema software definisce le ragioni del cambiamento;
- una responsabilità è una famiglia di funzioni che soddisfa le esigenze di un particolare attore, cioè dell’utenza del sistema;
- gli attori, l’utenza diventa una fonte di cambiamento per la famiglia di funzionalità che deve soddisfare l’esigenza dell’utente stesso;
- l’evoluzione delle esigenze dell’utenza, guida l’evoluzione delle funzionalità;
Vediamo degli esempi
Supponiamo di avere una classe Libro che incapsula il concetto di un libro e le sue funzionalità.
class Libro {
function getTitolo() {
return "Un Grande Libro";
}
function getAutore() {
return "Alessandro Baricco";
}
function prossimaPagina() {
// pagina successiva
}
function printPaginaCorrente() {
echo "contenuto della pagina corrente";
}
}
Questa è una normalissima classe. Abbiamo un libro, e la classe ci può fornire il titolo, ci può fornire l’autore e può voltare pagina. Infine, è anche in grado di stampare la pagina corrente sullo schermo.
C’è però un piccolo problema.
Ragionando sugli attori coinvolti nella gestione dell’oggetto Libro, chi potrebbero essere?
Possiamo facilmente pensare a due attori diversi qui: Gestione del libro (come il bibliotecario) e Meccanismo di presentazione dei dati (come il modo in cui vogliamo fornire il contenuto all’utente: su schermo, interfaccia utente grafica, interfaccia utente di solo testo, forse stampa) .
Abbiamo quindi due attori molto diversi che interagiscono con la classe.
In poche parole questa classe fa un mix tra:
- la logica aziendale con
- la presentazione
questo può essere un problema perché viola il principio di responsabilità unica (SRP).
Come possiamo modificare, come possiamo migliorare questo codice per rispettare il principio di responsabilità unica ?
Esempio
Per meglio comprendere l’applicazione dei principi solid, dai un’occhiata al codice seguente:
class Libro {
function getTitolo() {
return "Oceano Mare";
}
function getAutore() {
return "Alessandro Baricco";
}
function turnPagina() {
// pagina successiva
}
function getPaginaCorrente() {
echo "contenuto della pagina corrente";
}
}
interface Printer {
function printPage($page);
}
class StampaLibro implements Printer {
function stampaPagine($page) {
echo $page;
}
}
class HtmlPrinter implements Printer {
function stampaPagine($page) {
echo '<div style="single-page">' . $page . '</div>';
}
}
Questo esempio molto semplice mostra come separare la presentazione dalla logica aziendale, e nel rispetto dell’SRP offre grandi vantaggi nella flessibilità del nostro progetto.
Se vuoi vedere alcuni esempi di applicazione dei principi solid laravel, leggi questo nostro articolo: Principi SOLID in Laravel: un esempio completo
Vediamo un altro esempio di applicazione dei principi solid:
Un esempio simile a quello sopra è quando un oggetto può salvare e recuperare se stesso dalla presentazione.
class Libro {
function getTitolo() {
return "Oceano Mare";
}
function getAutore() {
return "Alessandro Baricco";
}
function turnPagina() {
// pagina successiva
}
function getPaginaCorrente() {
return "contenuto della pagina corrente";
}
function salva() {
$nomefile = '/documenti/'. $this->getTitolo(). ' - ' . $this->getAutore();
file_put_contents($nomefile, serialize($this));
}
}
Come prima, anche qui possiamo identificare diversi attori come Gestione del libro (come il bibliotecario) e la Persistenza. Ogni volta che vogliamo cambiare il modo in cui passiamo da una pagina all’altra, dobbiamo modificare questa classe. Possiamo avere diversi motivi di cambiamento.
class Libro {
function getTitolo() {
return "Oceano Mare";
}
function getAutore() {
return "Alessandro Baricco";
}
function turnPagina() {
// pagina successiva
}
function getPaginaCorrente() {
return "contenuto della pagina corrente";
}
}
class SimpleFilePersistence {
function salva(Libro $libro) {
$nomefile = '/documenti/' . $libro->getTitolo() . ' - ' . $book->getAutore();
file_put_contents($nomefile, serialize($book));
}
}
Spostare l’operazione di persistenza in un’altra classe separerà chiaramente le responsabilità e saremo liberi di scambiare metodi di persistenza senza influire sulla nostra classe Libro. Ad esempio, l’implementazione di una classe DatabasePersistence sarebbe banale e la nostra logica aziendale costruita attorno alle operazioni con i libri non cambierà.