什么是神奇数字?

为什么要避免呢?

有没有合适的情况?


当前回答

我总是以不同的方式使用术语“魔数”,作为存储在数据结构中的模糊值,可以作为快速有效性检查进行验证。例如,gzip文件的前三个字节包含0x1f8b08, Java类文件以0xcafebabe开头,等等。

您经常看到在文件格式中嵌入了神奇的数字,因为文件可以相当混乱地四处发送,并丢失关于它们如何创建的任何元数据。然而,魔法数字有时也用于内存中的数据结构,如ioctl()调用。

在处理文件或数据结构之前快速检查这个神奇的数字,可以尽早发现错误,而不是为了宣布输入完全是胡编乱作而进行可能很长的处理。

其他回答

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

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

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

你看过维基百科上关于魔法数字的词条了吗?

它详细介绍了魔术数字引用的所有方式。下面是关于魔术数字是一种糟糕的编程实践的引用

“魔数”一词也指在源代码中直接使用数字而不作解释的糟糕编程实践。在大多数情况下,这会使程序更难阅读、理解和维护。尽管大多数指南对数字0和1做了例外处理,但在代码中将所有其他数字定义为命名常量是个好主意。

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

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

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

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

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

神奇的数字仍然很有用。

@eed3si9n:我甚至认为“1”是一个神奇的数字。: -)

与神奇数字相关的一个原则是,代码处理的每个事实都应该声明一次。如果您在代码中使用神奇的数字(例如@marcio给出的密码长度示例),那么您很容易复制该事实,当您对事实的理解发生变化时,您就会遇到维护问题。

幻数与符号常数:何时替换?

魔法:语义未知

Symbolic Constant ->为使用提供正确的语义和上下文

语义:事物的意义或目的。

“创建一个常数,用它的含义命名它,并用它替换数字。”——马丁·福勒

首先,神奇数字不仅仅是数字。任何基本的价值都可以是“魔法”。基本值是显式实体,如整数、实数、双精度数、浮点数、日期、字符串、布尔值、字符等。问题不是数据类型,而是在代码文本中出现的值的“魔力”方面。

我们所说的“魔法”是什么意思?准确地说:通过“魔术”,我们打算在代码上下文中指出值的语义(含义或目的);它是未知的,不可知的,不清楚的,或令人困惑的。这就是“魔法”的概念。当一个基本值的语义或存在目的——在没有特殊辅助词(例如符号常量)的情况下,从周围的上下文中就可以快速、容易地知道、清楚和理解(而不是混淆)时,它就不是神奇的。

因此,我们通过测量代码读者从周围上下文了解、清晰和理解基本值的意义和目的的能力来识别神奇数字。读者越不了解、越不清楚、越困惑,基本价值就越“神奇”。

基础知识

我们有两种神奇的基本价值观。对于程序员和代码来说,只有第二个才是最重要的:

一个单独的基本值(如数字),其含义是未知的、不可知的、不清楚的或令人困惑的。 上下文中的基本值(例如数字),但其含义仍然未知、不可知、不清楚或令人困惑。

“magic”的主要依赖关系是,一个基本值(例如number)没有常见的语义(如Pi),但有一个局部已知的语义(例如您的程序),从上下文来看不完全清楚,或者在好或坏的上下文中可能被滥用。

大多数编程语言的语义不允许我们单独使用基本值,除非(可能)作为数据(即数据表)。当我们遇到“神奇数字”时,我们通常会在某个上下文中这样做。因此,答案是

"我要用符号常数替换这个神奇的数字吗"

is:

“你能多快地评估和理解的语义 它存在的目的是什么?”

有点魔力,但不完全是

有了这个想法,我们可以很快看到像圆周率(3.14159)这样的数字在适当的环境下(例如2 x 3.14159 x半径或2 pir)不是一个“神奇的数字”。在这里,数字3.14159是没有符号常量标识符的圆周率。

不过,由于数字的长度和复杂性,我们通常将3.14159替换为像Pi这样的符号常量标识符。圆周率的长度和复杂性(加上对精度的需求)通常意味着符号标识符或常数不太容易出错。“π”作为一个名字只是一个方便的奖励,但不是有这个常数的主要原因。

同时:回到牧场

