17

我目前正在处理的代码库中充斥着硬编码值。

我将所有硬编码值视为代码异味,并尝试在可能的情况下消除它们……但是有些情况我不确定。

以下是我能想到的两个示例,这让我想知道最佳实践是什么:

1. MyTextBox.Text = someCondition ? "Yes" : "No"
2. double myPercentage = myValue / 100;

在第一种情况下,最好的办法是创建一个允许我执行 MyHelper.Yes 和 MyHelper.No 或者在配置文件中执行类似操作的类(尽管它不太可能改变,谁知道是否有可能曾经是它的使用区分大小写的情况)。

在第二种情况下,除非数学定律发生变化,否则通过除以 100 找到百分比不太可能改变……但我仍然想知道是否有更好的方法。

谁能建议一种适当的方法来处理这种硬编码?任何人都可以想到硬编码可以接受的任何地方吗?

4

21 回答 21

20

任何人都可以想到硬编码可以接受的任何地方吗?

  • 小应用
  • 单人项目
  • 扔掉
  • 短期生活项目

简而言之,任何其他人不会维护的东西。

哎呀,我刚刚意识到过去作为维护者编码器对我的伤害有多大 :)

于 2008-12-31T00:30:13.560 回答
16

真正的问题不在于硬编码,而在于重复。如果您接受“实用程序员”中的优秀建议,那就不要重复自己(DRY)。

采用 DRY 的原则,任何时候都可以硬编码。但是,一旦您再次使用该特定值,请重构,以便该值仅硬编码一次。

于 2008-12-31T01:15:33.687 回答
16

当然,硬编码有时是可以接受的。遵循教条很少像使用大脑那样有用。

(举个例子,回到 goto 之战也许很有趣。你知道有多少程序员会发誓 goto 是邪恶的?那么为什么史蒂夫·麦康奈尔(Steve McConnell)要花十几页来有条不紊地讨论Code Complete 中的主题?)

当然,有很多来之不易的经验告诉我们,小的一次性应用程序经常会变异成生产代码,但这不是狂热的理由。敏捷专家告诉我们,我们应该做最简单的事情,在需要时进行重构。

这并不是说“最简单的东西”不应该是可读的代码。这可能是完全合理的,即使是在一个一次性的尖峰中写:

const MAX_CACHE_RECORDS = 50
foo = GetNewCache(MAX_CACHE_RECORDS)

这与以下事实无关,即在三个迭代时间内,有人可能会要求缓存记录的数量是可配置的,而您最终可能会重构常量。

请记住,如果您走极端,例如

const ONE_HUNDRED = 100
const ONE_HUNDRED_AND_ONE = 101

我们都会来 The Daily WTF 嘲笑你。:-)

思考!就这样。

于 2008-12-31T13:46:52.470 回答
7

这从来都不是好事,你只是证明了......

double myPercentage = myValue / 100;

这不是百分比。你想写的是:

double myPercentage = (myValue / 100) * 100;

或更正确地说:

double myPercentage = (myValue / myMaxValue) * 100;

但是这个硬编码的 100 让你的想法变得混乱了......所以请使用 Colen 建议的 getPercentage 方法:)

double getpercentage(double myValue, double maxValue)
{
   return (myValue / maxValue) * 100;
}

同样正如 ctacke 建议的那样,在第一种情况下,如果您需要本地化这些文字,您将陷入痛苦的世界。添加更多变量和/或函数永远不会太麻烦

于 2008-12-31T01:01:22.997 回答
6

如果您需要本地化,第一种情况会杀死您。将其移动到应用程序范围内的某个静态或常量至少会使本地化变得更容易一些。

于 2008-12-31T00:28:44.030 回答
6

案例 1:什么时候应该对东西进行硬编码:当你没有理由认为它会改变时。也就是说,你永远不应该在线硬编码东西。花时间制作静态变量或全局变量或您的语言为您提供的任何内容。在有问题的类中执行它们,如果您注意到代码的两个类或区域出于相同的原因共享相同的值(这意味着这不仅仅是巧合),请将它们指向同一个地方。

