幻数对比。符号常量:何时替换?
魔法:未知语义
符号常量 -> 提供正确的语义和正确的上下文以供使用
语义:事物的意义或目的。
“创建一个常量,根据含义命名,并用它替换数字。” ——马丁·福勒
首先,幻数不仅仅是数字。任何基本值都可以是“魔法”。基本值是清单实体,例如整数、实数、双精度、浮点数、日期、字符串、布尔值、字符等。问题不在于数据类型,而是出现在我们的代码文本中的值的“神奇”方面。
我们所说的“魔法”是什么意思?准确地说:通过“魔术”,我们打算在我们的代码上下文中指向值的语义(意义或目的);它是未知的、不可知的、不清楚的或令人困惑的。这就是“魔法”的概念。当一个基本值的语义或存在目的在没有特殊辅助词(例如符号常量)的情况下从周围的上下文中快速、容易地知道、清楚和理解(不混淆)时,它就不是魔法。
因此,我们通过衡量代码阅读者从周围环境中了解、清晰和理解基本值的含义和目的的能力来识别幻数。读者越不为人所知、越不清晰、越困惑,基本价值就越“神奇”。
有用的定义
- 迷惑:使(某人)感到困惑或困惑。
- 困惑:使(某人)变得困惑和困惑。
- 困惑:完全困惑;很困惑。
- 困惑:完全迷惑或困惑。
- 不解:无法理解;困惑。
- 理解:感知(单词、语言或说话者)的预期含义。
- 含义:单词、文本、概念或动作的含义。
- 意思是:打算传达、指示或提及(特定的事物或概念);表示。
- signify:表示。
- indication:指示某事的标志或信息。
- 指示:指出;显示。
- 符号:其存在或发生表明其他事物可能存在或发生的对象、质量或事件。
基础知识
我们的魔法基本价值观有两种情况。只有第二个对程序员和代码来说是最重要的:
- 一个单独的基本值(例如数字),其含义是未知、不可知、不清楚或令人困惑的。
- 上下文中的基本值(例如数字),但其含义仍然未知、不可知、不清楚或令人困惑。
“魔术”的一个总体依赖项是唯一的基本值(例如数字)如何没有众所周知的语义(例如 Pi),但具有本地已知的语义(例如您的程序),这在上下文中并不完全清楚,或者可能在好的或坏的情况下被滥用。
大多数编程语言的语义不允许我们使用单独的基本值,除了(也许)作为数据(即数据表)。当我们遇到“幻数”时,我们通常会在上下文中这样做。因此,
的答案
“我是否将这个幻数替换为符号常数?”
是:
“你能多快评估和理解
其上下文中的数字(其存在的目的)?”
有点魔法,但不完全是
考虑到这一点,我们可以很快看到像 Pi (3.14159) 这样的数字在放置在适当的上下文中时如何不是“幻数”(例如 2 x 3.14159 x 半径或 2*Pi*r)。这里,数字 3.14159 是心理识别的 Pi,没有符号常量标识符。
由于数字的长度和复杂性,我们通常将 3.14159 替换为像 Pi 这样的符号常量标识符。 Pi 的长度和复杂性方面(加上对准确性的需求)通常意味着符号标识符或常数不易出错。将“Pi”识别为一个名称只是一个方便的奖励,但不是使用常量的主要原因。
同时:回到牧场
抛开像 Pi 这样的常见常量,让我们主要关注具有特殊含义的数字,但这些含义仅限于我们软件系统的范围。这样的数字可能是“2”(作为基本整数值)。
如果我单独使用数字 2,我的第一个问题可能是:“2”是什么意思? “2”本身的含义是未知的,没有上下文是不可知的,使得它的使用不清楚和混乱。尽管由于语言语义,我们的软件中不会只有“2”,但我们确实希望看到“2”本身没有特殊的语义或单独存在的明显目的。
让我们将唯一的“2”放在padding := 2 的上下文中,其中上下文是“GUI 容器”。在这种情况下,2 的含义(作为像素或其他图形单位)为我们提供了对其语义(含义和目的)的快速猜测。我们可能会在这里停下来,说 2 在这种情况下是可以的,我们不需要知道其他任何事情。然而,也许在我们的软件世界中,这还不是全部。它还有更多,但“padding = 2”作为上下文无法揭示它。
让我们进一步假设 2 作为我们程序中的像素填充是整个系统中的“default_padding”变体。因此,写指令padding = 2 是不够的。没有透露“默认”的概念。只有当我写:padding = default_padding 作为上下文然后在其他地方写:default_padding = 2 时,我才能在我们的系统中完全实现 2 的更好和更完整的含义(语义和目的)。
上面的例子非常好,因为“2”本身可以是任何东西。只有当我们将理解的范围和领域限制在“我的程序”中,其中 2 是“我的程序”的 GUI UX 部分中的default_padding 时,我们才能最终在适当的上下文中理解“2”。这里“2”是一个“神奇”数字,它在“我的程序”的 GUI UX 上下文中被分解为符号常量 default_padding,以便在更大的上下文中快速理解为 default_padding的封闭代码。
因此,任何其含义(语义和目的)不能被充分和快速理解的基本值都是一个很好的候选符号常量来代替基本值(例如幻数)。
走得更远
刻度上的数字也可能具有语义。例如,假设我们正在制作一款 D&D 游戏,其中我们有怪物的概念。我们的怪物对象有一个名为life_force 的特征,它是一个整数。这些数字的含义是不可知的或没有文字来提供含义的清晰。因此,我们开始武断地说:
- full_life_force: INTEGER = 10 -- 非常活跃(并且没有受伤)
- minimum_life_force: INTEGER = 1 -- 勉强活着(非常受伤)
- 已死:INTEGER = 0 -- 已死
- 不死生物:INTEGER = -1 -- 最少不死生物(几乎死了)
- 僵尸:INTEGER = -10 -- 最大不死生物(非常不死生物)
从上面的符号常数,我们开始对我们的 D&D 游戏中的怪物的活、死和“不死”(以及可能的后果或后果)有了一个心理图景。没有这些词(符号常量),我们只剩下从-10 .. 10 范围内的数字。如果游戏的不同部分依赖于数字范围对各种操作(如attack_elves 或seek_magic_healing_potion)的含义,那么没有单词的范围就会让我们处于一个可能非常混乱的地方,并且可能在我们的游戏中出现错误。
因此,在搜索和考虑替换“幻数”时,我们希望就软件上下文中的数字提出非常有目的性的问题,甚至这些数字在语义上如何相互作用。
结论
让我们回顾一下我们应该问的问题:
如果...,您可能会有一个神奇的数字
- 基本值在您的软件世界中是否有特殊含义或用途?
- 即使在适当的上下文中,特殊含义或目的是否可能是未知、不可知、不清楚或令人困惑的?
- 是否可以在错误的上下文中不当使用正确的基本值并带来不良后果?
- 可以在正确的上下文中正确使用不正确的基本值并带来不良后果吗?
- 基本价值与特定上下文中的其他基本价值是否存在语义或目的关系?
- 一个基本值是否可以存在于我们的代码中的多个位置,每个位置具有不同的语义,从而使我们的读者感到困惑?
检查代码文本中的独立清单常量基本值。慢慢地、深思熟虑地询问每一个关于这种价值的实例的问题。考虑你的答案的强度。很多时候,答案不是非黑即白,而是带有被误解的含义和目的、学习速度和理解速度的阴影。还需要看看它是如何连接到它周围的软件机器上的。
最后,替换的答案是回答(在您的脑海中)读者建立联系的优势或劣势的衡量标准(例如“得到它”)。他们理解意义和目的的速度越快,你的“魔法”就越少。
结论:只有当魔法足够大以至于难以检测到由混淆引起的错误时,才用符号常量替换基本值。