Come ragionare per creare il design ottimo a partire dai requisiti di progetti reali
- Informazioni
- A cosa servono i design pattern (estratto del libro)
- Codici
- Indice del libro
Informazioni
Come nasce il libro
Insegno il Corso di Ingegneria del Software all’Università degli Studi di Perugia e dopo diversi anni mi sono accorto che i testi classici sui design pattern sono spesso pensati come manuali.
Ho sempre pensato che per la comprendere velocemente e in maniera efficace un concetto l’approccio a manuale spesso non basta. Per questo ho voluto scrivere un libro che spiegasse il processo per raggiungere un design ottimale, arrivando ad ottenere un pattern come naturale evoluzione del design.
Autore
Ciao, sono Salvatore e trovi tutto su di me sul sito
https://linkedin.com/in/salvatoreromeo
Sviluppo software fin da quando avevo 12 anni. Oggi è il mio lavoro principale. Insegno Ingegneria del Software all’Università degli Studi di Perugia e creo mini corsi di 3 giorni per le aziende che si occupano di sviluppo software. Il più recente è il corso su Angular erogato insieme al Codemotion. Il corso completo è anche disponibile in formato libro su Amazon.
Se vuoi maggiori informazioni su di me contattami pure tramite linkedin o tramite form in basso.
A cosa servono i design pattern
Lo sviluppo del software è una disciplina molto recente. Alcuni accomunano lo sviluppatore ad un artigiano che modella una materia (che nel caso dello sviluppatore vuol dire scrivere del testo) per ottenere un risultato (il sistema software che svolge le sue funzioni).
In quasi tutte le arti per ottenere un ottimo risultato finale occorre che tutte le parti siano state pensate e costruite nel migliore dei modi; nel caso del software può invece succedere qualcosa di singolare: il risultato finale può essere eccellente, anche se le parti del sistema sono state implementate in maniera approssimativa e superficiale.
Se questo non è un problema al momento della prima consegna del sistema, lo diventa subito dopo. Sono rari i casi in cui un software viene scritto e consegnato in maniera definitiva: molto più comune è il caso in cui un software si evolve, vengono aggiunte delle funzionalità, si trasforma per migliorare le prestazioni o i risultati attesi.
E’ proprio quando il software si trasforma e si evolve che cominciano i problemi.
Da questo momento, se le basi del sistema non erano state pensate fin dal principio per essere estese, si inizieranno ad aggiungere pezzi su pezzi, “appiccicare” codici qui e là finché si arriverà all’inevitabile: un sistema ingestibile.
Gli sviluppatori che hanno il compito di evolverlo non sanno più dove intervenire per migliorare una parte del sistema senza coinvolgere il tutto e magari rischiare di compromettere funzionalità precedenti per aggiungerne di nuove.
I design pattern sono una prima risposta a tutte queste problematiche, perché permettono di applicare soluzioni ormai consolidate a problemi che si ripetono nello sviluppo software. E ciò è vero a vari livelli. In questo testo ci occuperemo del livello più basso: le classi base che costituiscono un sistema software.
Cerchiamo di capire meglio questo concetto.
Quando un programmatore inizia a programmare per la prima volta, gli esercizi o gli esempi che esegue mentre sta sviluppando hanno tutti una caratteristica: esiste un input e si deve produrre un output. Per ottenere l’output si scrive il codice opportuno. Quello che si ottiene è quasi sempre un’unica classe con centinaia di righe di codice. Nella migliore delle ipotesi viene aggiunto un metodo solo quando lo stesso gruppo di istruzioni deve essere usato in vari punti del codice.
Immaginiamo ora di progettare un sistema moderno con questo approccio. Le centinaia di righe di codice diventerebbero presto migliaia.
Lo sviluppatore alle prime armi potrebbe ancora non vedere il problema: in fondo ha scritto personalmente tutto il codice e sa esattamente dove intervenire per modificare parti del sistema.
Supponiamo però di essere in una situazione differente: stiamo lavorando con un gruppo di persone, in un team, e un nostro collega ha scritto un sistema completo, usando la stessa filosofia di progettazione: migliaia di righe di codice in un unico file.
Immaginiamo quindi di dover capire dove intervenire per estendere il sistema del nostro collega. Sarebbe altrettanto facile? Quando non si conoscono i nomi delle funzioni, le istruzioni usate per arrivare allo scopo, un if pensato in un modo quando il nostro modo di ragionare ci avrebbe portato ad implementarlo in un altro modo. Con tutte queste varianti, siamo sicuri di essere ancora in grado di estendere il sistema altrettanto agevolmente come nel caso del sistema sviluppato personalmente?
Torniamo per un attimo al sistema scritto da noi. Supponiamo ora di non intervenire sul sistema per due mesi. In questi due mesi abbiamo continuato a programmare e abbiamo sviluppato altri sistemi. Magari il nostro modo di sviluppare è anche migliorato. Se ora andiamo a riprendere il codice di due mesi prima, siamo sicuri di comprenderlo e ricordarlo come allora?
Vi assicuro che l’esperienza è purtroppo identica al caso di intervenire sul codice di un’altra persona, se non peggio.
E’ importante quindi scrivere codice pensando sempre che domani un’altra persona possa dover intervenire al posto nostro.
E’ importante scrivere codice ordinato, sintetico, pulito e manutenibile.
Cosa possono fare i design pattern per noi? la cosa più significativa è che ci possono aiutare a scrivere codice che non dovrà mai più essere modificato quando ci sarà la necessità di:
- aggiungere funzionalità;
- migliorare le prestazioni delle funzionalità esistenti;
- eliminare funzionalità precedenti perché obsolete.
In tutti questi casi, se non dobbiamo intervenire sul codice esistente, sarà bassissimo il rischio di creare sistemi ingestibili e fragili.
Progettare un buon design consisterà quindi nel trovare il giusto compromesso tra due obiettivi:
- scrivere codice semplice e sintetico
- scrivere codice manutenibile (cioè che possa evolversi con pochissime modifiche al codice esistente)
Quando il codice è troppo semplice e scritto esclusivamente per implementare una funzione, il rischio è di aver progettato un design poco manutenibile; quando creiamo un design troppo complicato forzando i pattern usati, sicuramente abbiamo compromesso la semplicità. Quando avremo trovato il giusto equilibrio tra i due punti sopra avremo sicuramente ottenuto un buon design.
I design pattern, in questo senso, sono soluzioni che rispettano appieno questo equilibrio: come vedremo, sono la massima espressione della manutenibilità per la progettazione delle classi e al contempo sono semplici, eleganti e minimali. In più possono essere applicati in molte situazioni. Da qui il nome pattern, cioè uno schema che si presta bene a risolvere un problema ricorrente.
Codici
Tutti i codici presenti nel testo sono anche pubblicati su GitHub all’indirizzo
https://github.com/devexp-io/codici-design-pattern-typescript/tree/master/src
Sommario
Indice 5
Autore 10
Introduzione 12
Prerequisiti 13
A cosa servono i design patterns 15
Dependency Injection: comprendere l’utilità delle interfacce e del polimorfismo 20
Interfacce e polimorfismo 21
Dependency Injection 26
Cosa imparerai con l’esperienza 36
Template Method: progettare un componente con un metodo personalizzabile 39
Oltre il design pattern 47
Cosa imparerai con l’esperienza 48
Codice per filtrare in base alla data 49
Strategy: diverse implementazioni dello stesso concetto astratto 52
Strategy 54
Nota per il linguaggio Typescript 58
Oltre il pattern 58
Cosa imparerai con l’esperienza 59
Observer: aggiungere comportamenti dinamici ad un sistema in esecuzione 64
Oltre il pattern 74
Cosa imparerai con l’esperienza 75
Nota sui diagrammi 75
Command: come trasformare un dato o ripristinare sue versioni precedenti 77
Nota 87
Un’implementazione generica di annullamento 87
Una variante del command per risparmiare memoria 89
Oltre il pattern 91
Cosa imparerai con l’esperienza 91
State: assegnare un comportamento ad un sistema in base al suo stato 93
Una variante dello state in cui le transizioni sono dinamiche 100
Oltre il pattern 102
Cosa imparerai con l’esperienza 102
Composite: come implementare strutture gerarchiche 105
Variante del composite che distingue le foglie dai nodi 111
Oltre il pattern 116
Cosa imparerai con l’esperienza 116
Factory Method: fornire implementazioni diverse della stessa interfaccia 118
Oltre il pattern 124
Cosa imparerai con l’esperienza 124
Singleton: usare sempre e solo una singola istanza di una classe 128
Oltre il pattern 132
Cosa imparerai con l’esperienza 132
Proxy: estendere un’operazione con istruzioni prima e dopo la sua esecuzione 134
Oltre il pattern 139
Cosa imparerai con l’esperienza 139
Adapter: come adattare un’interfaccia di un sistema esterno ad un’interfaccia del nostro sistema 140
Decorator: ampliare il comportamento di un oggetto con funzioni extra 142
MVC: come strutturare un progetto 149
Model 150
Controller 151
View 151
Esercitazione MVC 152
Oltre il pattern 154
Cosa imparerai con l’esperienza 155
Design di sistemi reali: progetto di un gestionale per un ristorante 157
Architettura del sistema: modello 158
Ristorante 159
Aree, stanze e tavoli 160
Operazioni sui tavoli 165
Occupazione di un tavolo 168
Conclusioni 173
Design di sistemi reali: progetto di un robot 175
Modellazione del robot 176
Modellazione delle azioni 177
Rendere le operazioni annullabili 181
Modellazione dello stato del robot 183
Conclusioni 187
Conclusioni e contatti 189
Appendice 1 – Node e NPM 192
Esigenza 192
Node e NPM 193
Installare Node e NPM 193
Problemi durante l’installazione 196
Esercitazione 197
Passo 1: Installiamo una libreria con NPM: moment 197
Passo 2: Creiamo un file index.html 198
Passo 3: Visualizziamo il risultato sul browser 199
Ricapitolando 201
Cosa imparerai con l’esperienza 201
Conclusioni 202
Appendice 2 – Typescript: installazione, sintassi ed esempi 204
Perché TypeScript 204
Typescript 205
Variabili e oggetti JSON 207
Visualizzare i risultati di una espressione usando console.log 208
Tipi 210
Array e for 211
Ordinamento semplice 212
Ordinamento di oggetti json, filtro e trasformazione 212
Funzioni autonome 213
Classi 214
Interfacce 216
Come gestire librerie esistenti 217
Esercitazione 219
Installiamo Typescript 219
Installiamo WebStorm (o VisualStudio Code) 220
Esercitazione array, classi, interfacce e funzioni 222
Ricapitolando 227
Cosa imparerai con l’esperienza 227