ven 19 aprile 2024 - Lo Sviluppatore  anno VI

SOLID Design Principles

Condividi

La maggior parte degli sviluppatori di software professionisti conoscono le definizioni accademiche di accoppiamento,  coesione e incapsulamento. Nonostante ciò, molti di questi si trovano in difficoltà quando devono mettere in pratica questi principi e trarre quindi i vantaggi da concetti come basso accoppiamento , alta coesione e forte incapsulamento.  In questo articolo ci proponiamo di rendere più chiari questi argomenti, e di come si possa scrivere codice migliore sfruttando quelli che sono i 5  principi di progettazione OOD conosciuti sotto l’acronimo S. O. L. I. D. che danno le linee guida per la progettazione di “buone” classi.

Facciamo “mente locale”

Basso accoppiamento (low coupling)

L’accoppiamento nel contesto dello sviluppo di software è definita come il grado in cui un modulo, una classe, o altro costrutto, è legato direttamente ad altri. Per esempio, il grado di accoppiamento tra due classi può essere visto come la misura di quanto una classe dipende da un’altra. Se una classe è strettamente accoppiata (tight o high coupling) con una o più classi,  vuol dire che  è necessario utilizzare tutte le classi che sono accoppiate quando si desidera utilizzare anche solo una di loro. È possibile ridurre l’accoppiamento mediante la definizione di standard per le connessioni e di interfacce tra le varie parti di un sistema. Questo, in un linguaggio OO, avviene concretamente definendo delle classi astratte e delle interfacce che ben definiscono i punti di comunicazione tra le varie parti.

Alta Coesione (High Cohesion)

La coesione è la misura in cui due o più parti di un sistema sono correlate e come queste lavorano insieme per creare qualcosa di maggior valore rispetto a quello che fa ogni singola parte. In un sistema software siffatto, è ovvio che non è possibile creare un’alta coesione se si usano classi che fanno troppe cose e che hanno quindi molte responsabilità. Questo tipo di classi tendono ad essere autoreferenziali e la coesione con l’esterno diventa difficile da realizzare.

Incapsulamento (encapsulation)

La maggior parte degli sviluppatori definisce l’incapsulamento come un modo di nascondere informazioni,
quindi ad esempio definire metodi pubblici per leggere membri privati di una classe. Questa definizione, tuttavia, spesso porta ad una incompleta comprensione e implementazione dell’incapsulamento, in quanto quest’ultimo non serve solo per nascondere dati, ma anche processi e logica. Un forte incapsulamento è evidenziato dalla possibilità da parte degli sviluppatori di utilizzare una determinata classe o un modulo conoscendo solo la sua interfaccia; lo sviluppatore non conosce, e non deve conoscere, i dettagli implementativi della classe o del modulo.

I principi S.O.L.I.D.

I 5 principi SOLID si focalizzano principalmente sulla gestione delle dipendenze  più che sugli aspetti  strettamente legati alla modelllazione in se. Una buona gestione delle dipendenze porta a codice maggiormente flessibile, robusto e riutilizzabile.

Questi principi furono sperimentati e raccolti per la prima volta in un lavoro scritto da RobertZio Bob” Martin. Per maggiori dettagli vai qui : PrinciplesOfOod

Vediamo adesso l’elenco e una breve descrizione di questi 5 principi che verranno poi analizzati in dettaglio uno per uno.

SRP The Single Responsibility Principle  Una classe dovrebbe avere uno ed unico motivo per cambiare
OCP The Open Closed Principle Una qualsiasi entità software (classe, modulo,  funzione, ecc.) dovrebbe avere meccanismi che permettono di estenderne il comportamento senza apportare modifiche al codice preesistente. Quindi Aperte alle estensioni ma chiuse alle modifiche; da qui il nome Open-Closed.
LSP The Liskov Substitution Principle Le classi derivate devono sempre poter essere sostituite dalle classi da cui queste derivano (superclassi) in maniera trasparente.
ISP The Interface Segregation Principle Una classe client non dovrebbe dipendere da metodi che non usa, e che pertanto è preferibile che le interfacce siano molte, specifiche e piccole (composte da pochi metodi) piuttosto che poche, generali e grandi.
DIP The Dependency Inversion Principle Una classe dovrebbe dipendere da astrazioni e non da concrete e specifiche implementazioni.

 

SRP – Single Responsibility Principle o principio di singola responsabilità

oneIl Principio di singola responsabilità dice che ogni oggetto si deve focalizzare su una singola responsabilità e deve avere quindi un solo motivo per cambiare. In altre parole, ogni oggetto deve eseguire una cosa sola. È possibile applicare questa idea a diversi livelli del software: un metodo deve solo compiere una azione; un oggetto di dominio dovrebbe rappresentare solo una entità all’interno di tale dominio; il livello di presentazione (GUI) dovrebbe essere responsabile della presentazione dei dati; ecc. Questo principio mira a raggiungere i seguenti obiettivi:

  • Oggetti piccoli e concisi: serve ad evitare il problema di classi enormi e monolitiche che sono l’equivalente software di un coltellino Svizzero.
  • Testabilità: se un metodo svolge più di una attività, è più difficile scrivere un test
  • Leggibilità: la lettura di codice breve e conciso è certamente più facile che trovare un senso attraverso un groviglio di “spaghetti code”
  • Manutenzione più semplice

