È buona norma utilizzare le funzioni solo per centralizzare il codice comune?


20

Ho riscontrato questo problema molto. Ad esempio, attualmente scrivo una funzione di lettura e una funzione di scrittura, ed entrambi controllano se bufè un puntatore NULL e che la modevariabile rientra in determinati limiti.

Questa è la duplicazione del codice. Questo può essere risolto spostandolo nella propria funzione. Ma dovrei? Questa sarà una funzione piuttosto anemica (non fa molto), piuttosto localizzata (quindi non per scopi generali), e non resiste da sola (non riesco a capire a cosa ti serve se non vedi dove si trova Usato). Un'altra opzione è quella di utilizzare una macro, ma voglio parlare delle funzioni in questo post.

Quindi, dovresti usare una funzione per qualcosa del genere? Quali sono i pro e i contro?


2
I contenuti a linea singola richiedono più di essere utilizzati in più punti per garantire il passaggio a una funzione separata.

1
Ottima domanda Ci sono state così tante volte che mi chiedevo lo stesso.
rdasxy,

Risposte:


31

Questo è un ottimo uso delle funzioni.

Questa sarà una funzione piuttosto anemica (non fa molto) ...

Quello è buono. Le funzioni dovrebbero fare solo una cosa.

piuttosto localizzato ...

In una lingua OO, rendila privata.

(quindi non generico)

Se gestisce più di un caso, è uno scopo generale. Inoltre, generalizzare non è il solo uso di funzioni. Sono davvero lì per (1) salvarti scrivendo lo stesso codice più di una volta, ma anche (2) per suddividere il codice in blocchi più piccoli per renderlo più leggibile. In questo caso sta raggiungendo entrambi (1) e (2). Tuttavia, anche se la tua funzione veniva chiamata da un solo posto, potrebbe comunque essere d'aiuto con (2).

e non sta bene da solo (non riesco a capire a cosa ti serva se non vedi dove viene usato).

Trova un buon nome per questo, e sta bene da solo. "ValidateFileParameters" o qualcosa del genere. Ora sta bene da solo.


6
Bene punti merce. Aggiungerei (3) per impedirti di cercare un bug duplicato in tutta la tua base di codice quando puoi sistemarlo in un posto. La duplicazione del codice può portare a un inferno di manutenzione piuttosto veloce.
deadalnix,

2
@deadalnix: buon punto. Mentre stavo scrivendo mi sono reso conto che i miei punti erano una grossolana semplificazione. La scrittura e il debug più semplice sono certamente un vantaggio della suddivisione delle cose in funzioni (così come la capacità di testare unità di funzioni separate).
Kramii Ripristina Monica il

11

Dovrebbe essere totalmente una funzione.

if (isBufferValid(buffer)) {
    // ...
}

Molto più leggibile e più gestibile (se la logica di controllo cambia mai, la si cambia in un solo posto).

Inoltre, cose come queste vengono incorporate facilmente, quindi non devi nemmeno preoccuparti delle spese generali per le chiamate di funzione.

Lascia che ti faccia una domanda migliore. In che modo non farlo è una buona pratica?

Fare la cosa giusta. :)


4
Se isBufferValid è semplicemente return buffer != null;allora penso che tu stia danneggiando la leggibilità lì.
pdr,

5
@pdr: In questo semplice caso, la leggibilità è dannosa solo se si ha una mentalità da maniaco del controllo e si vuole davvero, davvero, VERAMENTE sapere come il codice controlla se il buffer è valido. Quindi è soggettivo in quei semplici casi.
Spoike,

4
@pdr Non so come faccia male alla leggibilità da qualsiasi standard. Stai esonerando altri sviluppatori dal prendersi cura di come stai facendo qualcosa e concentrandoti su ciò che stai facendo. isBufferValidè sicuramente più leggibile (nel mio libro) di buffer != null, perché comunica lo scopo in modo più chiaro. E ancora, per non parlare del fatto che ti salva anche dalla duplicazione. Cosa ti serve ancora?
Yam Marcovic,

5

IMO, vale la pena spostare gli snippet di codice nelle proprie funzioni ogni volta che ciò rende il codice più leggibile , indipendentemente dal fatto che la funzione sarà molto breve o verrà utilizzata una sola volta.

Esistono ovviamente limiti dettati dal buon senso. Non vuoi avere il WriteToConsole(text)metodo con il corpo semplicemente Console.WriteLine(text), per esempio. Ma sbagliare a livello di leggibilità è una buona pratica.


2

In genere è una buona idea utilizzare le funzioni per rimuovere la duplicazione nel codice.

Tuttavia , può essere portato troppo lontano. Questa è una chiamata di giudizio.

Per prendere l'esempio del tuo controllo del buffer nullo, probabilmente direi che il seguente codice è abbastanza chiaro e non dovrebbe essere estratto in una funzione separata, anche se lo stesso modello viene utilizzato in alcuni punti.

if (buf==null) throw new BufferException("Null read buffer!");

Se includi il messaggio di errore come parametro in una generica funzione di controllo null e consideri anche il codice richiesto per definire la funzione, non è un risparmio LOC netto per sostituirlo con:

checkForNullBuffer(buf, "Null read buffer!");

Inoltre, doversi immergere nella funzione per vedere cosa sta facendo durante il debug significa che la chiamata di funzione è meno "trasparente" per l'utente, e quindi può essere considerata meno leggibile / gestibile.


Probabilmente, il tuo esempio dice di più sulla mancanza di supporto contrattuale nella lingua di un reale bisogno di duplicazione. Ma comunque buoni punti.
deadalnix,

