Attualmente sto leggendo e lavorando su "Clean Code: A Handbook of Agile Software Craftsrafts" di Robert C. Martin. L'autore parla di come una funzione dovrebbe fare solo una cosa, e quindi essere relativamente breve. In particolare Martin scrive:
Ciò implica che i blocchi all'interno delle istruzioni if, else, while, e così via dovrebbero essere lunghi una riga. Probabilmente quella linea dovrebbe essere una chiamata di funzione. Questo non solo mantiene piccola la funzione che racchiude, ma aggiunge anche valore documentale perché la funzione chiamata all'interno del blocco può avere un nome ben descrittivo.
Ciò implica anche che le funzioni non dovrebbero essere abbastanza grandi da contenere strutture nidificate. Pertanto, il livello di rientro di una funzione non deve essere maggiore di uno o due. Ciò, ovviamente, semplifica la lettura e la comprensione delle funzioni
Questo ha senso, ma sembra essere in conflitto con esempi di quello che vedo come codice pulito. Prendi ad esempio il seguente metodo:
public static boolean millerRabinPrimeTest(final int n) {
final int nMinus1 = n - 1;
final int s = Integer.numberOfTrailingZeros(nMinus1);
final int r = nMinus1 >> s;
//r must be odd, it is not checked here
int t = 1;
if (n >= 2047) {
t = 2;
}
if (n >= 1373653) {
t = 3;
}
if (n >= 25326001) {
t = 4;
} // works up to 3.2 billion, int range stops at 2.7 so we are safe :-)
BigInteger br = BigInteger.valueOf(r);
BigInteger bn = BigInteger.valueOf(n);
for (int i = 0; i < t; i++) {
BigInteger a = BigInteger.valueOf(SmallPrimes.PRIMES[i]);
BigInteger bPow = a.modPow(br, bn);
int y = bPow.intValue();
if ((1 != y) && (y != nMinus1)) {
int j = 1;
while ((j <= s - 1) && (nMinus1 != y)) {
long square = ((long) y) * y;
y = (int) (square % n);
if (1 == y) {
return false;
} // definitely composite
j++;
}
if (nMinus1 != y) {
return false;
} // definitely composite
}
}
return true; // definitely prime
}
}
Questo codice è tratto dal repository del codice sorgente di Apache Commons all'indirizzo: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/primes/SmallPrimes.java
Il metodo mi sembra molto leggibile. Per implementazioni di algoritmi come questo (implementazione del test di primalità probabilistica di Miller-Rabin), è opportuno mantenere il codice così com'è e ancora considerarlo "pulito", come definito nel libro? O anche qualcosa di già leggibile come questo trarrebbe beneficio dall'estrazione di metodi per rendere l'algoritmo essenzialmente una serie chiama a funzioni che "fanno solo una cosa"? Un rapido esempio di estrazione di un metodo potrebbe essere lo spostamento delle prime tre istruzioni if in una funzione come:
private static int getTValue(int n)
{
int t = 1;
if (n >= 2047) {
t = 2;
}
if (n >= 1373653) {
t = 3;
}
if (n >= 25326001) {
t = 4;
}
return t;
}
Nota: questa domanda è diversa dal possibile duplicato (anche se quella domanda è utile anche a me), perché sto cercando di determinare se sto capendo l'intenzione dell'autore di Clean Code e sto fornendo un esempio specifico per rendere le cose più calcestruzzo.