撇开像π这样的常见常数不谈,让我们主要关注具有特殊含义的数字,但这些含义受限于我们的软件系统。这样的数字可能是“2”(作为一个基本整数值)。

如果我单独使用数字2,我的第一个问题可能是:“2”是什么意思?“2”本身的意义在没有上下文的情况下是未知和不可知的,这使得它的使用不清楚和令人困惑。尽管由于语言语义的原因,在我们的软件中不会出现“2”,但我们确实希望看到“2”本身没有特殊的语义或明显的目的。

让我们把唯一的“2”放在:padding:= 2的上下文中,其中上下文是“GUI Container”。在这种情况下,2的含义(作为像素或其他图形单位)为我们提供了对其语义(含义和目的)的快速猜测。我们可以停在这里,说2在这种情况下是可以的,没有其他需要知道的了。然而,在我们的软件领域,这可能不是全部。还有更多,但“padding = 2”作为上下文不能揭示它。

让我们进一步假设在程序中2作为像素填充在整个系统中是“default_padding”类型。因此,写入指令padding = 2是不够的。“违约”的概念并没有被揭示。只有当我写:padding = default_padding作为上下文,然后在其他地方写:default_padding = 2时,我才完全意识到2在我们的系统中更好和更充分的意义(语义和目的)。

The example above is pretty good because "2" by itself could be anything. Only when we limit the range and domain of understanding to "my program" where 2 is the default_padding in the GUI UX parts of "my program", do we finally make sense of "2" in its proper context. Here "2" is a "magic" number, which is factored out to a symbolic constant default_padding within the context of the GUI UX of "my program" in order to make it use as default_padding quickly understood in the greater context of the enclosing code.

因此,任何基本值,其含义(语义和目的)不能被充分和快速地理解,都可以用符号常数来代替基本值(例如幻数)。

要进一步

刻度上的数字也可能有语义。例如,假设我们正在制作一款《龙与地下城》游戏,其中有一个怪物的概念。我们的怪物对象有一个叫做life_force的特性,它是一个整数。这些数字的含义如果没有文字来解释,是无法理解或清楚的。因此,我们一开始就武断地说:

full_life_force: INTEGER = 10—非常活跃(和未受伤) minimum_life_force: INTEGER = 1—勉强活着(非常受伤) 死亡:INTEGER = 0 -死亡 undead: INTEGER = -1—最小undead(几乎死亡) 僵尸:INTEGER = -10—最大不死(非常不死)

From the symbolic constants above, we start to get a mental picture of the aliveness, deadness, and "undeadness" (and possible ramifications or consequences) for our monsters in our D&D game. Without these words (symbolic constants), we are left with just the numbers ranging from -10 .. 10. Just the range without the words leaves us in a place of possibly great confusion and potentially with errors in our game if different parts of the game have dependencies on what that range of numbers means to various operations like attack_elves or seek_magic_healing_potion.

因此,当搜索和考虑替换“魔术数字”时,我们希望在我们的软件上下文中询问关于数字的非常有目的性的问题,甚至是数字如何在语义上相互作用。

结论

让我们回顾一下我们应该问什么问题:

你可能有一个神奇的数字,如果……

Can the basic value have a special meaning or purpose in your softwares universe? Can the special meaning or purpose likely be unknown, unknowable, unclear, or confusing, even in its proper context? Can a proper basic value be improperly used with bad consequences in the wrong context? Can an improper basic value be properly used with bad consequences in the right context? Does the basic value have a semantic or purpose relationships with other basic values in specific contexts? Can a basic value exist in more than one place in our code with different semantics in each, thereby causing our reader a confusion?

检查代码文本中独立的清单常量基本值。慢慢地、仔细地问每一个问题,每一个例子都有这样的价值。考虑一下你的回答的说服力。很多时候,答案不是非黑即白的,而是有误解的意义和目的、学习速度和理解速度的阴影。还需要了解它是如何与周围的软件机器连接的。

最后,替换的答案是回答(在你的脑海中)读者的优点或缺点来建立联系(例如。“得到它”)。他们越快理解意义和目的,你的“魔力”就越小。

结论:只有当魔法大到难以检测由混淆引起的错误时,才能用符号常数替换基本值。