什么是幻数,为什么不好呢?

时间:2020-03-05 18:48:48  来源:igfitidea点击:

什么是魔数?

为什么要避免呢?

是否有合适的案例?

解决方案

回答

幻数是文件格式或者协议交换开始处的字符序列。该号码用作健全性检查。

例子:
打开任何GIF文件,我们将在开始时看到:GIF89. " GIF89"是魔术数字。

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

危险是随机二进制数据可能包含这些相同的字符。但这是不太可能的。

对于协议交换,我们可以使用它来快速识别正在传递给当前"消息"已损坏或者无效。

幻数仍然有用。

回答

我们是否看过Wikipedia条目上的魔术数字?

它详细介绍了魔术数字引用的所有创建方式。这是关于魔术数作为一种不好的编程习惯的报价

The term magic number also refers to the bad programming practice of using numbers directly in source code without explanation. In most cases this makes programs harder to read, understand, and maintain. Although most guides make an exception for the numbers zero and one, it is a good idea to define all other numbers in code as named constants.

回答

我认为这是对我对我们先前问题的回答的回应。在编程中,幻数是一个嵌入的数字常量,没有任何解释。如果它出现在两个不同的位置,则可能导致一个实例被更改而不是另一个实例被更改的情况。由于这两个原因,重要的是在使用它们的地方之外隔离和定义数字常量。

回答

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

例如,假设我们有一个页面显示"订单"概述页面中的最后50个订单。 50是此处的幻数,因为它不是通过标准或者约定设置的,它是我们出于规范中概述的原因而编造的数字。

现在,我们要做的是在不同位置放置50个SQL脚本(" SELECT TOP 50 * FROM orders"),网站(最近50个订单),订单登录名(" for(i = 0; i <50 ; i ++)`)以及其他许多地方。

现在,当有人决定将50更改为25时会发生什么?还是75?还是153?现在我们必须在所有位置替换50个,我们很可能会错过它。查找/替换可能不起作用,因为50可能用于其他用途,而盲目地将50替换为25可能会带来其他不良影响(例如," Session.Timeout = 50"调用也设置为25,并且用户报告超时太频繁)。

另外,代码可能很难理解,即"如果<50,则为bla",如果我们在复杂的函数中遇到该代码,其他不熟悉该代码的开发人员可能会问自己" WTF是50? ??"

这就是为什么最好在正好1个地方有这样的模棱两可和任意的数字" const int NumOrdersToDisplay = 50",因为这会使代码更具可读性("如果&lt;NumOrdersToDisplay",这也意味着我们只需要更改在1个定义明确的地方。

魔幻数字适合的地方是通过标准定义的所有内容,即" SmtpClient.DefaultPort = 25"或者" TCPPacketSize = any"(不确定是否标准化)。同样,仅在1个函数内定义的所有内容都可以接受,但这取决于上下文。

回答

在编程中,"幻数"是一个应赋予符号名称的值,但通常作为一个字面量(通常在多个位置)插入到代码中。

出于同样的原因,这很不好,因为SPOT(单点真值)很好:如果以后要更改此常数,则必须遍历代码以查找每个实例。这也是不好的,因为其他程序员可能不清楚这个数字代表什么,因此是"魔术"。

人们有时通过将这些常量移动到单独的文件中以作为配置,从而进一步消除了魔数。有时这是有帮助的,但也可能带来超出其价值的复杂性。

回答

幻数是代码中数字的直接用法。

例如,如果我们拥有(使用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之类的静态分析工具会检测代码中幻数的使用并提出重构建议。

回答

值得注意的是,有时我们确实希望在代码中使用不可配置的"硬编码"数字。有很多著名的算法,包括0x5F3759DF,用于优化的平方根反算法。

在极少数情况下,我发现需要使用这样的幻数,我在代码中将它们设置为const,并记录为什么使用它们,如何工作以及它们来自何处。

回答

@ eed3si9n:我什至建议'1'是一个魔术数字。 :-)

与幻数相关的一个原则是,代码处理的每个事实都应被声明一次。如果我们在代码中使用幻数(例如@marcio给出的密码长度示例),则很容易最终复制该事实,并且当我们了解该事实更改时,便遇到了维护问题。

回答

我一直用不同的术语"幻数"作为存储在数据结构中的模糊值,可以将其验证为快速有效性检查。例如,gzip文件的前三个字节包含0x1f8b08,Java类文件以0xcafebabe开头,依此类推。

我们经常会看到魔术数字嵌入在文件格式中,因为文件可以随意发送,并且丢失有关其创建方式的任何元数据。但是,魔术数字有时也用于内存中的数据结构,例如ioctl()调用。

在处理文件或者数据结构之前快速检查幻数,可以使人尽早发出错误信号,而不是一路潜入冗长的处理过程中,以便宣布输入是完整的秃头。

回答

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

这样做的不好之处在于,我们正在为特殊功能重新使用空格(记录ID的带符号整数的空格)。也许我们永远都不想创建ID为0或者ID为负的记录,但是即使不是这样,每个看代码或者数据库的人也可能会对此感到迷惑,并一开始就感到困惑。毋庸置疑,这些特殊值并没有得到充分记录。

可以说22、7,-12和620也算作魔术数字。 ;-)

回答

使用幻数未曾提及的问题...

如果我们有很多,那么我们有两个不同的目的(我们使用幻数)的几率相当合理,因为它们的值恰好是相同的。

然后,当然,我们需要更改值……仅出于一种目的。

回答

如何使用默认值在类顶部初始化变量?例如:

public class SomeClass {
    private int maxRows = 15000;
    ...
    // Inside another method
    for (int i = 0; i < maxRows; i++) {
        // Do something
    }

    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }

    public int getMaxRows() {
        return this.maxRows;
    }

在这种情况下,15000是一个幻数(根据CheckStyles)。对我来说,设置默认值是可以的。我不想做:

private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;

这会使阅读起来更困难吗?在安装CheckStyles之前,我从未考虑过这一点。