Quando l'estrazione dei metodi dal codice si interrompe per avere un senso? [duplicare]

3

Attualmente sto studiando i metodi di refactoring definiti da Marting Fowler ( link ).

Dichiara un consiglio per la sostituzione di blocchi di codice con un singolo metodo che fa quel lavoro. Finora, sono d'accordo, come tutti abbiamo imparato a conoscere gli aspetti negativi di Spaghetti-code. Ma l' esempio per questa regola ha il seguente aspetto:

Sostituzione

    void printOwing() {
      printBanner();

      //print details
      System.out.println ("name:  " + _name);
      System.out.println ("amount " + getOutstanding());
    }

da

    void printOwing() {
      printBanner();
      printDetails(getOutstanding());
    }

    void printDetails (double outstanding) {
      System.out.println ("name:  " + _name);
      System.out.println ("amount " + outstanding);
    }

La leggibilità e quindi la comprensione immediata del codice sono davvero migliori nel secondo esempio? Non vi è alcuna indicazione su quali "dettagli" siano in riferimento a "dover". Ad esempio, verrà elencato il nome o esiste un altro metodo printName ()? Anche l'interesse è elencato? Avrei bisogno di cercare l'implementazione del metodo printDetail () per scoprirlo.

Il metodo printOwing () stesso è già un metodo di stampa. Non sarebbe più semplice mantenere il codice per elencare semplicemente System.out.println () in questo metodo commentando lo scopo come nel primo esempio invece di "sparpagliare" il codice in questo modo?

Esiste una regola generale su quando interrompere "la metodologia" e quando ha ancora senso?

    
posta calotra 27.06.2014 - 11:28
fonte

2 risposte

4

Is the readability and thus, the immediate understanding of the code really better in the latter example?

Penso che lo sia. Guardandolo, puoi vedere che printOwing "stampa un banner e alcuni dettagli".

There is no indication about what "details" are in reference to "owing".

Se non è chiaro o noto durante la lettura del nome dell'API, l'API non dovrebbe essere chiamata printDetails in primo luogo (printDebtByName forse?)

For example, will the name be listed or is there another method printName()?

Dovresti vedere ciò osservando l'API oi documenti API.

I would need to search for the printDetail() method's implementation to find out about that.

In realtà dovresti semplicemente scegliere un nome migliore (rispetto a printDetails ). Questo è il motivo per cui la denominazione delle cose è difficile nella progettazione delle API. Questo è anche il motivo per cui è fondamentale. (Stepanov ha affermato in una conferenza che, per ottenere un buon design, è necessario spendere una decina di volte in più rispetto alla progettazione dell'API che si spendono per l'implementazione).

The method printOwing() itself is already a print-method. Would it not be easier for maintaining the code to just list the System.out.println()'s in this method commenting the purpose as in the first example instead of "scattering" the code this way?

Non proprio, anche se è più facile scrivere in una singola funzione in primo luogo (basta scrivere tutto lì e hai finito). Per la manutenzione a lungo termine, è fondamentale che il codice sia facile da leggere e comprendere, non (necessariamente) da scrivere.

Tuttavia, quando si esegue una manutenzione a lungo termine, è più facile refactoring se si dispone di un cambiamento così piccolo, piuttosto che se si dispone di un codice che potrebbe essere stato refactored un numero di volte, ma la gente ha capito "va bene dove è" ognuna di queste volte (tale codice diventa sempre più grande e quindi devi rifattorizzare un metodo di grandi dimensioni, che sarebbe stato naturalmente rotto n volte).

Più grande è il progetto, più è critico minimizzare lo sforzo di comprensione.

Is there a rule of thumb about when to stop "methodizing" and when it still makes sense?

Le linee guida di progettazione che (probabilmente) sono state applicate nella decisione di refactoring:

  • mantenendo livelli di astrazione simili per API simili (sia printHeader che printDetails hanno un livello di astrazione simile); printOwed non chiama più API con diversi livelli di astrazione.

  • SRP: printOwned non "stampa l'intestazione e quindi stampa il nome e quindi l'importo". Invece, stampa "parti di proprietà"; Concettualmente, è una cosa diversa.

  • Legge di Demeter: minimizzi la quantità di conoscenza necessaria per ogni API (printOwned non ha bisogno di sapere che qualcosa come System.out.println esiste affatto).

risposta data 27.06.2014 - 14:49
fonte
5

Una cosa da capire è che gli esempi nei libri sono lì per illustrare le tecniche, non necessariamente per mostrare l'apice delle migliori pratiche. Quindi se il punto debole del refactoring era in realtà quello di estrarre le funzioni a 50 righe da quelle a 500 righe, ogni esempio nel libro richiederebbe diverse pagine e oscurerebbe il punto reale.

Ciò che conta non è necessariamente la dimensione dell'esempio, ma puoi vedere e capire la tecnica utilizzata.

Nell'esempio specifico, il commento "dettagli di stampa" suggerisce un odore che può essere rimosso estraendo una funzione print_details() - meglio rendere la funzionalità esplicita e autodocumentante piuttosto che fare affidamento sui commenti.

Tuttavia, a meno che print_details() avesse altri usi, la mia opinione personale è che il refactoring sarebbe un make-work che richiede tempo lontano da cose più importanti.

Questa è solo la mia opinione: tuttavia sto leggendo Codice completo al momento e trovo molto su cui concordare: un punto che fa è che gli studi mostrano effettivamente un errore inferiore velocità in funzioni di medie dimensioni (50-150 linee) rispetto a molte funzioni minuscole o ad alcune funzioni enormi.

Questo jive con la mia esperienza (principalmente non C ++) è però in disaccordo con un sacco di consigli. Vedere i dati che supportano funzioni così grandi è una vera sorpresa ... "non sono solo io allora".

Prendi anche questo con un pizzico di sale:

  1. C ++ potrebbe essere diverso dalle altre lingue nella dimensione della funzione ottimale
  2. Se la tua esperienza suggerisce un diverso ottimale: 10 o 20 funzioni di linea, ad esempio, allora non sprecherei tempo ad aggregarle sul mio o Steve McConnell per dire così
  3. ma sono d'accordo sul fatto che rompere una funzione a 4 linee per motivi di refactoring è improbabile che valga la pena (a meno che ogni linea sia molto difficile da capire!)
risposta data 27.06.2014 - 11:54
fonte

Leggi altre domande sui tag