案例 2:对于案例 2,您是正确的:“百分比”定律不会改变(这里是合理的),因此您可以内联硬编码。

案例 3:第三种情况是您认为事情可能会发生变化,但您不想/没有时间去加载 ResourceBundles 或 XML 或其他任何东西。在这种情况下,你可以使用任何你可以使用的集中机制——讨厌的 Singleton 类是一个很好的机制——并继续使用它,直到你真正需要处理这个问题。

但是,第三种情况很棘手:如果不真正执行应用程序国际化是非常困难的......所以你会想要硬编码东西,只是希望当 i18n 人来敲门时,你的代码不是最糟糕的 -品尝代码:)

编辑:让我提一下,我刚刚完成了一个重构项目,在该项目中,之前的开发人员已将 MySql 连接字符串放置在代码 (PHP) 中的 100 多个位置。有时它们是大写的,有时它们是小写的,等等,所以它们很难搜索和替换(尽管 Netbeans 和 PDT 确实有很大帮助)。他/她这样做是有原因的(一个名为 POG 的项目基本上是在强迫这种愚蠢行为),但没有什么比在一百万个地方重复同样的事情更不像好的代码了。

于 2008-12-31T00:37:14.387 回答
4

第二个示例的更好方法是定义一个内联函数:

double getpercentage(double myValue)
{
   return(myValue / 100);
}

...

double myPercentage = getpercentage(myValue);

这样一来,你在做什么就更明显了。

于 2008-12-31T00:27:50.290 回答
4

硬编码文字应该出现在测试值的单元测试中,除非在单个测试类中重复使用太多值以至于局部常量很有用。

单元测试是对预期值的描述,没有任何抽象或重定向。想象一下你自己正在阅读测试——你想要将信息直接呈现在你面前。

我唯一一次使用常量作为测试值是当许多测试重复一个值(本身有点可疑)并且该值可能会发生变化时。

我确实使用常量来比较测试文件的名称等内容。

于 2009-01-13T02:49:22.943 回答
3

我不认为你的第二个真的是硬编码的例子。这就像有一个 Halve() 方法,它接受一个用于除以的值;没有意义。

除此之外,示例 1,如果您想更改应用程序的语言,您不需要更改类,所以它绝对应该在配置中。

应该避免硬编码,就像德古拉避开太阳一样。它最终会回来咬你的屁股。

于 2008-12-31T00:29:04.483 回答
3

“硬编码”是错误的担心。重点不在于特殊值是在代码中还是在配置文件中,重点是:

  • 如果价值可以改变,那需要做多少工作,找到它有多难?将它放在一个地方并在其他地方引用那个地方并没有太多工作,因此是一种安全的方法。
  • 维护程序员一定会理解为什么价值是这样的吗?如果有任何疑问,请使用解释含义的命名常量。

这两个目标都可以在不需要任何配置文件的情况下实现;事实上,如果可能的话,我会避免这些。“将东西放入配置文件意味着更容易更改”是一个神话,除非

  • 你真的想支持客户自己改变价值观
  • 任何可能放入配置文件的值都不会导致错误(缓冲区溢出,有人吗?)
  • 你的构建和部署过程很糟糕
于 2008-12-31T16:46:52.793 回答
2

条件的文本应该在资源文件中;这就是它的用途。

于 2008-12-31T01:08:40.737 回答
2

不。

今天是一个简单的一次性应用程序,明天将推动您的整个企业发展。始终使用最佳实践,否则您会后悔的。

于 2008-12-31T04:40:40.620 回答
2

不正常(硬编码文字是否可以接受)

另一种看待这个问题的方法是,使用良好的命名约定来代替硬编码文字的常量如何在程序中提供额外的文档。

即使该号码只使用一次,它仍然难以识别,甚至可能难以找到以备将来更改。

