Devo refactoring grandi funzioni che consistono principalmente di una regex? [chiuso]

15

Ho appena scritto una funzione che si estende su circa 100 righe. Sentendo questo, probabilmente sei tentato di parlarmi di singole responsabilità e di spingermi al refactoring. Questo è anche il mio istinto, ma qui c'è il problema: la funzione fa una cosa. Esegue una complessa manipolazione di stringhe e il corpo della funzione consiste principalmente in una regex dettagliata, suddivisa in molte righe documentate. Se interrompevo la regex in più funzioni, mi sento come se fossi in realtà perdere leggibilità, dal momento che sto cambiando efficacemente le lingue, e non sarà in grado di sfruttare alcune funzionalità regex l'offerta. Ecco ora la mia domanda:

Quando si tratta di manipolare le stringhe con espressioni regolari, i corpi di grandi dimensioni hanno ancora un anti-pattern? Sembra che i gruppi di cattura nominati abbiano uno scopo molto simile alle funzioni. A proposito, ho i test per ogni flusso attraverso la regex.

    
posta DudeOnRock 06.03.2014 - 04:11
fonte

7 risposte

36

Quello che stai incontrando è la dissonanza cognitiva che viene dall'ascolto di persone che favoriscono l'aderenza pedante alle linee guida sotto le spoglie di "migliori pratiche" su un processo decisionale ragionato.

Hai chiaramente fatto i compiti a casa:

  • Lo scopo della funzione è compreso.
  • Il funzionamento della sua implementazione è compreso (vale a dire, leggibile).
  • Sono disponibili test a copertura completa dell'implementazione.
  • Questi test passano, il che significa che ritieni che l'implementazione sia corretta.

Se qualcuno di questi punti non fosse vero, sarei in prima fila per dire che la tua funzione ha bisogno di lavoro. Quindi c'è un voto per lasciare il codice così com'è.

Il secondo voto deriva dall'osservazione delle opzioni e da ciò che ottieni (e perdi) da ciascuna:

  • Refactor. Ciò ti consente di rispettare l'idea di qualcuno sulla durata di una funzione e di sacrificare la leggibilità.
  • Non fare nulla. Questo mantiene la leggibilità esistente e sacrifica la conformità con l'idea di qualcuno di quanto a lungo dovrebbe essere una funzione.

Questa decisione si riduce a ciò che apprezzi di più: leggibilità o lunghezza. Cado nel campo che crede che la lunghezza sia bella , ma la leggibilità è importante e prenderà il secondo sul primo in qualsiasi giorno della settimana.

In conclusione: se non è rotto, non aggiustarlo.

    
risposta data 06.03.2014 - 05:28
fonte
19

Onestamente, la tua funzione potrebbe "fare una cosa", ma come hai detto tu stesso

I could start breaking up the regex into multiple functions,

il che significa che il tuo codice ex-reg fa un sacco di cose. E immagino che potrebbe essere suddiviso in unità più piccole, testabili individualmente. Tuttavia, se questa è una buona idea, non è facile rispondere (soprattutto senza vedere il codice reale). E la risposta corretta potrebbe essere né "sì" né "no", ma "non ancora, ma la prossima volta devi cambiare qualcosa in quel registro".

but feel like I would actually lose readability that way, since I am effectively switching languages

E questo è il punto centrale: hai un pezzo di codice scritto in reg ex lingua . Questo linguaggio non fornisce alcun buon mezzo di astrazione in sé (e non considero i "gruppi di cattura nominati" come una sostituzione per le funzioni). Quindi il refactoring "nella lingua del regex" non è realmente possibile, e l'intreccio dei reg exp più piccoli con la lingua ospite potrebbe non migliorare la leggibilità (almeno, senti così, ma hai dei dubbi, altrimenti il tuo non avrebbe postato la domanda). Quindi ecco il mio consiglio

  • mostra il tuo codice a un altro sviluppatore avanzato (magari su link ) per assicurarti che gli altri pensino alla leggibilità come fai tu . Siate aperti all'idea che altri potrebbero non trovare un record di 100 righe come leggibile come voi. A volte la nozione di "non facilmente infrangibile in pezzi più piccoli" può essere superata solo da un secondo paio di occhi.

  • osserva l'effettiva evoluzione - il tuo splendente reg ha ancora un aspetto così bello quando arrivano nuovi requisiti e devi implementarli e testarli? Finché il tuo reg exp funziona, non lo toccherei, ma ogni volta che qualcosa deve essere cambiato, vorrei riconsiderare se fosse davvero una buona idea mettere ogni cosa in questo grande blocco - e (sul serio!) Ripensare se dividere in pezzi più piccoli non sarebbero un'opzione migliore.

  • osserva la manutenibilità: puoi efficacemente eseguire il debug del reg exp nel modulo corrente molto bene? Soprattutto dopo aver cambiato qualcosa, e ora i tuoi test ti dicono che qualcosa non va, hai un debugger di reg exp che ti aiuta a trovare la causa principale? Se il debug diventa difficile, sarebbe anche un'occasione per riconsiderare il tuo design.

