Probleme beim Erfassen, wie sauberer Code im wirklichen Leben aussieht

10

Ich lese und arbeite gerade an "Clean Code: Ein Handbuch für agile Software-Handwerkskunst" von Robert C. Martin. Der Autor spricht darüber, wie eine Funktion nur eines tun und daher relativ kurz sein soll. Insbesondere schreibt Martin:

Dies bedeutet, dass die Blöcke in if-Anweisungen, else-Anweisungen, while-Anweisungen usw. eine Zeile lang sein sollten. Wahrscheinlich sollte diese Zeile ein Funktionsaufruf sein. Dies hält nicht nur die umschließende Funktion klein, sondern erhöht auch den Dokumentationswert, da die innerhalb des Blocks aufgerufene Funktion einen gut beschreibenden Namen haben kann.

Dies bedeutet auch, dass Funktionen nicht groß genug sein sollten, um verschachtelte Strukturen aufzunehmen. Daher sollte der Einzugspegel einer Funktion nicht größer als eins oder zwei sein. Dies erleichtert natürlich das Lesen und Verstehen der Funktionen

Dies ist sinnvoll, scheint jedoch im Widerspruch zu Beispielen zu stehen, die ich als sauberen Code betrachte. Nehmen Sie zum Beispiel die folgende Methode:

    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
    }
}

Dieser Code stammt aus dem Apache Commons-Quellcode-Repo unter: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/primes/SmallPrimes.java

Die Methode sieht für mich sehr lesbar aus. Ist es für Algorithmus-Implementierungen wie diese (Implementierung des Miller-Rabin Probabilistic Primality Test) geeignet, den Code unverändert zu lassen und ihn dennoch als "sauber" zu betrachten, wie im Buch definiert? Oder würde sogar etwas bereits so Lesbares wie dieses davon profitieren, Methoden zu extrahieren, um den Algorithmus im Wesentlichen zu einer Reihe von Aufrufen von Funktionen zu machen, die "nur eine Sache tun"? Ein schnelles Beispiel für eine Methodenextraktion könnte darin bestehen, die ersten drei if-Anweisungen in eine Funktion wie die folgende zu verschieben:

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;
    }

Hinweis: Diese Frage unterscheidet sich von dem möglichen Duplikat (obwohl diese Frage auch für mich hilfreich ist), da ich versuche festzustellen, ob ich die Absicht des Autors von Clean Code verstehe, und ein spezifisches Beispiel gebe, um die Dinge zu verbessern Beton.

1west
quelle
3
Soweit ich sehen kann, macht diese Funktion nur eines ... sie hat keine Nebenwirkungen, die ich sehen kann. Was lässt Sie denken, dass es möglicherweise nicht sauber ist? Welchen Teil dieser Funktion würden Sie für würdig halten, in eine andere Funktion aufgenommen zu werden, um sie sauberer zu machen?
Newtopian
14
Ihr Fragentitel fragt nach der "realen" Situation, und dann sieht Ihr Beispiel für mich wie ein perfektes Beispiel für eine nicht reale Funktion aus (zumindest für 99,9% der Anwendungs- oder Webentwickler). Es kann natürlich eine reale Funktion für Zahlentheoretiker, Mathematiker oder Informatiker sein, die auf diesem speziellen Gebiet arbeiten.
Doc Brown
2
Ja, für mich ist dies das wirkliche Leben, wie ich es derzeit auf dem Gebiet der rechnergestützten algebraischen Zahlentheorie
entwickle
2
Ich würde wahrscheinlich getTFactor () wie von Ihnen beschrieben umgestalten.
user949300

Antworten:

17

"Clean Code" ist kein Selbstzweck, sondern ein Mittel zum Zweck. Der Hauptzweck der Umgestaltung größerer Funktionen in kleinere und der Bereinigung des Codes auf andere Weise besteht darin, den Code entwicklungsfähig und wartbar zu halten.

Wenn die meisten Programmierer einen so spezifischen mathematischen Algorithmus wie den "Miller-Rabin" -Primetest aus einem Lehrbuch auswählen, möchten sie ihn nicht weiterentwickeln. Ihr Standardziel ist es, es vom Pseudocode des Lehrbuchs korrekt in die Programmiersprache ihrer Umgebung zu übertragen. Zu diesem Zweck würde ich empfehlen, das Lehrbuch so genau wie möglich zu befolgen, was normalerweise bedeutet, nicht umzugestalten.

Für jemanden, der als Mathematiker auf diesem Gebiet arbeitet und versucht, an diesem Algorithmus zu arbeiten und ihn zu ändern oder zu verbessern, kann IMHO diese Funktion in kleinere, gut benannte aufteilen oder die Gruppe der "magischen Zahlen" durch benannte Konstanten ersetzen helfen dabei, Änderungen am Code wie bei jeder anderen Art von Code zu vereinfachen.

Doc Brown
quelle
1
Genau das habe ich gesucht. Ich hatte Probleme zu bestimmen, wann ich die Clean-Code-Praktiken in dem Bereich verwenden soll, in dem ich mich entwickle. Ihre Antwort bietet die Klarheit, nach der ich gesucht habe. Vielen Dank!
1west