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

20

Ho incontrato molto questo problema. Ad esempio, attualmente scrivo una funzione di lettura e una funzione di scrittura, ed entrambi controllano se buf è un puntatore NULL e che la variabile mode si trova all'interno di determinati limiti.

Questa è la duplicazione del codice. Questo può essere risolto spostandolo nella sua funzione. Ma dovrei? Questa sarà una funzione piuttosto anemica (non fa molto), piuttosto localizzata (quindi non per scopi generali), e non regge bene da sola (non riesco a capire a cosa serve per meno che non vedi dove è Usato). Un'altra opzione è usare una macro, ma voglio parlare delle funzioni in questo post.

Quindi, dovresti usare una funzione per qualcosa di simile? Quali sono i pro e i contro?

    
posta EpsilonVector 17.11.2011 - 10:24
fonte

9 risposte

31

Questo è un grande uso di funzioni.

This will be a pretty anemic function (doesn't do much)...

Va bene. Le funzioni dovrebbero fare solo una cosa.

rather localized...

In un linguaggio OO, rendilo privato.

(so not general purpose)

Se gestisce più di un caso, è è generale. Inoltre, la generalizzazione non è l'unico uso di funzioni. Sono in effetti lì per (1) salvarti scrivendo lo stesso codice più di una volta, ma anche (2) per rompere il codice in blocchi più piccoli per renderlo più leggibile. In questo caso sta ottenendo entrambi (1) e (2). Tuttavia, anche se la funzione è stata chiamata da un solo punto, potrebbe comunque essere d'aiuto con (2).

and doesn't stand well on its own (can't figure out what you need it for unless you see where it is used).

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

    
risposta data 17.11.2011 - 10:45
fonte
11

È così totalmente dovrebbe essere una funzione.

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

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

Inoltre, cose come queste sono facilmente interconnesse, quindi non devi nemmeno preoccuparti dei costi di chiamata della funzione.

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

Fai la cosa giusta. :)

    
risposta data 17.11.2011 - 11:11
fonte
5

IMO, i frammenti di codice meritano di essere spostati nelle loro funzioni ogni volta che rende il codice più leggibile , indipendentemente dal fatto che la funzione sarà molto breve o verrà utilizzata una sola volta.

Ci sono naturalmente dei limiti dettati dal buon senso. Ad esempio, non vuoi avere il metodo WriteToConsole(text) con il corpo che è semplicemente Console.WriteLine(text) . Ma sbagliare nel lato della leggibilità è una buona pratica.

    
risposta data 17.11.2011 - 10:54
fonte
2

In genere è consigliabile utilizzare le funzioni per rimuovere la duplicazione nel codice.

Tuttavia può essere preso troppo lontano. Questo è un giudizio.

Per fare un esempio del controllo del buffer nullo, probabilmente direi che il codice seguente è abbastanza chiaro e non dovrebbe essere estratto in una funzione separata, anche se lo stesso modello è usato in alcuni punti.

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

Se includi il messaggio di errore come parametro per una funzione di controllo nullo generico e consideri anche il codice richiesto per definire la funzione, allora non è un salvataggio LOC netto per sostituirlo con:

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

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

    
risposta data 17.11.2011 - 10:36
fonte
2

Di solito la centralizzazione del codice è sempre una buona idea. Dobbiamo riutilizzare il codice il più possibile.

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

Tuttavia, qualsiasi pezzo di codice che si ripete come una prosa non si qualifica per essere immediatamente centralizzato. Questo non va bene. Puoi nascondere linee di codice arbitrarie all'interno di una funzione solo per nascondere un codice, nel tempo, quando iniziano a utilizzare più parti dell'applicazione, devono essere tutte compatibili con le esigenze di tutti i contatti della funzione.

Ecco alcune domande che dovresti porre prima di chiedere

  1. La funzione che stai creando ha un significato intrinseco o è solo un insieme di linee?

  2. Quale altro contesto richiederà l'uso delle stesse funzioni? È probabile che tu possa richiedere una leggera generalizzazione dell'API prima di utilizzarla?

  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 controllare se esistono già cose come questa. Ho visto così tante persone che tendono sempre a ridefinire i loro macros 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.

    
risposta data 17.11.2011 - 11:11
fonte
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 3: refactor prima che lo stesso pezzo di codice sia duplicato 3 volte, anotando il quirk quando è duplicato 2 volte.

Che cos'è una duplicazione del codice?

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

Considera l'esempio qui sotto:

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

diventa

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

Hai migliorato l'incapsulamento (ora puoi cambiare le condizioni per essere un amministratore in modo trasparente) e la semantica del codice. Se viene scoperto un bug nel modo in cui controlli che l'utente è un amministratore, non devi andare a gettare l'intera base di codice e fare una correzione ovunque (con il rischio di dimenticarne uno e ottenere un difetto di sicurezza nella tua applicazione).

    
risposta data 17.11.2011 - 11:17
fonte
1

DRY riguarda la semplificazione della manipolazione del codice. Hai appena toccato un punto delicato su questo principio: è non su come minimizzare il numero di token nel tuo codice, ma piuttosto creare singoli punti di modifica per codice semanticamente equivalente . Sembra che i tuoi assegni abbiano sempre la stessa semantica, quindi dovrebbero essere messi in una funzione nel caso tu debba modificarli.

    
risposta data 17.11.2011 - 11:51
fonte
0

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

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

Che cosa succede se devi controllare anche qualcos'altro?

Hai intenzione di trovare tutti i luoghi in cui devi aggiungere il controllo extra o semplicemente modificare una funzione?

    
risposta data 17.11.2011 - 10:40
fonte
0

È quasi sempre buono se vengono rispettate le seguenti condizioni:

  • migliora la leggibilità
  • usato nell'ambito limitato

In un ambito più ampio, devi valutare attentamente i compromessi tra duplicazione e dipendenza. Esempi di tecniche di limitazione dell'ambito: nascondendo in sezioni private o moduli senza esporli pubblici.

    
risposta data 17.11.2011 - 16:04
fonte

Leggi altre domande sui tag