clean code
Redesign
La produttività degli sviluppatori su un progetto sta calando.
A causa del disordine aggiungere nuove funzionalità diventa sempre più complicato.

Redesign
I manager tentano di aggiungere sviluppatori e fanno pressione per le scadenze da rispettare.
A causa della necessità di sviluppare più rapidamente aumenta il disordine.

Redesign
Si pensa quindi di fare un redesign del progetto.
Bisogna assegnare a un team il lavoro di sviluppare daccapo il progetto, il quale deve includere ogni cambiamento fatto nel frattempo sul vecchio progetto.
Il nuovo progetto non prenderà il posto di quello vecchio finché non fornirà tutti i servizi già esistenti.
Redesign
Si ha quindi il problema dell'allineamento tra i due progetti e il costo di dover mantenerli entrambi.
Ma se avete un problema che nessuno può risolvere – e se riuscite a trovarli – forse potrete ingaggiare il famoso...
Redesign

Redesign
Su un progetto di grandi dimensioni questo processo può continuare per anni.
Anche qui inizia a farsi pesante il disordine.
Col tempo i componenti originali del team vengono sostituiti.
I nuovi componenti iniziano a richiedere un redesign.
Redesign
Diventa necessario fare un redesign del redesign.
Creazione di un ulteriore nuovo team...
Redesign
E così via...
Disordine
Spesso si ha a che fare con scadenze, cambi di rotta, nuove richieste dal cliente e dai manager.
Sviluppatore: "Se il codice è sporco e disordinato è colpa delle pressioni che subisco dai manager..."
Disordine
SBAGLIATO!
I manager fanno pressioni sulle scadenze, ma perché è il loro lavoro difenderle.
Il lavoro del programmatore è difendere il codice.
Disordine
Scrivere codice veloce e sporco può sembrare vantaggioso, soprattutto con una scadenza incombente.
La rapidità con cui vengono implementati i requisiti inizialmente è molto elevata.
Disordine
Inevitabilmente il disordine porta ad un rallentamento esponenziale.
In questo modo facilmente la scadenza non verrà rispettata.
Disordine
L'unico modo per sapere se il lavoro verrà terminato entro la scadenza è mantenere il codice più pulito possibile.
Clean Code
Ma com'è fatto il codice pulito?
Clean Code
Clean code never obscures the designer's intent
Grady Booch
Clean Code
You call it beautiful code when the code also makes it look like the language was made for the problem
Ward Cunningham
Clean Code
Quali sono le regole e le pratiche per scrivere codice pulito?
Clean Code
I suggerimenti proposti in seguito vanno accompagnati da una regola fondamentale per funzionare al meglio:
CARE
Naming
AVOID DISINFORMATION
public static boolean[] calculate(int n) {
boolean[] p = new boolean[n];
Arrays.fill(p, true);
for (int i = 2; i <= n; i++)
if (p[i - 1])
for (int m = i * 2; m <= p.length; m += i)
p[m - 1] = false;
return p;
}
Naming
AVOID DISINFORMATION
public static boolean[] primesAsBooleanArray(int limit) {
boolean[] isPrime = new boolean[limit];
Arrays.fill(isPrime, true);
for (int number = 2; number <= limit; number++) {
if (isPrime[number - 1]) {
for (int multiple = number * 2;
multiple <= isPrime.length;
multiple += number) {
isPrime[multiple - 1] = false;
}
}
}
return isPrime;
}
Naming
Evitare abbreviazioni, nomi di variabili monolettera (storicamente riservati agli indici)
int p; //position, point, pointer, prime, ..?
double[] ic; //WAT?
List<String> nsba;
//Oh yeah that is "name-surname-birth-address" for sure!
Naming
Chiamare i metodi con un nome che riassuma tutto quello che fanno
Soprattutto evitare fraintendimenti
if (account.set("username","John")) { ... }
- Aggettivo: l'attributo "username" di account è impostato a "John"?
- Verbo: imposta l'attributo "username" di account a "John" e verifica se l'operazione è stata effettuata?
Naming
Evitare nomi dipendenti da un contesto non esplicito
public class HtmlGrammarNazi implements HtmlParser { ... }
Grammar nazi -> strict grammar rules -> HTML strict
Naming
Scegliere nomi presi dal dominio del problema
Chiunque abbia conoscenza del problema può leggere il codice più agevolmente aspettandosi che classi e metodi con determinati nomi rappresentino concetti ben noti
Naming
PRINCIPLE OF LEAST ASTONISHMENT
Sfruttare le conoscenze dell'utente per minimizzare la curva di apprendimento
Applicato al codice consiste nel fare in modo che chi lo legga, visto il nome del metodo o della classe, possa dire:
"Fa proprio quello che mi aspettavo"
"Fa proprio quello che mi aspettavo"
Functions
LIVELLI DI ASTRAZIONE
public static boolean[] primesAsBooleanArray(int limit) {
//allocazione e inizializzazione array dei numeri primi
boolean[] isPrime = new boolean[limit];
Arrays.fill(isPrime, true);
//crivello di Eratostene
for (int number = 2; number <= limit; number++) {
if (isPrime[number - 1]) {
//eliminazione multipli
for (int multiple = number * 2;
multiple <= isPrime.length;
multiple += number) {
isPrime[multiple - 1] = false;
}
}
}
return isPrime;
}
Functions
LIVELLI DI ASTRAZIONE
public static boolean[] trueInitializedBooleanArray(int length) {
boolean[] array = new boolean[limit];
Arrays.fill(array, true);
return array;
}
public static void setMultiplesToFalse(boolean[] numbers, int number) {
for (int multiple = number * 2;
multiple < numbers.length;
multiple += number) {
numbers[multiple - 1] = false;
}
}
Functions
LIVELLI DI ASTRAZIONE
public static boolean[] primesAsBooleanArray(int limit) { boolean[] isPrime = trueInitializedBooleanArray(limit); for (int number = 2; number <= limit; number++) { if (isPrime[number - 1]) { setMultiplesToFalse(isPrime, number); }} return isPrime; }
Functions
LIVELLI DI ASTRAZIONE
- SRP (Single Responsibility Principle): il metodo fa una cosa soltanto, ha una sola ragione per cambiare
- Risulta essere piccolo e facilmente leggibile
- È più semplice seguire l'andamento dell'algoritmo
- I test sono più facili da scrivere
Questi concetti si applicano anche al design delle classi
Functions
ARGUMENTS
- 0
- 1
- 2
- 3
- TROPPI!
Functions
ARGUMENTS
Quando si ha bisogno di passare molti argomenti a una funzione, conviene utilizzare un DTO (Data Transfer Object).
Un DTO serve allo scopo di trasportare parametri.
Esistono casi in cui si giustifica l'uso di molti argomenti:
System.arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length)
Functions
SIDE EFFECTS
Cercare di evitare di modificare lo stato degli oggetti passati come argomenti
-
Trasformare la funzione che modifica l'oggetto in metodo dell'oggetto stesso, o costruire un wrapper
-
Copiare l'oggetto e restituire la copia modificata
- Restituire l'oggetto modificato
Come fare se si deve restituire anche un altro valore?
Functions
COMMAND QUERY SEPARATION
Separare in metodi distinti la modifica di un oggetto e il calcolo di un valore usando il suo stato attuale
- Command-verb: modifica dello stato dell'oggetto
- Query-adjective: utilizzo del suo stato attuale
Comments
/* the cake */
[...] they lie. Not always, and not intentionally, but too often.
Robert C. Martin
Comments
- Più è vecchio un commento, più è lontano dal codice che descrive (sia per numero di modifiche alla riga a cui si riferisce, sia per le righe aggiunte nel mezzo)
- I programmatori non riescono a mantenere i commenti (ad esempio gli IDE possono rinominare variabili, metodi e classi, ma non sempre all'interno dei commenti)
Comments
NO COMMENT
Il codice deve essere autoesplicativo attraverso un'accurata scelta dei nomi di variabili, metodi e classi.
Quando si ha un buon naming i commenti diventano rumore.
Comments
NO COMMENT
- I commenti possono migrare all'interno del codice e diventare quindi ingannevoli
- Noise comments: quando non aggiungono alcuna informazione e risultano totalmente inutili
- Misleading comments: mentono fin da quando sono stati scritti (cattiva descrizione o copy-paste error)
- Journal comments: resi inutili dagli strumenti di versionamento del codice
Comments
CASI PARTICOLARI
TODO - FIXME
-
Aiutano a ricordare che cambiamenti sono stati lasciati in sospeso e perché
- Cercare di risolverli il prima possibile
- Dimenticare di rimuoverli può portare a interpretarli male in seguito
Comments
CASI PARTICOLARI
JavaDoc
- La documentazione delle API è il primo incontro tra uno sviluppatore e la libreria che va ad utilizzare, quindi deve essere curata tanto quanto il codice
- Documentare solo API pubblica per minimizzare il rumore
- Evitare commenti che non aggiungono informazioni
Comments
CASI PARTICOLARI
JavaDoc
Noise comments
/**
* Returns the neurons.
* @return the neurons
*/
public int neurons() {
...
}
Deadc0de
Quando si hanno più opzioni per implementare una parte di codice, si tende a commentare la parte già scritta invece che sostituirla con altro codice, o rinominare un metodo e lasciarlo inutilizzato.
Questo per tenere da parte codice che potrebbe tornare di nuovo utile.
Tuttavia spesso nessuno si fida a rimuoverlo pensando che possa servire a qualcun altro che sta lavorando sullo stesso progetto.
Deadc0de
Col tempo il codice non utilizzato diventa obsoleto, perché non allineato con i cambiamenti di design.
È ancora compilabile, ma siccome è stato scritto quando il sistema era diverso, non segue le nuove convenzioni adottate nel frattempo.
Deadc0de
Il problema è facilmente risolvibile con uno strumento di code versioning: se si necessita del codice rimosso, è sufficiente andare a vedere lo stato dei commit in cui era presente.
When you find dead code, do the right thing. Give it a decent burial.
Robert C. Martin
NULL
I call it my billion-dollar mistake.
Tony Hoare, inventor of null reference
Null
La null reference nacque con l'intento di indicare l'assenza di un valore.
Tuttavia ciò ha portato a del codice instabile, perché si utilizzano riferimenti a oggetti che possono essere null spesso senza effettuare prima alcun check.
Da qui innumerevoli crash delle applicazioni, che a livello aziendale possono portare perdite di denaro.
Null
DON'T PASS NULL
Se un metodo richiede un parametro opzionale, sostituirlo con due metodi: uno che lo richiede e l'altro no
Null
DON'T RETURN NULL
Quando un valore di ritorno esiste in base all'esito di un'operazione, se questa non da alcun risultato:
- lanciare un'eccezione per dichiarare il fallimento dell'operazione
- utilizzare un oggetto contenitore:Maybe, Option, Optional
Null
DON'T RETURN NULL
L'oggetto contenitore funziona da scatola, che può contenere l'oggetto restituito o essere vuota.
Nella pratica la scatola vuota contiene null, ma si ha a disposizione un oggetto contenitore non null a cui richiedere informazioni sul suo contenuto.
L'occorrenza di un valore null è gestita in fase di compilazione piuttosto che in fase di esecuzione.
Null
DON'T RETURN NULL
public static <T> Maybe<T> searchFirst(Iterable<T> values,
Predicate<T> filter) {
for (T value : values) {
if (filter.accept(value)) {
return Maybe.just(value);
}
}
return Maybe.nothing();
}
esempio riadattato da emaze-dysfunctional
Clean Code

GRAZIE!
@0x1abe5
@unitsdev
clean code
By labes
clean code
- 3,152