risposta data 06.03.2014 - 08:36
fonte
4

A volte una funzione più lunga che fa una cosa è il modo più appropriato per gestire un'unità di lavoro. È possibile accedere facilmente a funzioni molto lunghe quando si inizia a gestire query su un database (utilizzando la lingua di query preferita). Rendere più funzionale una funzione (o un metodo) limitandolo allo scopo dichiarato è quello che considererei il risultato più desiderabile di una funzione.

La lunghezza è uno "standard" arbitrario quando si tratta di dimensioni del codice. Dove una funzione di 100 linee in C # può essere considerata piuttosto lunga, sarebbe minima in alcune versioni di assembly. Ho visto alcune query SQL ben incluse nelle 200 righe dell'intervallo di codice che hanno restituito un set di dati molto complicato per un report.

Codice completamente funzionante , che è il più semplice possibile ragionevolmente che lo rende l'obiettivo.

Non cambiarlo solo perché è lungo.

    
risposta data 06.03.2014 - 06:48
fonte
3

Si può sempre suddividere la regex in sub-regexes e comporre gradualmente l'espressione finale. Ciò potrebbe aiutare la comprensione per uno schema molto grande, in particolare se lo stesso sottotrama viene ripetuto molte volte. Ad esempio in Perl;

my $start_re = qr/(?:\w+\.\w+)/;
my $middle_re = qr/(?:DOG)|(?:CAT)/;
my $end_re = qr/ => \d+/;

my $final_re = $start_re . $middle_re . $end_re;
# or: 
# my $final_re = qr/${start_re}${middle_re}${end_re}/
    
risposta data 06.03.2014 - 10:48
fonte
1

Direi di romperlo se è fragile. dal punto di vista della manutenibilità e forse della resuability, è logico romperlo, ma ovviamente devi considerare naturale la tua funzione e il modo in cui ottieni input e che cosa restituirà.

Ricordo che stavo lavorando per analizzare lo streaming di dati frammentati in oggetti, quindi quello che ho fatto fondamentalmente era dividerlo in due parti principali, uno era la costruzione di un'unità completa di String dal testo codificato e nella seconda parte l'analisi di tali unità in dati dizionario e organizzale (potrebbe essere una proprietà casuale per oggetti diversi) e di aggiornare o creare oggetti.

Potrei anche rompere ogni parte principale in diverse funzioni più piccole e più specifiche, così alla fine ho avuto 5 diverse funzioni per fare tutto e potrei riutilizzare alcune delle funzioni in un posto diverso.

    
risposta data 06.03.2014 - 05:14
fonte
1

Una cosa che potresti aver o non aver considerato è scrivere un parser piccolo nella lingua che stai usando, invece di usare una regex in quella lingua. Questo può essere più facile da leggere, testare e mantenere.

    
risposta data 06.03.2014 - 15:48
fonte
1

Le regex giganti sono una cattiva scelta nella maggior parte dei casi. Nella mia esperienza, sono spesso usati perché lo sviluppatore non ha familiarità con l'analisi (vedi la risposta di Thomas Eding ).

Ad ogni modo, assumiamo che tu voglia attenersi a una soluzione basata su espressioni regolari.

Poiché non conosco il codice effettivo, esaminerò i due possibili scenari:

  • La regex è semplice (molta corrispondenza letterale e poche alternative)

    In questo caso le funzionalità avanzate offerte da una singola regex non sono indispensabili. Ciò significa che probabilmente trarrai vantaggio dalla sua suddivisione.

  • L'espressione regolare è complessa (molte alternative)

    In questo caso non puoi realisticamente avere una copertura completa del test, perché probabilmente hai milioni di possibili flussi. Quindi, per testarlo, devi dividerlo.

Potrei mancare di immaginazione, ma non riesco a pensare a nessuna situazione del mondo reale in cui una regex di 100 righe sia una buona soluzione.

    
risposta data 06.03.2014 - 20:26
fonte