Una responsabilità di una classe, di solito, rappresenta una caratteristica o un entità del dominio, nella vostra applicazione. Se si assegnano molte responsabilità ad una classe c’è una maggiore probabilità che avrete bisogno di cambiarla. Queste responsabilità sono accoppiate insieme nella classe, rendendo ogni responsabilità individuale più difficile da cambiare senza introdurre errori nel resto. In questo contesto è ragionevole associare una responsabilità a un “motivo per cambiare”.

SRP è fortemente legato a quello che viene chiamato Separation of Concerns (SoC) cioè la separazione delle responsabilità. SoC significa sezionare un pezzo di software in distinte caratteristiche che incapsulano l’unico comportamento e i dati che possono essere utilizzati dalle altre classi. La separazione di un programma in piccole parti, ciascuna con la propria responsabilità, aumenta significativamente il riutilizzo del codice, la manutenzione e la testabilità.

Vedendolo da punto di vista della coesione e accoppiamento vediamo che qui si ha un elevato livello di coesione e un basso livello di accoppiamento dovuti alla semplicità e alla singola responsabilità delle classi.

OCP – Open Closed Principle o Principio Aperto-Chiuso

Open_ClosedOra è il momento di passare alla lettera ” O ” che sta per Open-Closed principle (OCP). OCP afferma che le classi dovrebbero essere aperte per l’estensione e chiuse per la modifica. Si dovrebbe essere in grado di aggiungere nuove funzionalità e di estendere una classe senza cambiare il suo comportamento interno . È sempre possibile aggiungere nuovi comportamenti ad una classe ma allo stesso tempo, non deve essere necessario ricompilare tutta l’applicazione solo per fare spazio a cose nuove. L’obiettivo principale di questo principio è quello di evitare di introdurre bug ed errori in genere alle funzionalità esistenti  a seguito dell’aggiunta di altre funzionalità nella classe. Come è possibile fare questo? La chiave del successo è quello di identificare le aree del dominio maggiormente soggette a cambiamenti e operare delle astrazioni con l’utilizzo di classi astratte e interfacce. Con l’utilizzo di astrazioni si separa l’interfaccia di un oggetto dalla sua implementazione. Eventuali cambiamenti di implementazione non si riflettono sulle classi che usano una classe, in quanto l’interfaccia rimane invariata. Eventuali aggiunte si traducono in un estensione dell’interfaccia e relativa implementazione che è trasparente per le classi che usano i “vecchi” metodi dell’interfaccia.

LSP – Liskov Substitution Principle o principio di sostituzione di Liskov

lspDopo aver visto le lettere ‘S‘ e ‘O‘ di SOLID, è il momento di discutere di ciò che la ‘L’ ha da offrire. L sta per Principio di Sostituzione di Liskov (LSP) e afferma che si dovrebbe essere sempre in grado di utilizzare qualsiasi classe derivata al posto di una classe genitore (superclasse) senza apportare alcuna modifica. Il principio garantisce che una classe derivata non influenzi il comportamento della classe padre, vale a dire che se abbiamo, per esempio, una funzione che prende in input la classe base A per operare su di essa, la stessa funzione deve essere in grado di lavorare con la classe B, sottoclasse di A, senza necessariamente conoscere i dettagli della classe B.

Il principio prende il nome da Barbara Liskov, che per prima ha descritto il problema nel 1988.

Altro caso per capire meglio il principio è il seguente:  se una classe di base definisce due metodi astratti  una sua classe derivata deve dare significative implementazioni di entrambi i metodi astratti. Se una classe derivata implementa un metodo astratto lanciando un eccezione del tipo NotImplementedException, perchè probabilmente nel contesto della classe derivata tale metodo non ha senso, allora vuol dire che la classe derivata non potrà completamente sostituire la  sua classe di base e inoltre questo potrebbere essere segno che la classe base non un “vera” classe base o astrazione e che probabilmente va rivista la propria gerarchia delle classi.

Tutti coloro che studiano OOP ad un certo punto hano a che fare con la relazione “E’ un” (IS-A), e quindi con il concetto di classe base e classi derivate: un Cane è un Animale, un Impiegato è un Dipendente che è una Persona, un Auto è un veicolo ecc. LSP affina questa relazione con una cosa del tipo  “può sostituire un”, il che significa che un oggetto è sostituibile con un altro oggetto in tutte le situazioni, senza nessuna eccezione.

ISP – Interface Segregation Principle o principio di segregazione delle interfacce

