Contattaci

Guardie, invarianti e complessità

Guardie, invarianti e complessità
  • Data: 14 Marzo 2023
  • Autore: Federico Teotini
  • Categorie

  • Giuneco Tech

    Il nostro settore richiede uno studio continuo e una forte attenzione all’innovazione. Incentiviamo quindi dei programmi formativi annuali per tutti i componenti del team, con ore dedicate (durante l’orario di lavoro) e serate formative sia online che in presenza. Sponsorizziamo eventi, sia come partner che semplicemente come partecipanti, e scriviamo articoli su quello che abbiamo imparato per essere, a nostra volta, dei divulgatori.

    Vai alla sezione Team
  • Qualsiasi sia il software che state sviluppando e qualsiasi sia il linguaggio che usate, gli aspetti più importanti da tenere in considerazione sono sempre gli stessi:

    Minimizzare i bug, che, in fondo, si riduce all’impedire al sistema di entrare in uno stato invalido/inaspettato.

    Minimizzare la complessità del codice, in quanto rende esponenzialmente più difficile comprenderne l’obbiettivo funzionale.

    In questo articolo ci concentreremo sulle guardie, tecnica il cui obbiettivo è quello di impedire stati invalidi, e su come implementarle in modo semplice.

    Guardie, non validazione

    La tecnica più banale, ma fondamentale (e spesso sottovalutata), consiste nel controllare che gli input di una funzione siano corretti; inoltre questo controllo deve essere eseguito immediatamente in modo da proteggere la logica vera e propria ( short-circuit o fail-fast).

    Esistono principalmente due metodologie di controllo degli input, la validazione e le guardie, che hanno due obbiettivi la cui differenza è sottile ma importante:

    1. La validazione si applica a input come potrebbero essere quelli forniti da un utente, ovvero input di cui ci aspettiamo che possano non essere corretti. In questo caso, la non correttezza non è un evento eccezionale.
    2. Le guardie invece, vengono usate per circostanze eccezionali ovvero situazioni in cui il software non si dovrebbe mai trovare, o impossibili da verificarsi durante il normale funzionamento.

    Le guardie, quindi, proteggono dalla violazioni di quelle regole che devono essere sempre valide durante l’intero ciclo di funzionamento del software, ovvero le invarianti.

    Inoltre, seguendo il principio fail-fast, sono implementate come semplici check che in caso di fallimento fanno uscire dalla funzione, con un lancio di un eccezione o un semplice return anticipato.

    Riduzione della complessità

    La più piccola unità di codice che abbiamo è la funzione e deve essere semplice capirne il funzionamento.

    Uno degli aspetti che spesso compromettono la semplicità di una funzione è la cosiddetta complessità condizionale, ovvero tutta quella logica necessaria a garantire la correttezza degli input, ad esempio le guardie.

    In questo esempio si possono vedere implementate due guardie, che, come è facile notare, aggiungono complessità “inutile” al vero intento della funzione, ovvero l’assegnamento di due proprietà. In questo semplice caso abbiamo 4 righe di codice superfluo (8 se consideriamo anche le parentesi graffe); situazioni ben più complesse potrebbero trasformare una funzione semplice in una lunga e nebulosa.
    Le guardie dovrebbero rappresentare dei dettagli implementativi, potrebbero quindi essere scritte, per esempio, così:

    Ma perchè reinventare la ruota? Esistono diversi pacchetti che permettono di scrivere guardie in modo semplice ed elegante, uno di questi è GuardClauses (https://github.com/ardalis/GuardClauses) di Steve “ardalis” Smith.
    GuardClauses permette di riscrivere il precedente esempio con una sintassi fluent molto comoda:

    GuardClauses include molte di queste semplici guardie, ma permette anche di scriverne altre adatte alle singole casistiche:

    In questo modo, la logica di queste semplici guardie può essere estratta in modo da abbattere la complessità delle nostre funzioni.