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"

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
    Made with Slides.com