Tutorial

SOLID cosa sono i 5 principi della programmazione a oggetti

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?

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 il primo principio SOLID, cioè il

Single Responsibility Principle

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. 

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. 

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 ?

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);

Newsletter sull’Innovazione
Non perderti le notizie più importanti sull'Innovazione. Iscriviti per riceverle via e-mail.

}

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.

Vediamo un altro esempio:

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à.

Continua leggendo il secondo principio Open/Closed —>

Ercole Palmeri

Newsletter sull’Innovazione
Non perderti le notizie più importanti sull'Innovazione. Iscriviti per riceverle via e-mail.

Articoli recenti

Veeam presenta il supporto più completo per il ransomware, dalla protezione alla risposta e al ripristino

Coveware by Veeam continuerà a fornire servizi di risposta agli incidenti di cyber-estorsione. Coveware offrirà funzionalità di forensics e remediation…

23 Aprile 2024

Rivoluzione Verde e Digitale: Come la Manutenzione Predittiva sta Trasformando il Settore Oil & Gas

La manutenzione predittiva sta rivoluzionando il settore dell'oil & gas, con un approccio innovativo e proattivo alla gestione degli impianti.…

22 Aprile 2024

L’autorità antitrust del Regno Unito lancia l’allarme BigTech su GenAI

La CMA del Regno Unito ha lanciato un avvertimento circa il comportamento delle Big Tech sul mercato dell’intelligenza artificiale. La…

18 Aprile 2024

Casa Green: rivoluzione energetica per un futuro sostenibile in Italia

Il Decreto "Case Green", formulato dall'Unione Europea per potenziare l'efficienza energetica degli edifici, ha concluso il suo iter legislativo con…

18 Aprile 2024