Магические числа против именованных констант - PullRequest
15 голосов
/ 04 марта 2009

При написании кода, особенно при работе с датами и временем, вам приходится работать с большим количеством конкретных чисел, например: 60 секунд в минуту, 3600 = секунд в час.

Некоторые люди предпочитают использовать необработанные значения для многих из них, тогда как другие помещают их в константы для повышения читабельности.

например:

$x = time() + 3600;
$y = time() + 86400;
$z = time() + 604800;

// vs

define('MINUTE', 60);
define('HOUR',   60 * MINUTE);   // 3600
define('DAY',    24 * HOUR);     // 86400
define('WEEK',    7 * DAY);      // 604800

$x = time() + HOUR;
$y = time() + DAY;
$z = time() + WEEK;

Конечно, второе легче читать, но немного OTT для некоторых нижних значений, поэтому где именно вы рисуете линию? Лично я не вижу проблем с читаемостью 86400 (в своей голове я автоматически читаю это как «24 часа»), но нарисую линию на постоянной НЕДЕЛИ.

Ответы [ 22 ]

40 голосов
/ 04 марта 2009

86400 не в порядке, так как вы можете легко напечатать его как 84600, 88400 и т. Д.

Неправильная константа будет ошибкой компиляции

16 голосов
/ 04 марта 2009

Один из моих профессоров однажды сказал нам не включать магические числа в наш код, кроме 1, -1 и 0. Это немного экстремально, но это запоминается и все еще направляет меня, хотя я не придерживаюсь это полностью.

В вашем примере я бы предпочел символические имена во всех случаях.

8 голосов
/ 04 марта 2009

Я бы использовал константы (или некоторую простую производную, например, соглашение 15.minutes Rails) почти везде. Для меня это упрощение «печатания» всего этого; если я вижу «10 * МИНУТ» где-то в строке, я знаю, что я имею дело со временем (или кто-то готов к удару задом) Если я увижу 10 * 60 или 600, вполне возможно, что я не пойму, что мы так легко справляемся со временем.

6 голосов
/ 04 марта 2009

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

Если бы я поддерживал ваш код, я бы предпочел видеть именованные константы, такие как:

const int secondsInDay = 86400;

.. не много двусмысленности там. :) Зависит от того, потребуется ли кому-либо (в том числе и вам ... я имею в виду, что мне сложно вспомнить, что я написал на прошлой неделе, не говоря уже о прошлом году!), Чтобы поддерживать ваш код на каком-то этапе.

4 голосов
/ 04 марта 2009

Я говорю своим ученикам это:

Если вы можете прочитать код без комментарии, то нет необходимости константы. Если вам нужно иметь объясни код тогда надо комментарии.

Я также говорю им:

Думать плохо. Во что если ты пишешь код, который заставляет людей копать через это, чтобы понять, что это не очень хорошая вещь.

Итак, если вы можете показать строки сотруднику, и они могут понять это без констант, вы (вероятно) хороши без них. Скорее всего, вам понадобятся постоянные.

4 голосов
/ 04 марта 2009

Мой подход заключается в том, чтобы не использовать именованные константы, но разделять единицы следующим образом:

long twelvePM = 12 * 60 * 60 * 1000L;
long timeout = 60 * 1000L;

Таким образом, ясно, что это в миллисекундах, и их легко настроить, если я захочу изменить значения позже.

4 голосов
/ 04 марта 2009

Лично я бы использовал SECS_PER_MIN, SECS_PER_HOUR и т. Д. Мне даже было известно, что иногда использовал NANOS_PER_SEC. Я всегда делал бы это, если бы в языке не было научной нотации целочисленных литералов.

Речь идет не о читабельности. Причина использования SECS_PER_DAY вместо 86400 заключается не только в том, какие общие знания мы ожидаем от читателя. Речь идет о самодокументирующемся коде, который означает однозначную демонстрацию вашей работы.

