Ответ 1
86400 - это не нормально, так как вы можете легко обмануть его как 84600, 88400 и т.д.
Ошибочная константа будет ошибкой компиляции
При написании кода, особенно при работе с датами и временем, вам нужно работать со множеством конкретных чисел, например: 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 часа" ), но нарисую линию на постоянной WEEK.
86400 - это не нормально, так как вы можете легко обмануть его как 84600, 88400 и т.д.
Ошибочная константа будет ошибкой компиляции
Один из моих профессоров однажды сказал нам, чтобы мы не вводили никаких магических чисел в наш код, кроме 1, -1 и 0. Это немного экстремально, но оно у меня в голове и по-прежнему направляет меня, хотя я не придерживаюсь это полностью.
В вашем примере я бы предпочел символические имена во всех случаях.
Я бы поместил константы (или какой-нибудь cutey производный, как Rails '15.minutes
) почти везде. Для меня это упрощает "типизацию" всего этого; если я увижу "10 * MINUTES" где-то в очереди, я знаю, что имею дело со временем (или кто-то, кто за задницу). Если я увижу 10 * 60 или 600, вполне возможно, что я не могу понять, что мы имеем дело со временем довольно легко.
Существует относительно хороший аргумент, тогда любое число, отличное от нуля, или одно должно быть именованной константой. Даже в вашем примере, где вы утверждаете, что у вас нет проблем с читабельностью 86400, по-прежнему существует какая-то двусмысленность в отношении вашей единицы измерения.
Если бы я поддерживал ваш код, я бы предпочел увидеть именованные константы вроде:
const int secondsInDay = 86400;
.. не так много двусмысленности.:) В зависимости от того, кто (включая себя.. Я имею в виду, я изо всех сил пытаюсь вспомнить то, что я написал на прошлой неделе, не говоря уже о прошлом году!) Будет необходимо для поддержания вашего кода на определенном этапе.
Мой подход заключается в том, чтобы не использовать именованные константы, но не разделять единицы следующим образом:
long twelvePM = 12 * 60 * 60 * 1000L;
long timeout = 60 * 1000L;
Таким образом, ясно, что это в миллисекундах, и их легко настроить, если я хочу изменить значения позже.
Я говорю своим ученикам следующее:
Если вы можете прочитать код без комментариев, тогда нет необходимости константы. Если вы должны иметь объясните код, который вам нужен комментарии.
Я также говорю им:
Мышление плохое. В том случае, если вы пишете код, который заставляет людей копать через это, чтобы понять это, что нехорошо.
Итак, если вы можете показать строки сотруднику, и они могут понять это без констант, вы (вероятно) хорошо без них. Вероятно, вам понадобятся константы.
Лично я бы использовал 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), вы можете получить это правильно, используя знакомые методы.
Для использования в таких устройствах хороший трюк состоит в том, чтобы называть все блоки (даже базовые), таким образом ваш код читается как правильно заданное измерение:
// 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) время от времени.
Я имею тенденцию использовать константы по магическим числам почти исключительно. Я думаю, что он увеличивается с готовностью и дает вам одну точку в программе, чтобы исправить любые ошибки. Есть несколько магических 60-х, например: 60 секунд в минуту, 60 минут в час.
Я держусь в простой форме; имя переменной и комментарий (если необходимо в случае очень магических чисел) будет достаточным для прохождения обзора кода.
int someDelay = 1232323; // in milliseconds.
Мое эмпирическое правило при обучении (так, более жесткие основные правила, чем в реальной жизни) было любым числом, отличным от -1, 0, 1 или 2, которое использовалось более одного раза. НЕОБХОДИМО быть константой. Если вы используете его только один раз, вы можете просто прокомментировать его, если хотите...
Я бы нарисовал линию на основе размера проекта. Чем больше проект, тем больше абстракций и констант... Проще всего.
У дня всегда будет 24 часа, а час будет всегда 60 минут.
Я думаю, что смысл называть их как описательные константы, где a) числовое значение нечеткое (604800?) б) значение может когда-нибудь измениться
Лично мне нравится иметь именованные константы в случае, если мне нужно изменить значение константы, тогда мне нужно сделать это только в одном месте.
Я бы определенно использовал константы, в то время как вы можете смотреть на 86400 и в течение 24 часов, будущий программист, выполняющий техническое обслуживание, может быть не таким ярким и будет царапать голову, что именно означает это число. Это также относится к вам, если вы вернетесь в один прекрасный день и забыли, что означает 86400.
$x = time() + HOUR; $y = time() + DAY; $z = time() + WEEK;
Как насчет магических строк? Я обычно делаю так:
$x = strtotime('now + 1 hour');
$y = strtotime('now + 1 day');
$z = strtotime('now + 1 week');
Да, он, вероятно, работает дробно медленнее. Но в большей схеме вещей, действительно ли это имеет значение?
Я могу просить о нижних видах здесь, но я считаю, что ни один из ваших примеров не будет волшебным числом. Это хорошо определенные универсальные константы, которые вам никогда не придется заменять чем-то другим.
Мой подход представляет собой сочетание некоторых из предыдущих ответов:
// properly named variables
int daysBeforeFriday = 4;
// expanded math expressions
int minutesBeforeFriday = 4 * 24 * 60;
// 4 days in seconds (clarification comments)
int secondsBeforeFriday = 4 * 24 * 60 * 60;
// etc..
Я бы, однако, предварительно компилировал определенные выражения в константе в тех случаях, когда задействованные значения не являются очевидными, а оптимизация и/или повторяемость - это проблемы (значения с плавающей запятой и их ошибки округления, например).
Извините меня за использование C в качестве примера этого последнего случая, но посмотрите здесь, чтобы понять, что я имею в виду.;)
Я использую именованные константы
Иногда я вставляю номер в качестве подсказки для чтения.
#define LPP57 57 // Lines per page
и др.
Важно понимать, что ваши константы включают единицы измерения. В противном случае
$x = time() + HOUR;
едва ли лучше. Откуда мы знаем, что $x, функция time() и константа HOUR - это все числа в секундах? Ну, мы не можем переименовать функцию time(), но мы можем хотя бы написать
$x_seconds = время() + SECONDS_PER_HOUR;
и теперь это ясно, без необходимости комментировать.
В этом отношении, как мы узнаем, что вы не выполняете конкатентацию строк, то есть добавляете суффикс "am" или "pm". Вы могли бы сказать: "ну, никто в здравом уме не назвал бы это ЧАСОМ", но я не уверен.
Некоторые люди выступают за венгерскую нотацию для этой цели. Мне не нравится форма венгерского, который украшает имена с их базовым типом (int, handle и т.д.), Но мне нравится украшение с семантическим типом (в секундах).
Это немного зависит от языка, но я бы создал тип. Таким образом, вы сможете прочитать намерение кода, а также проверить систему проверки этого количества во время компиляции и/или времени выполнения.
Константы скрыты внутри соответствующих названных методов и не повторяются (вы можете unit test тип/константы, если хотите).
Конечно, в некоторых случаях могут возникать некоторые проблемы оптимизации/производительности при таком подходе. Но по моему опыту они редко имели значение.
например. для иллюстрации что-то вроде:
class Time
{
private long time;
Time(long hours, long minutes, long seconds, long milliseconds) { ... }
Time(long milliseconds) { ... }
Time addMilliseconds(long increment)
{
return new Time(time + increment));
}
Time addSeconds(long increment)
{
return addMilliseconds(increment * 1000);
}
Time addMinutes(long increment)
{
addSeconds(increment * 60);
}
Time addHours(long increment)
{
addMinutes(increment * 60);
}
[...]
long getTimeInMilliseconds() { ... };
long getTimeInSeconds() { ... };
long getTimeInMinutes() { ... };
[.. maybe some static factory methods ...]
}
Что можно использовать следующим образом:
Time oneHourAhead = new Time(now()).addHours(1);
Time tenMinutesAgo = new Time(now()).addMinutes(-10);
Time oneHourTenMinutesAhead = new Time(now()).addHours(1).addMinutes(10);
Я бы сказал, что ЧАСЫ, МИНУТЫ И ДЕНЬ хороши. Похоже, что более гранулированный не обеспечивает большей удобочитаемости.
Работайте на самом высоком уровне абстракции.
Пример Ruby ActiveSupport
3.months.ago
1.year.from_now
Тот же принцип может быть применен и к другим менее гибким языкам, Java позволит вам сделать что-то вроде:
TimeHelper.months().ago(3);
Если у вас нет объектов на вашем языке, вы можете сделать это:
$x = strtotime('now + 1 hour');
$y = strtotime('now + 1 day');
$z = strtotime('now + 1 week');
Намного меньше места для неправильной интерпретации. Зачем возиться с вещами под проблемным доменом, если вам не нужно?