Qual è una buona pratica di codice quando creare una funzione / metodo per piccoli segmenti di codice ripetitivi?


12

Molte volte durante la scrittura di programmi più grandi ho messo in dubbio dopo quante copie e paste ha senso mettere il codice in una funzione o metodo e qual è una buona regola empirica? Ho usato una regola empirica di quattro o più righe e ho visualizzato più di due volte, quindi creo una semplice funzione / metodo contenente quel codice. Riesci a pensare a una pratica migliore o offrire qualche suggerimento? Questa è più una domanda di modello di progettazione generale piuttosto che una domanda specifica della lingua.

Risposte:


29

Uso le funzioni in parte come modo per documentare il codice. La chiamata di una funzione con un nome significativo semplifica la comprensione del codice. In alcuni casi anche una funzione con una sola riga ha senso.

Ad esempio, in "Codice pulito", Robert C. Martin fornisce il seguente esempio: quale preferiresti vedere? Questo:

// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&
    (employee.age > 65))

O questo?

if (employee.isEligibleForFullBenefits())

Non sono sempre d'accordo con lui, ma in questo caso lo faccio. Il codice dovrebbe essere leggibile, non solo quando lo scrivi e conosci ogni dettaglio, ma anche alle 21:00 quando devi correggere i bug nel codice di qualcun altro. Non è raccomandato fissare una condizione lunga e cercare di capire tutti i doppi negativi. Se puoi semplicemente inserire un nome (non solo le condizioni, ma ogni parte di codice che scrivi), diventa molto più semplice.

Non mi sono mai pentito di aver inserito qualcosa in una funzione, e se sei preoccupato per le prestazioni, allora prima profilo.


2
Seguire questa semplice pratica alla fine ti permetterà di scrivere il codice dell'applicazione a un livello relativamente alto. Le piccole funzioni vengono raccolte in piccole classi e presto stai convertendo le specifiche funzionali in codice quasi parola per parola.
Kevin Cline

11
Ho adorato questo esempio. All'improvviso non hai più bisogno di quel commento. Questa è una regola empirica : se il tuo commento può essere convertito in una variabile o in un nome di funzione, fallo!
Karoly Horvath,

Sono d'accordo con Omrib qui, spesso si tratta di ripulire il codice - ciò che lo rende più leggibile rispetto a qualsiasi altra regola empirica. Se finisco per riutilizzare qualcosa, lo estrarrò ad un metodo. Tuttavia, il mio IDE e gli strumenti spesso mi aiutano, quindi è facile e veloce da fare.
Travis,

+1 Questo tipo di codice potrebbe anche essere più veloce, almeno se deve essere JIT-ed - paghi solo per quello che usi.
Giobbe

1
noto anche come Intention Revealing Names .
rwong

13

Esiste un malinteso diffuso sul fatto che le chiamate di funzione dovrebbero essere effettuate solo per evitare segmenti di codice ripetitivi. La mia regola empirica è che qualsiasi unità logica di lavoro dovrebbe essere trasformata in una funzione, anche quando viene utilizzata solo in un unico posto. Questo di solito porta a una migliore leggibilità e ti consente di scrivere codice autocompattante, in cui i nomi delle funzioni sostituiscono i commenti e non è necessario scrivere commenti aggiuntivi che spieghino cosa stai facendo.


1
Vedi a quali lunghezze andranno i programmatori per evitare di scrivere un commento?
Alger,

1
@Alger come dovrebbero
MatrixFrog l'

6

Se viene utilizzato in più di un posto, e

  • è probabile che cambi, o
  • è difficile avere ragione

quindi renderlo una funzione o un metodo. Pezzi lunghi di codice ripetuto, nella mia esperienza, ricadranno naturalmente in una di queste categorie (di solito la prima, ma poi le categorie si sovrappongono molto;). Naturalmente, tutto ciò che deve essere nell'interfaccia è anche una funzione / metodo a sé stante.


3
La domanda è: perché non dovresti scrivere una funzione per un pezzo di codice comunemente ripetuto anche se non è stato difficile ottenere il giusto o probabilmente cambiare? (La mia regola empirica: se è ripetuta e più lunga della chiamata di una funzione, rendila una funzione)
Winston Ewert

Andrei ancora oltre. Come programmatore devi eliminare ogni tipo di ripetizione . Se si trova nel database (normalizza), alcuni test manuali (sostituiscili con test unitari) o la distribuzione (automatizzalo).
Karoly Horvath,

