2011-01-14 20 views
6

Ho fatto delle ricerche qui e non ho trovato nulla di simile, quindi vado avanti e chiedo. Questo è molto più sulla semantica di una vera domanda di programmazione. Attualmente sto scrivendo qualcosa in C++ ma la lingua non ha molta importanza.quanto dovrebbe essere breve una funzione?

Sono ben consapevole del fatto che è buona pratica di programmazione mantenere le proprie funzioni/metodi il più brevi possibile. Eppure come fai a sapere se una funzione è troppo lunga? In alternativa, è mai possibile interrompere troppo le funzioni?

Il primo linguaggio di programmazione che ho appreso (oltre a Applesoft BASIC, che non conta ...) era 6502 linguaggio assembly, in cui velocità e ottimizzazione sono tutto. Nei casi in cui un numero di cicli conta i tempi dell'intero programma, spesso è meglio impostare una posizione di memoria o registrare direttamente piuttosto che saltare a un'altra subroutine. La prima operazione potrebbe richiedere 3 o 4 cicli, mentre, nel complesso, quest'ultima potrebbe impiegarne due o tre volte.

Mentre mi rendo conto che al giorno d'oggi se dovessi menzionare il conteggio dei cicli ad alcuni programmatori mi darebbero solo un'occhiata vuota, è un'abitudine difficile da rompere.

In particolare, diciamo che (ancora una volta utilizzando C++) abbiamo un metodo di classe privata che è qualcosa di simile al seguente:

int Foo::do_stuff(int x) { 
    this->x = x; 
    // various other operations on x 
    this->y = this->x; 
} 

Ho visto alcuni argomenti che, per lo meno, ogni serie di azioni dovrebbero essere la sua funzione Ad esempio, do_stuff() dovrebbe in teoria essere chiamato a set_x (int x), una funzione separata dovrebbe essere scritta per l'insieme di operazioni eseguite sul membro della classe x, e una terza funzione dovrebbe essere scritta per assegnare il valore finale del membro della classe x al membro della classe y. Ma ho visto altri argomenti che ogni operazione dovrebbe avere una sua funzione.

Per me, questo sembra solo sbagliato. Di nuovo, sto guardando le cose da una prospettiva interna; ogni chiamata al metodo sta spingendo un indirizzo nello stack, eseguendo le sue operazioni, quindi ritornando dalla subroutine. Sembra solo un sovraccarico per qualcosa di relativamente semplice.

Esiste una best practice per questo genere di cose o dipende più dal giudizio individuale?

risposta

8

Fin dai tempi dell'assemblaggio 6502, sono accadute due cose: i computer sono diventati molto più veloci ei compilatori (laddove appropriato) sono diventati più intelligenti.

Ora il consiglio è di smettere di spendere tutto il tempo che si preoccupa per i singoli cicli, finché non si è certi che si tratti di un problema. Puoi passare quel tempo in modo più saggio. Se mi dici che il ciclo conta per me, non ti guarderò senza capire perché non so cosa siano. Ti guarderò chiedendoti se stai sprecando il tuo sforzo.

invece, cominciare a pensare di fare le funzioni abbastanza piccolo per essere:

  • comprensibile,
  • verificabile,
  • riutilizzabile, forse, ove ciò sia appropriato.

Se, in seguito, si trova che parte del codice non è sufficientemente veloce, considera come ottimizzarlo.

Nota: l'ottimizzazione potrebbe consistere nel suggerire al compilatore di spostare la funzione in linea, in modo da ottenere comunque i vantaggi sopra, senza il calo di prestazioni.

+0

Grazie, questo è decisamente utile. C++ non è affatto il mio primo linguaggio non di assemblaggio, ma dal momento che non ho mai avuto alcun addestramento formale in qualcosa ho pensato che fosse meglio avere un feedback sulle pratiche di codifica. – xobicvap

+1

+1 per dire di non preoccuparti dei cicli fino a quando non è un problema. L'ottimizzazione prematura è la radice di tutto il male. – riwalk

5

La cosa più importante nel decidere dove suddividere una funzione non è necessariamente quanto la funzione fa. Si tratta piuttosto di determinare l'API della tua classe.

Supponiamo di interrompere Foo::do_stuff in Foo::set_x, Foo::twiddle_x e Foo::set_y. Ha mai senso fare queste operazioni separatamente? Succederà qualcosa di brutto se metto in giro x senza prima averlo impostato? Posso chiamare set_y senza chiamare set_x? Rompendo questi in metodi separati, anche con metodi privati ​​all'interno della stessa classe, si sta implicando che siano almeno potenzialmente operazioni separate.