Конечно, все знают, что есть 60 секунд в минуту. Но они также знают, что есть 60 минут в час. Если вы используете литерал 60, вероятно, очевидно, для чего предназначен ваш код. Но зачем рисковать?

Если вы используете SECS_PER_MIN, то совершенно очевидно, что вы конвертируете время в минутах (в вашем примере всего 1 минуту) в время в секундах. Например, вы не добавляете один час к времени, измеренному в минутах, или один градус к углу в минутах. Вы не добавляете 5 футов к длине, измеряемой в дюймах.

Именованные константы предоставляют больше контекста для окружающего кода. Для вашего примера добавления времени мы просто знаем по одной строке, что $ x должно быть временем в секундах. Название константы повторяет, что $ x - это не время в миллисекундах, или такты, или микрофины. Это облегчает для всех проверку и поддержание правильности единиц: они могут сказать, что то, что вы намеревались сделать, это то, что вы на самом деле сделали. Им даже не нужно даже думать о том, что вы намеревались добавить 60 миллисекунд к времени, измеренному в миллисекундах, но ошиблись, потому что $ x фактически был в секундах.

Именованные константы также помогают избежать опечаток. Конечно, все знают, что в день есть 86400 секунд. Из этого не обязательно следует, что они не будут опечатывать это как 84600, или что они сразу же обнаружат ошибку, если кто-то другой обнаружит. Конечно, у вас есть полное тестирование, поэтому такая ошибка никогда не попадет в поле, но самый эффективный способ ее исправить - это предотвратить ошибочный код, который в первую очередь проверяет его. Поэтому я бы определил константы, как это (или любой другой синтаксис):

SECS_PER_MIN := 60; 
SECS_PER_HOUR := 60 * SECS_PER_MIN;
SECS_PER_DAY := 24 * SECS_PER_HOUR;
SECS_PER_WEEK := 7 * SECS_PER_DAY;

Или, если в любом случае нужны были другие константы (чего в случае времени они, вероятно, не сделали бы, потому что при первой возможности вы нормализуете все в секунды, чтобы уменьшить вероятность путаницы):

SECS_PER_MIN := 60;
MINS_PER_HOUR := 60;
HOURS_PER_DAY := 24;
DAYS_PER_WEEK := 7;

SECS_PER_HOUR := SECS_PER_MIN * MINS_PER_HOUR;
etc.

Обратите внимание на порядок на RHS: минуты явно «отменяются», делая работу еще более четкой. Не так уж сложно со временем, но хорошо установить согласованную схему на ранней стадии, чтобы, когда дела пойдут на спад позже (CLOCKS_PER_SEC, PLANCK_LENGTHS_PER_PARSEC), вы могли сделать это правильно, используя знакомые приемы.

3 голосов
/ 04 марта 2009

Для использования в таких единицах, хороший трюк - назвать все единицы (даже основание), чтобы ваш код читался как правильно заданное измерение:

// The base unit of time is the second
const double second = 1.0;
const double ns = 1e-9 * second;
const double micros = 1e-6 * second;
const double ms = 1e-3 * second;
const double minute = 60.0 * second;
const double hour = 60 * minute;
const double day = 24 * hour;
const double week = 7 * day;
const double year = 365.24 * day; 

затем всегда используйте соответствующую единицу измерения в вашем коде

// Set up a 90 second timeout
timeout(90*second);

или

elapsedDays = floor(elapsedtime / day);

Время от времени вы видите эту формулировку в различных научных пакетах (например, Geant4 ).

2 голосов
/ 04 марта 2009

держу в простом; имя переменной и комментарий (при необходимости в случае очень магических чисел) будет достаточным для прохождения проверки кода.

int someDelay = 1232323; // in milliseconds.
2 голосов
/ 04 марта 2009

Я склонен использовать константы над магическими числами почти исключительно. Я думаю, что он легко читается и дает вам одно очко в программе, чтобы исправить любые ошибки. Например, существует несколько магических 60-х: 60 секунд в минуту, 60 минут в час.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...