Design Patterns
Introduzione
Uno degli strumenti più potenti nella risoluzione di problemi complessi sono i design patterns, ossia delle soluzioni standard a problemi tipici.
I principali design patterns |
numerosi vantaggi, tra i quali: riuscire più facilmente ad inserirlo in un contesto di una problematica più ampia, poter comunicare con altri in maniera efficace potendosi riferire ad un concetto condiviso, e non ultimo, avere una freccia in più nella propria faretra di sviluppatore/progettista!
E' quindi importante acquisire dimestichezza con un numero più grande possibile di design patterns per poterli utilizzare nella risoluzione di problemi complessi e/o nel migliorare soluzioni esistenti.
Un altro vantaggio dei design patterns è che essendo oggetto di letteratura, se ne conoscono ampiamente i vantaggi e gli eventuali svantaggi, pertanto si sa già in partenza che tipo di risultati si otterranno, anche in termini di leggibilità, manutenibilità, efficienza.
In questa nota faremo una panoramica sui design patterns, introdurremo il concetto di debito tecnico e dei campanelli di allarme (code smells). In successive note saranno spiegati, sinteticamente, alcuni dei principali design patterns
Caratteristiche
Un design pattern si identifica con:
- un nome
- la classe di problemi che risolve e/o situazioni in cui è consigliato
- uno schema di soluzione generale, eventualmente costituito di oggetti o algoritmi
- vantaggi e svantaggi, in termini di efficienza, occupazione di memoria, manutenibilità etc.
Possiamo suddividere i design patterns in varie classi, tra cui:
- Patterns creazionali (tra cui: abstract factory, Builder, Factory Method, Prototype, Singleton, la lazy instantiation)
- Patterns strutturali (tra cui l'adapter, il Bridge, la Composizione, il Decorator, il Facade, il Proxy, il Flyweight)
- Pattern comportamentali (tra cui la chain of resposibility, il Command, l'Interprete, l'Iteratore, il Mediatore, il Memento, l'Observer, lo State, lo Strategy, il template method, il visitatore
- Pattern di concorrenza
- Altri (Naked Objects, Repository, i Layers, il Data Access Object, i Data Transfer Objects, le Reflection, le Pipes, Model View Controller, Model View ViewModel
Le prime tre classi sono presenti nella "bibbia" dei design patterns, ossia "Design Patterns" Elements of Reusable Object-Oriented Software della famosa "gang of four" Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides
Debito Tecnico
Uno dei vantaggi dei design patterns è quello di contenere o ridurre il debito tecnico (tecnical debt)
Quando si operano modifiche sul codice di un programma complesso, è spesso necessario effettuare una serie di modifiche secondarie su altre parti del programma collegate, inclusa la documentazione, gli unit test, e progressivamente la qualità del codice e delle strutture dati utilizzata degrada, trovandosi ad essere usati in contesti diversi da quelli per cui erano state progettate inizialmente.
Col tempo, se non si gestisce il technical debt, il costo della manutenzione diventa via via più elevato, sino a diventare letteralmente impossibile ed il software non più utilizzabile.
Cause del debito tecnico possono essere:
- Sviluppo di aggiunte e/o migliorie protratte nel tempo, che rendono le strutture iniziali non più adeguate ai nuovi requisiti
- Pressioni commerciali sul tempo di consegna, che prevale sulla completezza e accuratezza delle modifiche da fare (inclusa la documentazione)
- Ritardi nel refactoring: man mano che il debito si accumula è necessario prendere provvedimenti, più si ritardano i provvedimenti e più i costi di refactoring aumentano esponenzialmente
- Mancanza di comprensione del processo da parte del management, che è spesso cieco al concetto di technical debt e non ne comprende le conseguenze
- Mancanza di un framework di test
- Sviluppo del software su rami paralleli, questo amplifica notevolmente il technical debt, venendo a mancare una visione globale sull'uso delle strutture dati e delle funzioni
- Mancanza di collaborazione e condivisione della conoscenza tra chi deve lavorare su moduli condivisi e/o comunicanti
- Ignoranza dei canoni fondamentali sulla scrittura del codice e dei design patterns
L'uso di design patterns, ossia soluzioni note in letteratura ai problemi, senz'altro fa si che le strutture e gli algoritmi utilizzati siano migliori rispetto a soluzioni improvvisate di volta in volta.
Code Smells
I Code Smells sono dei sintomi caratteristici della presenza di debito tecnico e/o altri problemi ancora più gravi. Sono principalmente violazioni dei principi fondamentali della progettazione del software. I code smells non sono propriamente bug ma debolezze che rendono il codice più difficile da comprendere, più costoso da manutenere ed aumentano il rischio di bug.
Alcuni dei principali sintomi sono:
Sintomi a livello di applicazione
- Codice duplicato
- Codice eccessivamente complesso, che potrebbe essere semplificato usando specifici design patterns
- Necessità di cambiare molte parti di codice a seguito di ogni richiesta di modifica, questo è collegato anche all'accoppiamento tra le classi
- Effetti collaterali imprevedibili, questi spesso presenti quando non c'è abbastanza information hiding e non è rispettato il principio di single resposibility
Sintomi a livello di classe
- Classi troppo grandi (le super-classi)
- Classi che dipendono da dettagli implementativi di altre classi, questo capita se si fanno assunzioni su caratteristiche non documentate di come funziona la classe o servizio utilizzati
- Classi che fanno troppo poco
- Complessità ciclomatica elevata: troppo cicli/selettori in una funzione tipicamente indicano che quella funzione andrebbe suddivisa in parti più piccole
- Eccessivo uso di costanti
- Presenza di downclassing: classi astratte che vengono "castate" a tipi derivati per esigenze varie
- Accoppiamento per struttura, quando gruppi di variabili vengono condivise e viaggiano in blocco invece di costituire, ad esempio, un oggetto ben definito
Sintomi a livello di metodo
- Numero eccessivo di parametri
- Metodi troppo lunghi. Idealmente una funzione dovrebbe stare in una pagina.
- Nomi variabile troppo lunghi: sono facili da sbagliare o confondere
- Nomi variabile troppo corti: sono poco significativi
- Funzioni che restituiscono dati che nessuno legge
- Commenti troppo prolissi e poco significativi
- Righe di codice troppo lunghe: una linea di codice dovrebbe essere leggibile senza scorrere la pagina, e dovrebbe essere comprensibile senza bisogno di rileggerla 10 volte
Quando si rileva un accumulo di technical debt, è necessario operare una manutenzione strutturale o refactoring. Ma ne parleremo nei prossimi articoli, in cui esamineremo anche molti design pattern in dettaglio.
Nessun commento:
Posta un commento