Проблемы с пониманием того, как чистый код выглядит в реальной жизни


10

В настоящее время я читаю и работаю над «Чистым кодом: Справочник по мастерству гибкого программного обеспечения» Роберта Мартина. Автор говорит о том, как функция должна делать только одну вещь и, следовательно, быть относительно короткой. Конкретно Мартин пишет:

Это означает, что блоки внутри операторов if, операторов else, операторов while и т. Д. Должны быть длиной в одну строку. Вероятно, эта строка должна быть вызовом функции. Это не только уменьшает объем включаемой функции, но также добавляет документальное значение, поскольку вызываемая внутри блока функция может иметь приятное описательное имя.

Это также подразумевает, что функции не должны быть достаточно большими, чтобы содержать вложенные структуры. Следовательно, уровень отступа функции не должен быть больше одного или двух. Это, конечно, облегчает чтение и понимание функций

Это имеет смысл, но, кажется, вступает в противоречие с примерами того, что я вижу как чистый код. Возьмите следующий метод для примера:

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

Этот код взят из репозитория исходного кода Apache Commons по адресу: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/primes/SmallPrimes.java

Метод выглядит очень читабельным для меня. Для реализаций алгоритма, подобных этой (реализация вероятностного критерия простоты Миллера-Рабина), подходит ли сохранить код как есть и все еще считать его «чистым», как определено в книге? Или даже что-то уже столь же читаемое, как эта выгода от извлечения методов, делающих алгоритм по существу последовательным вызовом функций, которые «делают только одну вещь»? Одним из быстрых примеров извлечения метода может быть перемещение первых трех операторов if в такую ​​функцию:

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

Примечание. Этот вопрос отличается от возможного дубликата (хотя этот вопрос и мне полезен), потому что я пытаюсь определить, понимаю ли я намерение автора Чистого кода и приведу конкретный пример, чтобы сделать вещи более бетон.


3
насколько я вижу, эта функция делает только одно ... у нее нет побочных эффектов, которые я вижу. Что заставляет вас думать, что это может быть не чисто? Какую часть этой функции вы считаете достойной включения в другую функцию, чтобы сделать ее чище?
Ньютопия

14
Заголовок вашего вопроса запрашивает ситуацию «в реальной жизни», а затем ваш пример выглядит для меня как идеальный пример нереальной функции (по крайней мере, для 99,9% разработчиков приложений или веб-разработчиков). Конечно, это может быть реальная функция для теоретиков чисел, математиков или ученых, работающих в этой конкретной области.
Док Браун


2
Да, для меня это реальная жизнь, так как я в настоящее время развиваюсь в области вычислительной алгебраической теории чисел :)
1

2
Я бы, вероятно, рефакторинг getTFactor (), как вы описываете.
user949300

Ответы:


17

«Чистый код» - это не самоцель, а средство для достижения цели. Основная цель преобразования больших функций в более мелкие и очистки кода другими способами состоит в том, чтобы обеспечить возможность развития и поддержки кода.

Когда из учебника выбирают такой специфический математический алгоритм, как простой тест Миллера-Рабина, большинство программистов не хотят его развивать. Их стандартная цель состоит в том, чтобы правильно перенести его из псевдокода учебника на язык программирования их среды. Для этой цели я бы рекомендовал как можно ближе следовать учебнику, что, как правило, означает не проводить рефакторинг.

Тем не менее, для кого-то, работающего в качестве математика в этой области, который пытается работать над этим алгоритмом и изменить или улучшить его, ИМХО разделение этой функции на более мелкие, хорошо именованные или замена группы «магических чисел» именованными константами, может помочь сделать изменения в коде проще, как и для любого другого вида кода.


1
Это именно то, что я искал. У меня были проблемы с определением того, когда использовать методы чистого кода в той области, в которой я работаю. Ваш ответ обеспечивает ясность, которую я искал. Спасибо!
1
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.