1
Ti sei perso il punto nel modo in cui lo vedo io. Non dover passare o considerare la logica ogni volta che esegui il debug è ciò che sto cercando. Se voglio sapere come è fatto, lo controllo una volta. Doverlo fare ogni volta è semplicemente stupido.
Yam Marcovic,

Se il messaggio di errore ("Null read buffer") viene ripetuto, è necessario eliminare definitivamente tale duplicazione.
Kevin Cline,

Beh, è ​​solo un esempio, ma presumibilmente avrai un messaggio diverso in ogni sito di chiamata di funzione, ad esempio "Null read buffer" e "Null write buffer" per esempio. A meno che tu non voglia lo stesso registro / messaggi di errore per ogni situazione, non puoi de-duplicarlo .......
mikera,

-1 Precondizioni.checkNotNull è una buona pratica, non una cattiva. Non è necessario includere il messaggio di stringa. google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234

2

La centralizzazione del codice è generalmente sempre una buona idea. Dobbiamo riutilizzare il codice il più possibile.

Tuttavia, è importante notare come farlo. Ad esempio, quando si dispone di un codice che compute_prime_number () o check_if_packet_is_bad () è buono. È probabile che si evolverà l'algoritmo della funzionalità stessa che ne trarrà beneficio.

Tuttavia, qualsiasi parte di codice che si ripete come prosa non può essere centralizzata immediatamente. Questo non va bene. È possibile nascondere righe di codice arbitrarie all'interno di una funzione solo per nascondere un codice, nel tempo, quando più parti dell'applicazione iniziano a utilizzare, tutte devono rimanere compatibili con le esigenze di tutti i chiamanti della funzione.

Ecco alcune domande che dovresti porre prima di porre

  1. La funzione che stai creando ha il suo significato intrinseco o è solo un mucchio di righe?

  2. Quale altro contesto richiederà l'uso delle stesse funzioni? È probabile che potresti aver bisogno di generalizzare leggermente l'API prima di usarlo?

  3. Quale sarà l'aspettativa di (diverse parti delle) applicazioni quando si generano eccezioni?

  4. Quali sono gli scenari per vedere che le funzioni si evolveranno?

Dovresti anche verificare se esistono già cose come questa. Ho visto così tante persone che tendono sempre a ridefinire le loro macro MIN, MAX piuttosto che cercare ciò che già esiste.

In sostanza, la domanda è: "Questa nuova funzione è davvero degna di essere riutilizzata o è solo una copia-incolla ?" Se è il primo, è bello andare.


1
Bene, se il codice duplicato non ha il suo significato, il tuo codice ti dice che deve essere sottoposto a refactoring. Perché i luoghi in cui si verifica la duplicazione probabilmente non hanno nemmeno il loro significato.
deadalnix,

1

La duplicazione del codice dovrebbe essere evitata. Ogni volta che lo anticipi, dovresti evitare la duplicazione del codice. Se non l'hai previsto, applica la regola di 3: refactor prima che lo stesso pezzo di codice venga duplicato 3 volte, anota la stranezza quando viene duplicata 2 volte.

Che cos'è una duplicazione del codice?

  • Una routine che si ripete in diversi punti della base di codice. Questo tourine deve essere più complesso di una semplice chiamata di funzione (altrimenti, non otterrai nulla fattorizzando).
  • Un compito molto comune, anche banale (spesso un test). Ciò migliorerà l'incapsulamento e la semantica nel codice.

Considera l'esempio seguente:

if(user.getPrileges().contains("admin")) {
    // Do something
}

diventa

if(user.isAdmin()) {
    // Do something
}

Hai migliorato l'incapsulamento (ora puoi modificare le condizioni per essere un amministratore in modo trasparente) e la semantica del codice. Se viene rilevato un bug nel modo in cui si verifica che l'utente sia un amministratore, non è necessario lanciare l'intera base di codice e apportare una correzione ovunque (con il rischio di dimenticarne uno e ottenere un difetto di sicurezza nella propria applicazione).


1
Btw L'esempio illustra la Legge di Demetra.
MaR,

1

DRY riguarda la semplificazione della manipolazione del codice. Hai appena accennato a un bel punto su questo principio: non si tratta di minimizzare il numero di token nel tuo codice, ma piuttosto di creare singoli punti di modifica per un codice semanticamente equivalente . Sembra che i tuoi assegni abbiano sempre lo stesso semantico, quindi dovrebbero essere messi in funzione nel caso in cui tu debba modificarli.


0

Se lo vedi duplicato, dovresti trovare un modo per centralizzarlo.

Le funzioni sono un buon metodo (forse non il migliore, ma dipende dalla lingua) Anche se la funzione è anemica come dici tu non significa che rimarrà tale.

E se dovessi controllare anche qualcos'altro?

Troverai tutti i posti in cui devi aggiungere il controllo extra o semplicemente modificare una funzione?


... d'altra parte, cosa succederebbe se ci fosse una probabilità molto alta di non aggiungere mai qualcosa. Il tuo suggerimento è ancora il miglior modo di agire anche in quel caso?
Epsilon.Vettore

1
@EpsilonVector la mia regola empirica è che se dovessi cambiarlo una volta, potrei anche riformularlo. Quindi, nel tuo caso, lo lascerei e se dovessi cambiarlo, diventerebbe una funzione.
Thanos Papathanasiou,

0

È quasi sempre positivo se sono soddisfatte le seguenti condizioni:

  • migliora la leggibilità
  • utilizzato in ambito limitato

In un ambito più ampio è necessario valutare attentamente la duplicazione rispetto ai compromessi di dipendenza. Esempi di tecniche di limitazione dell'ambito: nascondersi in sezioni o moduli privati ​​senza esporli al pubblico.

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.