interfacesQuesto principio afferma che una classe client non dovrebbe dipendere da metodi che non usa, e che pertanto è preferibile che le interfacce siano molte, specifiche e piccole (composte da pochi metodi) piuttosto che poche, generali e grandi. Questo consente a ciascun client di dipendere da un insieme minimo di metodi, ovvero quelli appartenenti alle interfacce che effettivamente usa. In ossequio a questo principio, un oggetto dovrebbe tipicamente implementare numerose interfacce, una per ciascun ruolo che l’oggetto stesso gioca nei diversi contesti o diverse interazioni con altri oggetti.

DIP- Dependency Inversion Principle o principio di inversione delle dipendenze

dependency-injectionIl principio di inversione delle dipendenze aiuta a disaccoppiare il codice in modo tale che le classi dipendano da astrazioni piuttosto che da implementazioni concrete. La Dependency Injection (DI)  è un’applicazione di questo principio, di fatto DI e DIP sono spesso usati per dire la stessa cosa. Una caratteristica fondamentale del DIP è la programmazione per astrazioni:  il “consumo” di classi  avviene mediate l’uso di astrazioni (Interfacce e classi astratte), piuttosto che a  livello di specifiche implementazioni. E’ mediate la DI che vengono fornite le specifiche implementazioni ad una classe client che ne fa uso.
Un altro termine correlato alla DIP è Inversion of Control (IoC), che indica più o meno lo stesso tpo di concetto. Inversion of Control o Dependency Injection è il principale pattern su cui si basa il famoso framework java Spring. Come suggerisce il nome, l’IoC sposta la responsabilità di gestire il ciclo di vita degli oggetti, come ad es. la creazione di oggetti, il setting di dipendenze, ecc dalla applicazione al framework. Vantaggi dell’uso della DI sono una riduzione dell’accoppiamento tra calssi, una maggiore flessibilità nei test, consentendo l’uso di mock object e maggiore flessibilità in quanto è possibile cambiare ad esempio una implementazione poco performante con una più performante in maniera semplice e trasparente per la classi client che ne fanno uso.

Conclusioni

Tutti questi principi di progettazione object oriented consentono di scrivere codice flessibile e in generale migliore. La teoria è il primo passo, ma ciò che è più importante è quello di sviluppare la capacità di sapere quando  applicare questi principi di progettazione e riconoscere se ne stiamo violando qualcuno compromettendo la flessibilità del codice.

Riferimenti

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

 

11 thoughts on “SOLID Design Principles

  1. Mai visto un blah blah tanto nozionistico. Maturate un poco di praticità anglossassone, per piacere. Tanti concetti astratti corredati da foto per cerebrolesi, errori ortografici e neanche l’ombra di un esempio. Alcune frasi, inoltre, fanno capire come chi ha scritto questo inutilissimo pezzo non abbia capito nulla dei principi SOLID. Meglio, ancora una volta, i siti stranieri. Che noia…

    1. Caro Luca mi dispiace che tu non abbia apprezzato l’articolo, magari ti aspettavi qualcosa di diverso e mi scuso degli errori di battitura che ho corretto. Comunque l’articolo è volutamente di taglio teorico e vuole essere un tentativo di schematizzare in maniera concisa argomenti che forse è difficile sintetizzare in poche parole, pertanto non presenta esempi pratici che avrebbero allungato non di poco il pezzo, e poi, da come parli, mi sembri uno che non ha certo bisogno di esempi, visto che sembri così esperto dei principi SOLID. Saluti.

    2. In verità invece questo articolo è ottimo come sintesi. Naturalmente devi avere un po’ di esperienza di programmazione per capire queste cose senza esempi.

      Capisco sia molto semplice e alla portata di tutti con i soliti esempi presenti ovunque, ma per gli esperti come te Luca non dovrebbe essere un problema.

  2. Ciao complimenti per l’articolo e per il sito. Grazie per lo sforzo di racchiudere in un unico punto, in italiano, nozioni e concetti importanti nell’ambito dello sviluppo. Probabilmente non saranno cosi anglosassoni, ma fanno comunque bene visto che gli argomenti presentati non sono noti cosi spesso a molti sviluppatori.

  3. Grazie mille per l articolo che in sintesi racchiude una teoria che difficilmente viene schematizzata in poche righe, per i commenti negativi, dovete solo ringraziare, ci sono numerosi professoroni universitari che per riassumere concetti così importanti gli ci vorrebbe una vita.
    Mitici!

  4. Grazie e complimenti.
    E’ ottimo anche come ripasso per la teoria, che dopo anni di pratica, a volte si tende a dimenticare dandola per scontata.
    Rileggere e ristudiare le definizioni, aiuta a rivedere il proprio lavoro e migliorarlo

  5. Ogni mattina, qualsiasi persona abbia un ide installata, deve prendere questo articolo e rileggerlo almeno per tre anni.
    Non sarà iperdettagliato, ma almeno la base per evitare certe schifezze che si vedono in giro la da a tutti

  6. Articolo, a mio avviso, ben fatto.
    Come già detto in un altro commento, dopo tanti anni con le mani immerse nel codice, alcune nozioni teoriche si danno per scontate.
    Un bel ripassino è sempre gradito.
    Grazie

Lascia un commento

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

Top