Il modo più amichevole per ordinare le definizioni dei metodi di classe?


37

In ogni definizione di classe, ho visto le definizioni dei metodi ordinate in vari modi: alfabetico, cronologico basato sull'uso più comune, alfabetico raggruppato per visibilità, alfabetico con getter e setter raggruppati insieme, ecc. Quando inizio a scrivere una nuova classe, Tendo a digitare tutto, quindi riordinare quando ho finito di scrivere l'intera classe. In quella nota, ho tre domande:

  1. L'ordine è importante?
  2. C'è un ordine "migliore"?
  3. Immagino di no, quindi quali sono i pro e i contro delle diverse strategie di ordinazione?

1
Non ti aspetti davvero che le persone tagliano / incollino il codice semplicemente per riordinare i metodi. Se l'IDE lo fa automaticamente, allora va bene. Altrimenti, è difficile da applicare.
Reactgular

Per chiarire, la mia domanda riguarda la leggibilità / usabilità, non la sintassi.
Johntron,

Risposte:


51

In alcuni linguaggi di programmazione, l'ordine è importante perché non è possibile utilizzare le cose fino a quando non sono state dichiarate. Ma a parte questo, per la maggior parte delle lingue non importa al compilatore. Quindi, sei rimasto con ciò che conta per gli umani.

La mia citazione preferita di Martin Fowler è: Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Quindi direi che l'ordinamento della tua classe dovrebbe dipendere da ciò che rende facile per gli umani capire.

Personalmente preferisco il trattamento dimensionale che Bob Martin dà nel suo Clean Codelibro. Le variabili membro nella parte superiore della classe, quindi i costruttori, quindi tutti gli altri metodi. E ordinate che i metodi siano vicini a come vengono usati all'interno della classe (piuttosto che mettere arbitrariamente tutto il pubblico, poi il privato, quindi il protetto). Lo chiama minimizzando la "distanza verticale" o qualcosa del genere (non avere il libro su di me al momento).

Modificare:

L'idea di base della "distanza verticale" è che vuoi evitare di far saltare le persone intorno al tuo codice sorgente solo per capirlo. Se le cose sono correlate, dovrebbero essere più vicine tra loro. Le cose non correlate possono essere più distanti.

Il capitolo 5 di Clean Code (ottimo libro, a proposito) spiega in dettaglio come il signor Martin suggerisce di ordinare il codice. Suggerisce che leggere il codice dovrebbe funzionare in qualche modo come leggere un articolo di giornale: i dettagli di alto livello vengono prima di tutto (in alto) e ottieni maggiori dettagli mentre leggi. Dice "Se una funzione chiama un'altra, dovrebbero essere vicini verticalmente e il chiamante dovrebbe essere al di sopra della chiamata, se possibile". Inoltre, i concetti correlati dovrebbero essere vicini.

Quindi ecco un esempio inventato che è male in molti modi (cattivo design OO; non usare mai doubleper soldi) ma illustra l'idea:

public class Employee {
  ...
  public String getEmployeeId() { return employeeId; }
  public String getFirstName() { return firstName; }
  public String getLastName() { return lastName; }

  public double calculatePaycheck() {
    double pay = getSalary() / PAY_PERIODS_PER_YEAR;
    if (isEligibleForBonus()) {
      pay += calculateBonus();
    }
    return pay;
  }

  private double getSalary() { ... }

  private boolean isEligibleForBonus() {
    return (isFullTimeEmployee() && didCompleteBonusObjectives());
  }

  public boolean isFullTimeEmployee() { ... }
  private boolean didCompleteBonusObjectives() { ... }
  private double calculateBonus() { ... }
}

I metodi sono ordinati in modo che siano vicini a quelli che li chiamano, scendendo dall'alto. Se avessimo messo tutti i privatemetodi al di sotto di publicquelli, allora dovresti fare di più saltando in giro per seguire il flusso del programma.

getFirstNamee getLastNamesono concettualmente correlati (e getEmployeeIdprobabilmente lo è anche), quindi sono vicini tra loro. Potremmo spostarli tutti verso il basso, ma non vorremmo vedere getFirstNamein alto e getLastNamein basso.

Spero che questo ti dia l'idea di base. Se sei interessato a questo genere di cose, ti consiglio vivamente di leggere Clean Code.


Devo sapere come devono essere posizionati setter e getter di variabili di istanza. Dovrebbe venire subito dopo il costruttore della classe o in fondo alla classe?
srinivas,

Personalmente mi piacciono in alto dopo il costruttore. Ma non importa davvero; Consiglierei la coerenza nel tuo progetto e con il tuo team come un buon modo per decidere.
Allan,

Non dovrebbe calculateBonus()venire prima isFullTimeEmployee()e didCompleteBonusObjectives()?
winklerrr,