Se non è il caso, quindi tenerli tutti in una funzione.

+1

+1. Mantenere invarianti e consistenza di stato/oggetti è la chiave del software gestibile. –

+0

Questo è praticamente lo stesso di quello che stavo pensando, ma volevo essere sicuro. Usando l'esempio sopra, in una funzione che sto attualmente scrivendo, non c'è assolutamente alcun motivo per set_y che si verifichi mai senza set_x e twiddle_x che sono stati chiamati per primi. Essendo venuto da altre lingue (non-OOP), io * davvero * apprezzo l'incapsulamento in quanto mi aiuta a definire nella mia testa esattamente come una particolare variabile dovrebbe essere alterata e cosa ha il diritto di alterarla. Ho solo un po 'paura di andare * troppo * fuori bordo con esso. Queste linee guida dovrebbero aiutare. – xobicvap

0

Penso che in qualsiasi base di codice, la leggibilità è molto più preoccupante di un paio di cicli di clock per tutte le ragioni ben note, soprattutto la manutenibilità, che è dove la maggior parte del codice trascorre il suo tempo e, di conseguenza, dove la maggior parte dei soldi è speso per il codice. quindi è una specie di schivare la tua domanda dicendo che le persone non si preoccupano di questo perché è trascurabile rispetto ad altre preoccupazioni [più aziendali].

anche se mettiamo da parte altre preoccupazioni, ci sono istruzioni inline esplicite e, cosa più importante, ottimizzazioni del compilatore che spesso possono rimuovere la maggior parte del sovraccarico in questione con chiamate di funzione banali. il compilatore è una macchina di ottimizzazione incredibilmente intelligente e spesso organizza le chiamate di funzione in modo molto più intelligente di quanto si possa.

+0

Sono d'accordo, anche se per fare l'avvocato del diavolo vorrei dire che penso che almeno una conoscenza del linguaggio assembly sia sicuramente utile in quanto hai almeno qualche idea su come le cose funzionino a livello di CPU. Allo stesso tempo, non mi manca affatto la programmazione degli assiemi. La leggibilità è praticamente nulla nel codice ASM e non solo a causa della terminologia. – xobicvap

+0

@ubtng, d'accordo, non intendevo implicare il contrario. il mio punto era che quando la gente va al tavolo da disegno, la decomposizione della funzione non è in cima alla lista delle priorità. per quanto mi riguarda, il montaggio è fantastico! illeggibile, ma fantastico! – davin

3

Sono ben consapevole che si tratta di una buona pratica di programmazione per mantenere i vostri funzioni/metodi più breve possibile

non vorrei usare i criteri di cui sopra di refactoring le mie funzioni di quelli più piccoli. Qui di seguito è quello che io uso

  1. mantenere tutte le funzioni allo stesso livello di astrazione
  2. assicurarsi che non vi siano effetti collaterali (per eccezionali casi assicurarsi di esplicitamente documento loro)
  3. Assicurarsi una funzione non sta facendo più di una cosa (principio SRP). Ma puoi rompere questo per onorare 1.

Altro good practices for Method Design

  1. Non rendere il cliente fa nulla il modulo potrebbe fare
  2. non violano il principio di minima Stupore
  3. Fail Errori Fast-relazione quanto prima come Possibile dopo che si sono verificati
  4. Sovraccarico con cura
  5. Usa apposito parametro ei tipi restituiti
  6. utilizzare il parametro costante per l'ordinazione Attraverso metodi
  7. evitare lunghe Elenchi dei parametri
  8. evitare i valori di ritorno che la domanda eccezionale lavorazione
2

Dopo aver letto Clean Code: A Handbook of Agile Software Craftsmanship che ha toccato su quasi ogni pezzo di consigli su questa pagina ho iniziato a scrivere più breve. Non per avere funzioni brevi ma per migliorare la leggibilità e la testabilità, tenerle allo stesso livello di astrazione, farle fare solo una cosa, ecc.

Quello che ho trovato più gratificante è che mi ritrovo a scrivere molto meno documentazione perché non è necessario per l'80% delle mie funzioni. Quando la funzione fa solo quello che dice il suo nome, non ha senso riaffermare l'ovvio. Anche i test di scrittura diventano più semplici perché ogni metodo di test può impostare meno e eseguire meno affermazioni. Quando rivisito il codice che ho scritto negli ultimi sei mesi con questi obiettivi in ​​mente, posso apportare più rapidamente il cambiamento che voglio e andare avanti.

Problemi correlati