恕我直言,让程序更易于阅读应该是经验丰富的软件专业人员的第二天性。原始数字很少进行有意义的交流。

使用命名良好的常量所花费的额外时间将使代码可读性(易于记忆)并且对未来的重新挖掘(代码重用)有用。

于 2009-02-10T04:44:17.980 回答
1

我倾向于根据项目的范围和规模来看待它。

我是一个独立开发者的一些简单项目?当然,我对很多东西都进行了硬编码。我编写的工具只有我会使用?当然,如果它完成了工作。

但是,在处理更大的团队项目时呢?我同意,他们是可疑的,通常是懒惰的产物。标记它们以供审查,看看您是否可以发现可以将它们抽象出来的模式。

在您的示例中,文本框应该是可本地化的,那么为什么不是处理它的类呢?

于 2008-12-31T00:28:47.853 回答
1

请记住,您将忘记任何非显而易见的硬编码值的含义。

因此,请务必在每个之后添加简短评论以提醒您。

一个德尔福的例子:

长度:= 长度 * 0.3048;{ 0.3048 将英尺转换为米}

于 2008-12-31T00:32:46.113 回答
1

只要您不进行重构、单元测试、同行代码审查,就可以。而且,您不想要回头客。谁在乎?

于 2008-12-31T00:40:17.533 回答
1

代码总是在进化。当您最初编写东西时,硬编码是最简单的方法。稍后,当需要更改该值时,可以对其进行改进。在某些情况下,需求永远不会到来。

需求可以以多种形式出现:

  1. 该值在许多地方使用,需要由程序员更改。在这种情况下,显然需要一个常数。

  2. 用户需要能够更改该值。

我认为没有必要避免硬编码。当有明确的需求时,我确实看到了改变事物的必要性。

完全不同的问题是代码当然需要可读,这意味着可能需要对硬编码值进行注释。

于 2008-12-31T16:16:02.120 回答
0

对于第一个值,它真的取决于。如果您预计您的应用程序不会被广泛采用,那么国际化将永远不会成为问题,我认为这基本没问题。但是,如果您正在编写某种开源软件或具有更多受众的东西,请考虑有一天可能需要翻译它的事实。在这种情况下,您最好使用字符串资源。

于 2008-12-31T00:32:42.727 回答
0

我曾经有一位老板拒绝不硬编码某些东西,因为在他看来,这让他可以完全控制软件以及与软件相关的项目。问题是,当运行软件的硬件死机时,服务器被重命名......这意味着他必须找到他的代码。那花了一段时间。我只是找到了一个十六进制编辑器并绕过它而不是等待。

于 2008-12-31T02:16:42.113 回答
0

硬编码应该永远被禁止。尽管在您非常简单的示例中,我认为在任何类型的项目中使用它们都没有任何问题。

在我看来,硬编码是当您相信变量/值/定义等永远不会改变并根据该信念创建所有代码时。

这种硬编码的例子是每个人都应该避免的《24 小时内自学 C》一书。

于 2008-12-31T15:09:40.540 回答
0

我通常为字符串和数字添加一组辅助方法。

例如,当我有诸如 'yes' 和 'no' 之类的字符串时,我有一个名为 __ 的函数,所以我调用 __('yes'); 它在项目中通过仅返回第一个参数开始,但是当我需要做更复杂的事情(例如国际化)时,它已经存在并且参数可以用作键。

另一个例子是网上商店的增值税(英国税的形式),最近它从 17.5% 变为 15%。任何通过以下方式对增值税进行硬编码的人:

$vat = $price * 0.175;

然后必须遍历所有引用并将其更改为 0.15,而不是超级有用的方法是为 VAT 设置一个函数或变量。

在我看来,任何可以改变的东西都应该以可变的方式来写。如果我发现自己在同一天做同样的事情超过 5 次,那么它就变成了一个函数或一个配置变量。

于 2008-12-31T16:33:06.267 回答