@winklerrr Riesco a vedere un argomento per questo. Li ho posizionati dove ho fatto perché isFullTimeEmployeee didCompleteBonusObjectivessono usati da isEligibleForBonuscosì dovrebbero essere vicini verticalmente. Ma potresti potenzialmente spostarti calculateBonusverso l'alto per avvicinarlo a come viene chiamato. Sfortunatamente, poiché hai funzioni che chiamano funzioni, alla fine si verificano problemi (come una funzione condivisa chiamata da più altre) in cui non esiste un ordinamento perfetto. Quindi è lasciato al tuo miglior giudizio. Consiglio di mantenere piccole classi e funzioni per mitigare questi problemi.
Allan,

2

In genere ordino i miei metodi in base alla relazione e all'ordine di utilizzo.

Prendi una classe di networking, raggrupperò tutti i metodi TCP insieme, quindi tutti i metodi UDP insieme. Il TCP interno avrebbe il metodo di installazione come il primo, forse inviare un dato messaggio secondo e chiudere il socket tcp come terzo.

Ovviamente non tutte le classi si adatteranno a quel modello, ma quello è il mio flusso di lavoro generale.

Lo faccio in questo modo per il debug più di ogni altra cosa, quando ho un problema e voglio saltare al metodo, non penso come sia scritto, penso di cosa sia responsabile e vado in quella sezione.

In questo modo, in particolare, ha senso che una terza parte visualizzi / utilizzi il tuo codice come raggruppato e seguirà l'ordine in cui viene utilizzato, quindi il codice che scriveranno con la tua classe seguirà la stessa struttura della classe.

Per quanto riguarda?

per leggibilità, sicuramente.

diverso da quello non proprio, solo nei casi in cui alcune lingue non è possibile chiamare il metodo a meno che non sia definito sopra dove è chiamato ecc.


0

Idealmente, le tue lezioni sono così piccole che non importa. Se hai solo una dozzina di metodi e il tuo editor o IDE supporta la piegatura, non hai problemi, perché l'intero elenco di metodi si adatta a 12 righe.

In caso contrario, la distinzione di livello superiore dovrebbe essere pubblica vs. privata. Elenca prima i metodi pubblici: questi sono ciò che le persone cercheranno di più, perché definiscono il modo in cui la tua classe si interfaccia con il resto della base di codice.

Quindi, all'interno di ciascuno di questi, ha più senso raggruppare i metodi in base alla funzionalità: costruttori e distruttori in un blocco, getter / setter in un altro, sovraccarichi dell'operatore, metodi statici e il resto del gruppo. In C ++, tuttavia, mi piace stare operator=vicino ai costruttori, perché è strettamente correlato al costruttore di copie e anche perché voglio essere in grado di individuare rapidamente se tutto (o nessuno) di ctor predefinito, copia ctor, operator = e dtor esistere.


-1

tl; dr

Solo se la lingua in cui lavori richiede un ordine specifico. A parte questo, l'ordinazione dipende da te. Scegli un sistema che sia coerente e sensato e prova a rispettarlo il più possibile.


1 L'ordine è importante?

Solo se la lingua che stai lavorando deve avere una funzione definita prima nel file rispetto a dove viene chiamata, come in questo esempio:

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

riceverai un errore perché chiami funcB()prima di usarlo. Penso che questo sia un problema in PL / SQL e possibilmente anche in C, ma puoi avere dichiarazioni anticipate come:

void funcB();

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

Questa è l'unica situazione a cui riesco a pensare dove se l'ordine è "sbagliato", non sarai nemmeno in grado di compilare.

Altrimenti, puoi sempre riordinarli come preferisci. Probabilmente potresti anche scrivere uno strumento per farlo per te (se non riesci a trovarne uno là fuori).

2 C'è un ordine "migliore"?

Se la lingua / l'ambiente non ha requisiti di ordinazione, l'ordine "migliore" è quello che funziona meglio per te . Per me, mi piace avere tutti i getter / setter insieme, di solito all'inizio della classe (ma dopo i costruttori / inizializzatori statici), e poi i metodi privati, quindi protetti, quindi pubblici. In ogni gruppo basato sull'ambito di solito non esiste un ordinamento, sebbene i metodi sovraccarichi che cerco di tenere insieme, in ordine di numero di parametri. Cerco anche di mantenere insieme i metodi con funzionalità correlate, anche se a volte devo interrompere il mio ordinamento basato sull'ambito per farlo; e talvolta tentando di mantenere l'ordinamento basato sull'ambito interrompe il raggruppamento per funzionalità. E il mio IDE può darmi una vista in ordine alfabetico, quindi va bene lo stesso. ;)

Alcuni linguaggi, come C #, hanno la capacità di raggruppare il codice in "regioni" che non hanno alcun effetto sulla compilazione ma potrebbero rendere più semplice tenere insieme le funzioni correlate, quindi nasconderle / visualizzarle con l'IDE. Come ha sottolineato MainMa , ci sono alcuni che lo considerano una cattiva pratica. Ho visto esempi positivi e negativi di regioni utilizzate in questo modo, quindi se hai intenzione di procedere in questo modo, fai attenzione.

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.