window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = finestra if (w.LeadBooster) { console.warn('LeadBooster esiste già') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() thecodest, Author at The Codest - Page 8 of 18

Pertanto, per alcuni è incomprensibile che rifattorizzazione è un'area della programmazione ed è anche una parte molto importante del lavoro del programmatore. Il codice è in continua evoluzione, verrà modificato finché ci sarà la possibilità di aggiungere nuove funzionalità. Tuttavia, potrebbe assumere una forma che non consente più di aggiungere efficacemente nuove funzionalità e sarebbe più semplice riscrivere l'intero programma.

Che cos'è il refactoring?

Di solito, la risposta che si sente è che si tratta di cambiare la struttura del codice applicando una serie di trasformazioni di refactoring senza influenzare il comportamento osservabile del codice. Questo è vero. Recentemente, mi sono imbattuto in una definizione di Martin Fowler nel suo libro "Migliorare la progettazione del codice esistente dove descrive rifattorizzazione come "fare grandi cambiamenti a piccoli passi". Descrive rifattorizzazione come una modifica del codice che non influisce sul suo funzionamento, ma sottolinea che deve essere fatto a piccoli passi.

Il libro sostiene inoltre che rifattorizzazione non influisce sul funzionamento del codice e sottolinea che non ha alcun effetto sul superamento dei test in qualsiasi momento. Descrive passo dopo passo come eseguire in modo sicuro rifattorizzazione. Mi è piaciuto il suo libro perché descrive semplici trucchi che possono essere utilizzati nel lavoro di tutti i giorni.

Perché abbiamo bisogno di refactoring?

 Il più delle volte è necessario quando si vuole introdurre una nuova funzionalità e il codice nella sua versione attuale non lo consente o sarebbe più difficile senza modifiche al codice. Inoltre, è utile nei casi in cui l'aggiunta di ulteriori funzionalità non è conveniente in termini di tempo, cioè sarebbe più veloce riscrivere il codice da zero. A volte si dimentica che rifattorizzazione può rendere il codice più pulito e leggibile. Martin scrive nel suo libro come esegue il refactoring quando avverte odori sgradevoli nel codice e, come dice lui stesso, "lascia sempre spazio al meglio". E qui mi ha sorpreso vedendo il refactoring come un elemento del lavoro quotidiano sul codice. Per me il codice è spesso incomprensibile, leggerlo è un po' un'esperienza perché il codice è spesso poco intuitivo.

La caratteristica distintiva di un programma ben progettato è la sua modularitàgrazie alla quale è sufficiente conoscere solo una piccola parte del codice per introdurre la maggior parte delle modifiche. La modularità facilita anche l'inserimento di nuove persone e l'avvio di un lavoro più efficiente. Per ottenere questa modularità, gli elementi del programma correlati devono essere raggruppati insieme e i collegamenti devono essere comprensibili e facili da trovare. Non esiste un'unica regola empirica che indichi come farlo. Man mano che si conosce e si capisce meglio come dovrebbe funzionare il codice, si possono raggruppare gli elementi, ma a volte è necessario anche testare e controllare.

Una delle regole della rifattorizzazione in YAGNIè l'acronimo di "You Aren't Gonna Need It" (non ne avrai bisogno) e deriva da Programmazione eXtreme (XP) utilizzato principalmente in Agile sviluppo software squadre. Per farla breve, YAGNI dice che si dovrebbero fare solo cose aggiornate. Ciò significa che anche se qualcosa potrebbe essere necessario in futuro, non dovrebbe essere fatto adesso. Ma non possiamo nemmeno bloccare ulteriori estensioni e qui la modularità diventa importante.

Quando si parla di rifattorizzazioneÈ necessario menzionare uno degli elementi più essenziali, ovvero i test. In rifattorizzazionedobbiamo sapere che il codice funziona ancora, perché rifattorizzazione non cambia il funzionamento, ma la struttura, quindi tutti i test devono essere superati. È meglio eseguire i test per la parte di codice su cui stiamo lavorando dopo ogni piccola trasformazione. Questo ci dà la conferma che tutto funziona come dovrebbe e accorcia i tempi dell'intera operazione. Questo è ciò di cui parla Martin nel suo libro: eseguire i test il più spesso possibile per non fare un passo indietro e perdere tempo a cercare una trasformazione che ha rotto qualcosa.

Rifattorizzazione del codice senza test è una rottura e c'è una grande possibilità che qualcosa vada storto. Se è possibile, sarebbe meglio aggiungere almeno alcuni test di base che ci rassicurino sul funzionamento del codice.

Le trasformazioni elencate di seguito sono solo esempi, ma sono davvero utili nella programmazione quotidiana:

Esempio

Questo è un articolo su rifattorizzazione ed è necessario un esempio. Qui di seguito voglio mostrare un semplice esempio di rifattorizzazione con l'uso di Sovrascrittura della dichiarazione annidata e Sostituzione del polimorfismo dell'istruzione condizionale. Supponiamo di avere una funzione del programma che restituisce un hash con informazioni su come innaffiare le piante nella vita reale. Tali informazioni probabilmente si troverebbero nel modello, ma per questo esempio le abbiamo nella funzione.

def irrigazione_info(impianto)
     risultato = {}
     if pianta.is_a? Suculent || plant.is_a? Cactus
         result = { water_amount: "Un po'" , come_fare: "Dal fondo", durata_innaffiatura: "2 settimane" }
     elsif plant.is_a? Alocasia || plant.is_a? Maranta
         result = { water_amount: "Grande quantità", come_fare: "Come preferisci", durata_innaffiatura: "5 giorni" }
     elsif plant.is_a? Peperomia
         risultato = { quantità_acqua: "Quantità decente",
             come_fare: "Dal basso! Non amano l'acqua sulle foglie",
             durata_innaffiatura: "1 settimana" }
     altrimenti
         risultato = { quantità_acqua: "Quantità d'acqua",
             come_fare: "Come preferisci",
             durata_innaffiatura: "1 settimana"
             }
     fine
     restituire il risultato
 fine

L'idea è di cambiare se tornare:

se plant.isa? Suculent || plant.isa? Cactus

     result = { wateramount: "Un po'" , howto: "Dal basso",

A

return { water_amount: "Un po'" , how_to: "Dal fondo", durata_innaffiatura: "2 settimane" } if plant.is_a? Suculent || plant.is_a? Cactus

restituire { acquaquantità: "Un po'", comea: "Dal basso", irrigazionedurata: "2 settimane" } if plant.isa? Suculent || plant.is_a? Cactus

E così via, fino ad arrivare a una funzione che assomiglia a questa:

def irrigazione_info(impianto)

return result = { wateramount: "Un po'" , howto: "Dal fondo", wateringduration: "2 settimane" } if plant.isa? Suculent || plant.is_a? Cactus

return result = { wateramount: "Grande quantità", modalità: "Come preferisci", durata dell'irrigazione: "5 giorni" } if plant.isa? Alocasia || plant.is_a? Maranta

return result = { quantità_acqua: "Quantità d'acqua",

          howto: "Dal basso! Non amano l'acqua sulle foglie",
          durata dell'irrigazione: "1 settimana" } if plant.is_a? Peperomia

return result = { quantità_acqua: "Quantità dicente",

          come_fare: "Come preferisci",

          durata_innaffiatura: "1 settimana"

          }

fine

 Alla fine, avevamo già un risultato di ritorno. Una buona abitudine è quella di procedere passo dopo passo e testare ogni modifica. Si potrebbe sostituire questo blocco if con un caso switch e il risultato sarebbe immediatamente migliore, senza dover controllare ogni volta tutti gli if. Sarebbe così:

def irrigazione_info(impianto)

swich plant.class.to_string

caso Suculent, Cactus

     {acqua: "Un po'" , howto: "Dal fondo", durata_innaffiatura: "2 settimane" }

caso Alocasia, Maranta

     {acqua: "Grande quantità", modalità: "Come preferisci", durata_innaffiatura: "5 giorni" }

caso Peperomia

     {acqua_ammontare: "Quantità dicente",

          come_fare: "Dal basso! Non amano l'acqua sulle foglie",

          durata_innaffiatura: "1 settimana" }

altrimenti

     {acqua_ammontare: "Quantità dicente",

            come_fare: "Come preferisci",

       durata_innaffiatura: "1 settimana" }

fine

fine

E poi si può applicare il metodo Sostituzione del polimorfismo delle istruzioni condizionali. Si tratta di creare una classe con una funzione che restituisca il valore corretto e che li collochi al posto giusto.

classe Suculent

...

def irrigazione_info()

     return { wateramount: "Un po'" , howto: "Dal fondo", watering_duration: "2 settimane" }

end

fine

classe Cactus

...

def irrigazione_info()

     return { wateramount: "Un po'" , howto: "Dal fondo", watering_duration: "2 settimane" }

end

fine

classe Alocasia

...

def irrigazione_info

     return {acqua: "Grande quantità", howto: "Come preferisci", watering_duration: "5 giorni" }

fine

fine

classe Maranta

...

def irrigazione_info

     return {acqua: "Grande quantità", howto: "Come preferisci", watering_duration: "5 giorni" }

fine

fine

classe Peperomia

...

def irrigazione_info

     return { importo_acqua: "Quantità di acqua",

      come_fare: "Dal basso! Non amano l'acqua sulle foglie",

      durata_innaffiatura: "1 settimana" }

fine

fine

classe Impianto

...

def irrigazione_info

     return { importo_acqua: "Quantità di acqua",

              come_fare: "Come preferisci",

              durata_innaffiatura: "1 settimana" }

fine

fine

E nella main watering_infofunction, il codice sarà simile a questo:

def irrigazione_info(impianto)

    plant.map(&:watering_info)

fine

Naturalmente, questa funzione può essere rimossa e sostituita con il suo contenuto. Con questo esempio, ho voluto presentare la funzione generale modello di rifattorizzazione.

Sintesi

Rifattorizzazione è un argomento importante. Spero che questo articolo sia stato un incentivo a leggere di più. Questi competenze di refactoring vi aiuterà a individuare i bug e a migliorare il vostro laboratorio di codice pulito. Vi consiglio di leggere il libro di Martin (Improving the Design of Existing Code), che è un insieme di regole abbastanza basilari e utili di rifattorizzazione. L'autore mostra le varie trasformazioni passo dopo passo con una spiegazione completa e una motivazione e consigli su come evitare errori in rifattorizzazione. Grazie alla sua versatilità, è un libro delizioso per frontend e sviluppatori backend.

Diventare sviluppatore Ruby junior

Per saperne di più

GraphQL Ruby. E le prestazioni?

Rotaie e altri mezzi di trasporto

Sviluppo di Rails con TMUX, Vim, Fzf + Ripgrep

it_ITItalian