@ Winston: dipende dalla lingua utilizzata. Non tutti i costrutti possono essere catturati in modo naturale come una funzione, la funzione potrebbe richiedere più spazio del codice originale (si pensi a C e si ritorni con il puntatore), le chiamate di funzione potrebbero incorrere in costi generali.
Fred Foo,

@larsman, sono curioso di sapere a cosa ti riferisci "(pensa C e ritorna con il puntatore)". Ma quello che stai dicendo è quello che stavo cercando di ottenere con la mia regola empirica. Chiamare la funzione deve essere più semplice (cioè catturare naturalmente e occupare meno spazio) quindi implementare il contenuto della funzione.
Winston Ewert,

Se un pezzo di codice calcola più valori, diciamo float x, int ye double densityquindi impostare quei calcoli come una funzione C può essere più complicato della semplice ripetizione del codice, dal momento che devi escogitare un modo per ottenere tutti e tre i valori. Se gli stessi calcoli ripetuti sono banali, a volte è meglio lasciarli in linea.
Fred Foo,

4

Quasi sempre, soprattutto se ogni duplicato rappresenta la stessa operazione da un punto di vista concettuale. Se viene eseguito allo stesso modo, ma su tipi diversi, eseguire un'implementazione generica.

L'unico motivo per cui non mi viene in mente è quello della manutenzione: a volte potrebbe essere più conveniente evitare di creare una dipendenza tra cose separate, anche a costo di una duplicazione.


Fai attenzione alla tipizzazione anatra, se per ora l'implementazione è simile, ma la funzionalità è effettivamente diversa, quindi l'unione delle due rende fastidioso dividere indietro. Soprattutto in lingue con scarso supporto IDE (ehi, lavoro in C ++ ...)
Matthieu M.

D'altra parte tenendoli separati, hai due funzioni che fanno la stessa cosa per testare, metà delle possibilità che il tuo codice venga esercitato, due posti in cui lo stesso bug può insinuarsi e devi ricordare di sistemare quello in cui il bug non è stato ancora rilevato. Mi piacerebbe ancora lavorare in C ++, nonostante il scarso supporto IDE ;-)
Nicola Musatti,

1

Una ricerca di " refactoring " ti porterà a molte risorse per "best practice" del settore per questo processo molto comune. L'articolo un po 'famoso, Once and Only Once è un grande riferimento storico che spiega quali alcuni considerano "buone pratiche" per le preoccupazioni sollevate dalla tua domanda. Inoltre, il concetto ancora più generale è noto come Don't Repeat Yourself (DRY) . Per una serie davvero approfondita di risposte alla tua domanda, leggi il grande classico di Martin Fowler , Refactoring: Improving the Design of Existing Code , che copre alcuni dei consigli più noti per il refactoring , che è ciò che stai cercando intuitivamente di realizzare !


0

Se il codice viene ripetuto esattamente in più di una volta e la sezione ripetuta non cambierà nel prossimo futuro, lo interrompo in una funzione.


1
se sta per cambiare, ancora più motivi per rimodellare. Quindi dovrai cambiarlo solo una volta
SHug

0

Ciò dipende dalla natura della coesione del codice ripetuto. Se la sezione ripetuta del codice esegue una funzione specifica, allora è un ottimo candidato per essere trasformato in un metodo, in parte a causa del principio DRY , in parte perché se la funzione deve essere ottimizzata o corretta, allora c'è solo una sezione di codice da affrontare.

Se l'associazione è una coincidenza, è meglio ripetere il codice piuttosto che trasformarlo in un metodo. Se devi aggiungere qualcosa al centro di una delle sequenze di codice per soddisfare uno degli usi di quello snippet, se si trova in un metodo, la modifica apportata potrebbe influire su altri usi di quel metodo.

Vedi l'articolo di Wikipedia sul concetto di coesione del codice .


se l'associazione sembra coincidente, è probabile che i due processi condividano un'idea comune, e probabilmente dovresti esplorare se i due processi sono effettivamente due aspetti della stessa cosa. Più spesso, lo è.
Lie Ryan,

0

Devi distinguere tra funzioni nel senso della programmazione strutturata e metodi di una classe.

Nel tuo esempio, quello che hai mostrato è un metodo e come tale non dovrebbe essere codificato in linea.

potresti dover convalidare una stringa per vedere se è un numero oppure no, in questo caso usi una funzione e si applicano la maggior parte delle risposte precedenti.

Questa distinzione è importante specialmente nei grandi progetti.

Per quanto è possibile, cerca di separare le regole di business (che sono metodi) dagli algoritmi di elaborazione (che sono pure funzioni di programmazione).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.