为什么许多程序员建议避免使用它们?
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");
}
}
}
TRUE
/FALSE
)。 - Brendan LongSmtpClient.DefaultPort = 25
可能比 SmtpClient.DefaultPort = DEFAULT_SMTP_PORT
更加清晰明了。 - user253751IANA
分配的数字。 - njsg你看过魔数的维基百科页面了吗?
该页面详细介绍了魔数引用的所有方式。以下是关于魔数作为不良编程实践的一段引用:
术语“魔数”也指在源代码中直接使用数字而不加说明的不良编程实践。在大多数情况下,这会使程序更难阅读、理解和维护。虽然大多数指南对零和一这两个数字有例外,但最好将代码中的所有其他数字定义为命名常量。
魔数:未知的语义
符号常量 -> 提供正确的语义和正确的使用上下文
语义:事物的含义或目的。
"创建一个常量,将其命名为其含义,并将数字替换为它。" -- Martin Fowler
首先,魔数不仅仅是数字。任何基本值都可以是“魔数”。基本值是明显的实体,例如整数、实数、双精度浮点数、浮点数、日期、字符串、布尔值、字符等等。问题不在于数据类型,而在于“魔力”值在代码文本中出现的方面。
我们所说的“魔数”是什么意思呢?准确地说:“魔数”是指我们的代码上下文中值的语义(含义或目的);它是未知的、无法知晓的、不清楚的或混淆的。这就是“魔数”的概念。当基本值的语义含义或存在目的从周围上下文中很快、很容易地得知、清楚并理解(不混淆),而不需要特殊的帮助词(例如符号常量)时,则不是“魔数”。
因此,我们通过测量代码读者从上下文中知道、清楚和理解基本值的含义和目的的能力来确定魔数。读者越不知道、不清楚和困惑,基本值就越“神奇”。
我们有两种情况下出现基本值的魔力。只有第二种情况对程序员和代码来说才是最重要的:
“魔力”的一个重要前提是,孤立的基本值(例如数字)没有常见的语义(如圆周率),但具有局部已知的语义(例如你的程序),这在上下文中并不完全清晰或可能被滥用于良好或恶劣的上下文中。
大多数编程语言的语义不允许我们使用孤立的基本值,除非(也许)作为数据(即数据表)。当我们遇到“魔数”时,通常是在上下文中。因此,对于
"我要用符号常量替换这个魔数吗?"
答案是:
"您能多快地评估和理解数字的语义含义(它在上下文中的目的)?"
考虑到这一点,我们可以很快看到像圆周率(3.14159)这样的数字在适当的上下文中并非是一个“魔数”(例如 2 x 3.14159 x 半径或 2πr)。在这里,数字3.14159心理上被认为是圆周率,而没有符号常量标识。
尽管如此,由于Pi的长度和复杂性,我们通常会用符号常量标识代替3.14159。由于Pi的长度和复杂性(以及需要精度),符号常量标识较少出错。把“π”作为名称进行识别只是一个方便的奖励,但不是具有该常量的主要原因。
暂且抛开像圆周率这样的常量,让我们主要关注那些具有特殊含义的数字,但其含义仅限于我们软件系统的宇宙之内。这样的数字可能是“2”(作为基本整数值)。
如果我仅使用数字2,则我的第一个问题可能是:“‘2’是什么意思?”仅仅使用数字“2”而没有上下文环境,其意义未知,无法得知其用途。尽管由于语言语义,我们的软件中不会仅使用“2”,但我们仍然希望看到,单独的数字“2”没有特殊的语义或明显的目的。
让我们将孤立的“2”放入这样的上下文中:padding := 2
,其中上下文是“GUI容器”。在这种情况下,“2”的含义(以像素或其他图形单位)可以让我们快速猜测其语义(含义和目的)。我们可以在此停止,并说“2”在这个上下文中很好,我们无需了解其他信息。然而,也许在我们的软件宇宙中,这还不是全部。它还有更多的含义,但是“padding = 2”作为上下文无法揭示这一点。
让我们进一步假设,程序中作为像素填充的“2”是整个系统中的“default_padding”类型。因此,仅写指令padding = 2
是不够的。我们并没有透露“default”的概念。只有当我在上下文中写出: padding = default_padding
,然后在其他地方写出: default_padding=2
时,我才能充分认识到“2”在我们的系统中的更好和更全面的含义(语义和目的)。
-10..10
的数字。仅有范围而没有单词会让我们处于可能极度混乱的位置,并且在不同的游戏部分具有依赖关系时可能会导致游戏错误,例如attack_elves
或seek_magic_healing_potion
等操作。检查代码文本中独立的显式常量基本值。针对每个此类值实例,缓慢、深思熟虑地询问每个问题。考虑您的回答的强度。许多时候,答案并不是非黑即白的,而是存在着对含义和目的的误解、学习速度和理解速度的程度差异。同时还需要看它如何与周围的软件机器相连接。
最终,替换的答案是回答读者连接(例如“理解”)的强度或弱点的度量(在您的头脑中)。他们理解含义和目的的速度越快,你就会有更少的“魔法”。
结论:只有当“魔法”足够大以至于会因混淆而导致难以检测的错误时,才使用符号常量来替换基本值。
魔数是文件格式或协议交换开头的一系列字符。这个数字用作检查其合理性。
例如:打开任何GIF文件,你会在开头看到GIF89。其中"GIF89"就是魔数。
其他程序可以读取文件的前几个字符并正确识别GIF。
危险在于,随机的二进制数据可能包含相同的字符。但这种情况非常不太可能发生。
至于协议交换,你可以使用它快速识别当前传递给你的'消息'是否已损坏或无效。
魔数仍然很有用。
(foo[i]+foo[i+1]+foo[i+2]+1)/3
这样的表达式可能比循环计算要快得多。如果将3
替换为其他数字(例如5
),而不重写代码为循环,那么看到ITEMS_TO_AVERAGE
被定义为3
的人可能会认为可以将其改为5
以便平均更多的项。相比之下,看到具有字面意义的数字3
的表达式的人会意识到3
表示正在加在一起的项的数量。 - supercatquadratic_root = (-b + sqrt(b*b - 4*a*c)) / (2*a)
怎么样?实际上,任何数学公式中,“魔法数字”除了在公式中没有其他意义。 - jodag使用魔术数字存在一个未被提及的问题...
如果您有很多魔术数字,那么有相当大的几率您会为两个不同的目的使用魔术数字,其值恰好相同。
然后,不出所料,您需要更改值...只针对一个目的。
我猜这是对我之前问题的答案的回应。在编程中,魔数是一个嵌入的数字常量,没有解释就出现了。如果它在两个不同的位置出现,可能会导致一个实例被更改而另一个实例没有被更改。因此,为了这两个原因,将数字常量与使用它们的地方隔离和定义是很重要的。
const myNum = 22; const number = myNum / 11;
现在我的11可能是人或啤酒瓶之类的东西,因此我会将其更改为常量,例如inhabitants。 - user6913790