魔数与命名常数

时间:2009-03-04 00:51:15

标签: language-agnostic

编写代码时,尤其是在处理日期和时间时,您必须使用大量特定数字,例如:一分钟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常数处绘制线条。

22 个答案:

答案 0 :(得分:40)

86400不行,因为您可以轻易将其输入为84600,88400等

错误的常量将是编译错误

答案 1 :(得分:16)

我的一位教授曾告诉我们不要在我们的代码中添加任何魔法数字,除了1,-1和0.这有点极端,但它仍然在我的脑海里仍然指导着我,尽管我不坚持它完全。

在你的例子中,我更喜欢所有情况下的符号名称。

答案 2 :(得分:8)

我几乎到处都是常数(或者像Rails'15.minutes惯例一样可爱的衍生物)。对我而言,它是关于简化所有人的“打字”;如果我在一条线上的某个地方看到“10 * MINUTES”,我知道我正在处理时间(或者是某人正在进行屁股踢)。如果我看到10 * 60或600,我完全有可能不会理解我们处理时间非常容易。

答案 3 :(得分:6)

有一个相对好的参数,那么除零或一个以外的任何数字都应该是命名常量。 即使在您断言您对86400的可读性没有任何问题的示例中,您的度量单位仍然存在一些模糊性。

如果我维护你的代码,我宁愿看到如下的命名常量:

const int secondsInDay = 86400;

..那里没有太多的含糊之处。 :) 取决于是否有人(包括你自己......我的意思是,我很难记住我上周写的内容,更不用说去年了!)将需要在某个阶段维护你的代码。

答案 4 :(得分:4)

我的方法是不使用命名常量,但保持单位分开:

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

这样很明显,这是以毫秒为单位,如果我想稍后更改值,它们很容易调整。

答案 5 :(得分:4)

就我个人而言,我会使用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)时,你可以使用熟悉的技术来解决问题。

答案 6 :(得分:4)

我告诉我的学生:

  

如果你可以不用读代码   评论然后没有必要   常量。如果你必须有   解释它需要的代码   评价。

我也告诉他们:

  

思考很糟糕。如果你写的话   使人们不得不挖掘的代码   通过它来理解它   不是一件好事。

所以,如果你可以向同事展示这些行,并且他们可以在没有常数的情况下理解它,那么没有它们你可能会很好。可能你会想要常数。

答案 7 :(得分:3)

要在像这样的单位中使用,一个好方法是将所有命名为单位(甚至是基数),这样你的代码就像正确指定的测量一样:

// 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)。

答案 8 :(得分:2)

我教导时的经验法则(因此,核心规则比现实生活中更为严格)是-1,0,1或2以外的任何数字,不止一次被用作常数。如果您只使用一次,如果您愿意,可以对其进行评论......

答案 9 :(得分:2)

我保持简单;变量的名称和注释(如果需要非常神奇的数字)将足以通过代码审查。

int someDelay = 1232323; // in milliseconds.

答案 10 :(得分:2)

我倾向于几乎完全使用常数而非幻数。我认为它可以增加,并在程序中给你一点修复任何错误。有几个神奇的'60',例如:一分钟60秒,一小时60分钟。

答案 11 :(得分:1)

我会根据项目的大小绘制线条。项目越大,抽象和常数越多......就这么简单。

答案 12 :(得分:0)

就个人而言,我喜欢使用命名常量,以防我必须更改常量的值,然后我只需要在一个地方执行。

答案 13 :(得分:0)

一天总是有24小时,一小时总是60分钟。

我认为将它们命名为描述性常量的重点在哪里 a)数值不清楚(604800?) b)价值可能有一天会改变

答案 14 :(得分:0)

我肯定会使用常量,而你可以看一下86400和24小时,未来的程序员进行维护可能不会那么明亮,并且会对这个数字究竟是什么感到惊讶手段。如果你有一天回来并忘记了86400的含义,那也是你的情况。

答案 15 :(得分:0)

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

魔术弦怎么样?我倾向于做这样的事情:

  $x = strtotime('now + 1 hour');
  $y = strtotime('now + 1 day');
  $z = strtotime('now + 1 week');

是的,它可能运行速度稍慢。但是在更大的方案中,真的重要吗?

答案 16 :(得分:0)

我可能会在这里要求downvotes,但我相信你的例子都不是魔术数字。它们是明确定义的通用常量,您永远不会用其他东西替代它们。

我的方法是以前的一些答案的混合:

// 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作为最后一个案例,但看here来理解我的意思。 ;)

答案 17 :(得分:0)

我使用命名常量

  • 如果该号码将出现在多个地方
  • 如果号码可能会改变(新配置,新项目)

有时我会将数字嵌入,作为可读性提示。

#define LPP57   57     // Lines per page

答案 18 :(得分:0)

可读性对于您的常量包含度量单位非常重要。 否则

$ x =时间()+小时;

几乎没有任何好转。我们怎么知道$ x,time()函数和常量HOUR都是以秒为单位的数字?好吧,我们不能重命名time()函数,但我们至少可以编写

$ x_seconds = time()+ SECONDS_PER_HOUR;

现在很明显,不需要评论。

就此而言,我们怎么知道你没有进行字符串连接,即附加后缀“am”或“pm”。你可能会说“嗯,没有一个心智正常的人会称之为”,但我不太确定。

有些人为此目的提倡匈牙利表示法。我不喜欢用其底层类型(int,handle等)装饰名称的匈牙利语形式,但我喜欢用语义类型(秒)装饰。

答案 19 :(得分:0)

这取决于语言,但我会创建一个类型。这样你就可以阅读代码的意图,并让类型检查系统在编译时和/或运行时验证一些意图。

常量隐藏在相关命名的方法中,不会重复(如果需要,可以单元测试类型/常量)。

当然,在某些情况下,此方法可能存在一些优化/性能问题。但根据我的经验,他们很少重要。

e.g。例如:

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

答案 20 :(得分:-1)

以最高抽象级别工作。

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');

误解的空间要小得多。如果你不需要,为什么要解决问题域下的问题?

答案 21 :(得分:-1)

我会说HOUR,MINUTE和DAY很好。更细粒度似乎没有提供更多的可读性。