什么是神奇数字?

为什么要避免呢?

有没有合适的情况?


当前回答

魔术数字也可以是具有特殊的硬编码语义的数字。例如,我曾经看到一个系统,其中记录id > 0被正常对待,0本身是“新记录”,-1是“这是根”,-99是“这是在根中创建的”。0和-99将导致WebService提供一个新的ID。

这样做的不好之处在于,您重用了用于特殊功能的空间(记录id的有符号整数的空间)。也许您永远不希望创建ID为0或ID为负的记录,但即使不是这样,每个查看代码或数据库的人都可能在一开始无意中发现这一点并感到困惑。不用说,这些特殊的值并没有得到很好的记录。

可以说,22、7、-12和620也可以算作神奇数字。: -)

其他回答

魔术数字是在代码中直接使用数字。

例如,如果你有(在Java中):

public class Foo {
    public void setPassword(String password) {
         // don't do this
         if (password.length() > 7) {
              throw new InvalidArgumentException("password");
         }
    }
}

这应该被重构为:

public class Foo {
    public static final int MAX_PASSWORD_SIZE = 7;

    public void setPassword(String password) {
         if (password.length() > MAX_PASSWORD_SIZE) {
              throw new InvalidArgumentException("password");
         }
    }
}

它提高了代码的可读性,也更容易维护。想象一下我在GUI中设置密码字段大小的情况。如果我使用一个神奇的数字,每当最大大小发生变化时,我必须在两个代码位置进行更改。如果我忘记了一个,就会导致不一致。

JDK中有很多例子,比如Integer, Character和Math类。

PS:像FindBugs和PMD这样的静态分析工具可以检测到代码中使用的神奇数字,并建议进行重构。

那么返回变量呢?

我发现在实现存储过程时尤其具有挑战性。

想象下一个存储过程(我知道语法错误,只是举个例子):

int procGetIdCompanyByName(string companyName);

如果该公司存在于特定的表中,则返回该公司的Id。否则,返回-1。 不知何故,这是一个神奇的数字。到目前为止,我读到的一些建议说,我真的必须设计这样的东西:

int procGetIdCompanyByName(string companyName, bool existsCompany);

顺便问一下,如果公司不存在,它应该返回什么?它会将existesCompany设为false,但也会返回-1。

Antoher的选择是做两个独立的功能

bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);

因此第二个存储过程的先决条件是公司存在。

但是我害怕并发,因为在这个系统中,一个公司可以由另一个用户创建。

顺便说一句,最重要的是:你对使用这种相对已知和安全的“神奇数字”来判断某事不成功或某事不存在有什么看法?

幻数是文件格式或协议交换开头的字符序列。这个数字可以作为一个完整性检查。

例子: 打开任何GIF文件,你会在最开始看到:GIF89。GIF89是一个神奇的数字。

其他程序可以读取文件的前几个字符,并正确识别gif。

危险在于随机二进制数据可能包含这些相同的字符。但这种可能性非常小。

至于协议交换,您可以使用它来快速识别正在传递给您的当前“消息”是否已损坏或无效。

神奇的数字仍然很有用。

关于使用神奇数字,还有一个问题没有被提及……

如果你有很多这样的数字,很有可能你有两个不同的目的,你使用魔法数字,其中的值碰巧是相同的。

然后,果然,你需要改变值…只有一个目的。

Magic Number是一个硬编码的值,它可能在以后的阶段更改,但因此很难更新。

例如,假设您有一个页面,在“您的订单”概览页面中显示最近50个订单。50在这里是一个神奇的数字,因为它不是通过标准或惯例设置的,它是您根据规范中概述的原因虚构的数字。

现在,你要做的是你在不同的地方有50个-你的SQL脚本(SELECT TOP 50 * FROM orders),你的网站(你的最后50个订单),你的订单登录(for (i = 0;I < 50;i++))和可能的许多其他地方。

那么,如果有人决定把50岁改成25岁,会发生什么呢?还是75年?还是153年?现在你必须在所有的地方替换50,你很可能会错过它。Find/Replace可能不起作用,因为50可能用于其他事情,盲目地将50替换为25可能会产生一些其他不良副作用(即你的Session)。Timeout = 50呼叫,也设置为25,用户开始报告太频繁的超时)。

此外,代码可能很难理解,即。"if a < 50那么bla"——如果你在一个复杂的函数中遇到这种情况,其他不熟悉代码的开发人员可能会问自己"WTF是50?? "

这就是为什么最好在1个地方有这样模糊和任意的数字-“const int NumOrdersToDisplay = 50”,因为这使代码更具可读性(“如果< NumOrdersToDisplay”,这也意味着你只需要在1个定义良好的地方更改它。

适用Magic Numbers的地方是通过标准定义的所有内容,即SmtpClient。DefaultPort = 25或TCPPacketSize =任何(不确定是否标准化)。此外,只在一个函数中定义的所有内容都可能是可接受的,但这取决于上下文。