源代码中毫无意义的代码


34

我从高级编码员那里听到了一些故事,而我自己也看到了一些。似乎有许多以上的程序员在编写无意义的代码。我会看到类似的东西:

  • 没有价值的方法或函数调用。
  • 在单独的类文件,对象或方法中进行的冗余检查。
  • if 始终为true的语句。
  • 分离的线程不执行任何操作。

仅举几个。有人告诉我,这是因为程序员想要故意使代码混乱,以至于给组织增加自己的价值,或者确保在进行承包或外包工作时要重复业务。

我的问题是。其他人看到过这样的代码吗?您的结论是为什么存在该代码?

如果有人写过这样的代码,可以分享原因吗?


4
if (false) {...}块非常适合注释代码!</
sarcasm

18
永远不要将愚蠢充分解释为恶意,尤其是在软件开发中,临时的快速黑客很少是临时的。
2011年

1
@ dlras2情节扭曲:#DEFINE false true :)
Silviu Burcea 2014年

Answers:


17

我听说开发人员试图使他们的编码成就听起来比实际情况复杂。我从未听说过有人承认这一点,但是我看到符合您标准的代码是出于匆忙或不良做法而不是蓄意破坏而故意创建的。恶意代码周围的代码可能已更改为特定功能不再有用的地步。

在得出只有该开发人员才能管理复杂性的结论之前,实际上必须有人亲自看一下此代码。大多数经理和其他业务人员之所以得出这个结论,是因为他们不懂任何代码,也不想填补这个职位。


2
在这种情况下,我倾向于为您提供正确的答案,因为我看到的某些代码并非是无意的……除非除非有人在编码时觉得很高,否则认为这很有趣!我确实相信其他人也有使用无用代码的相关原因,但是我看到的代码是在少数人从事的项目上进行的,而且我是原始开发团队之外从事该工作的第一个人。我不得不说,这似乎增加了震惊和敬畏的复杂性。
阿里

18
@Ali:永远不要将恶意归因于无能。换句话说,该代码可能演变成这种混乱状态,因为没有人敢于花时间去实际查看它并查看其实际作用。所有这些听起来像是一堆又一遍的快速修复,直到剩下的只是一堆讨厌。
quick_now 2011年

1
为@quickly_now +1。通常这就是最终发生的情况。每个人都害怕触摸任何“有效的”东西,以免破坏它(或者天堂禁止,要花费更长的时间来真正改善代码!这是恐怖的!)。因此,代码腐烂并溃烂,并最终崩溃了许多年。
韦恩·莫利纳

@Ali,在某些情况下,似乎描述了最语义和最合理的代码,因为我可能认为这很有趣或炫耀。反之亦然,我看到了其他人的代码。您永远不会知道谁在疯狂,而且在大多数情况下,它只是取决于经验和偏好。(这里不是在客观上谈论糟糕的代码,只是这样的描述很容易抛出)
Mihail Malostanidis

73

我没有看到这样的代码,但是由于其他原因,我看到了看起来毫无意义或毫无意义的代码:

  1. 向后兼容。您发现了做事的更好方法,但是您必须保留旧的(至今为止不是很有用)的API /功能,因为那里的某些第三方模块可能正在使用此API /功能。即使该函数没有做任何有用的事情,缺少该函数也可能会破坏某些代码。

  2. 防御性编码。您知道此代码中的检查毫无意义,因为已经在其他地方进行了检查。但是,如果有人在其他地方更改此代码并删除或更改检查以使它们不再符合您的先决条件怎么办?

  3. 有机增长。在大型项目中,这些年来许多事情发生了变化,结果证明以前使用过的某些方法不再使用,但是没有人愿意删除它们,因为没有人跟踪是否使用了这种特定方法,因此只是重构了他们的代码段,碰巧发生了,他们都停下来使用此方法。或曾经具有意义但在其他地方重构了应用程序的条件,因此条件始终变为真实,但没有人愿意删除它。

  4. 过度设计。人们可能会编写某些东西“以防万一我们需要它”,而实际上并不需要。就像“让我们生成线程以防万一我们必须离线进行一些工作”,然后没有人要求离线进行任何操作,程序员忘记了它并转到其他项目(甚至可能是另一家公司),并且代码仍然存在永远,因为没人知道它在那里的原因或删除它的安全性。

因此,尽管我从未见过它是出于恶意或误导性的工作安全方法来完成的,但我已经看到无数次它是软件开发的自然结果。


22
我认为#3,有机增长解释了我在工作中看到的大部分“无用代码”。但是所有这四个原因都假设一个聪明的程序员。一些无用的代码是由于某人不了解需要做什么,不应该做什么而产生的,而纯粹出于担心更改(某种)工作的担心而遗留了很多代码。
布鲁斯·埃迪格

2
我在项目中看到了#4:通常不是故意在公司内部拥有更多权力,而是有些人总是试图创建更通用的解决方案,即使不需要它。关于#2,出于您所解释的确切原因,我经常使用它:恕我直言,即使最小的函数或方法也不应对其余代码的工作方式或更改方式做出任何假设。相反,我的代码遵循简单的模式:“如果输入OK,则输出else错误”。这遵循最小化依赖关系的一般设计原则。
乔治

3
您还忘记了:糟糕的开发人员。有些人不应该编写代码,并且许多商店中没有良好的审查流程。

20

我的问题是。其他人看到过这样的代码吗?您的结论是为什么存在该代码?

1)是的。

2)在我所看到的情况下,我将其归类为:

  • 程序员经验不足
  • 程序员不了解他尝试修改的特别复杂和/或执行不佳的设计
  • 程序员在(例如)重构过程中被打断。
  • 疏忽

现在,也许我对此很慈善,但是我的一般做法是,对这些事情宽容/不打架比指责并坚持低劣的质量更好。显然,事情可能会变得很糟,必须执行某些操作,但是通常只需朝正确的方向轻推即可。

当然,如果质量/错误会严重影响“业务”,则您不能采取这种放任自流的方法。但是在这种情况下,您需要对所有内容进行强制性勤奋的代码审查,并结合全面的测试程序。


以我的经验,人们倾向于(至少部分)对质量差的代码“保持警惕”,因为它违反了他们的个人标准。(个人)追求完美是一件非常好的事情,但是将您的个人标准投射给其他人则有点不合理。根据事物的声音(例如,根据示例的性质),这就是您正在做的事情。

海事组织,这没有生产力,也不利于与您的同事建立良好的工作关系。


+1正在输入回复,发现您几乎列出了我要提及的所有原因。
canadiancreed

+1表示慈善。纠正别人的错误而不用指责,您的同事将尊重您的技术技能和人际交往能力。Harangue作者糟糕的代码,您的同事会反感您的方法并降低您的能力。
卡莱布(Caleb)

13

所有这些通常都是项目如何老化的症状。

 1.没有任何价值的方法或函数调用。很多时候,一些代码仍然保持原样(希望有一个大大的不推荐使用的警告,但是由于大多数语言都没有该警告,因此并不总是遵循该警告...),因为在某一时刻它可以为某些代码服务真正的目的,没人知道如果删除违规行将发生什么。

我似乎从Dailywtf记得这一点:

@deprecated // he might have been crazy enough to use reflection...
boolean getTrue() {
    return false; 
}

 2.在单独的类文件,对象或方法中进行的冗余检查。沟通的层次也不完美(曾经读过《神话人月》?否则,您在计算机上正在做什么!通常,一个人会从事某项工作,然后离开项目,然后下一个家伙发现一些奇怪的错误,在这里和那里抛出额外的检查以试图消除它。删除错误后,检查不是因为,如果它没有损坏,就不要修复它。

 3. if语句始终评估为true。哦,我已经做到了。我曾经有一个项目,它有一系列大约10-15个if / else块。要更改行为,我只需将a true||放在第一行。直到几个月(几年?)之后,我才回来说:“哦,哇,这条代码本来应该被销毁了,但从未被销毁。”

 4.分离的线程不做任何注意。我可以想象一条思路是这样的:

  1. 我知道!我可以异步处理这两个问题!我将创建线程foo和bar。
  2. (两个月后)嗯,您知道,bar的功能在foo中要好一些。我要搬过去。
  3. (一年后),将bar中的其他内容放入foo是有意义的。
  4. (很多年后,很多年)“嘿,这个bar线程看起来好像没有做任何事情,我们可以删除它吗?” “最好不要,它已经存在很多年了……”

5
+1代表“最好不要,它已经存在很多年了……”-反复发生。由于担心后果(“我们如何测试我们没有破坏某些东西”,尤其是周围没有单元测试的情况下)而害怕被删除。
quick_now 2011年


8

以前的老乡告诉我,有一次咨询师是根据他们所生产的代码行数来付款的。因此,他们使用令人费解的结构使利润最大化

如今,我一直以为那家伙在工作时仍在学习语言。而且他很着急。


谈论切断鼻子使脸变臭。我想如果您不必再查看代码就可以了。
JeffO

6

大多数答案可以归结为以下两个简单事实:
[1]代码反映了代码的历史,
[2]代码反映了代码的预期未来。

鉴于当前的规格,在当前的应用程序环境中,我编写的函数没有任何价值,但是将来可能需要。

我已经写过if陈述,目前它总是评估为true。但也许过去可能是错误的。

至于冗余检查,嘿,我不信任其他代码,甚至不信任自己的代码。如果一个模块依赖于N为1或2或3,则最好弄清楚这一点,如果不是,则进行有意义的崩溃。模块B爆炸是非法的,因为模块A拧紧了。模块B抱怨模块A搞砸了是完全合法的。请记住,下个月,该参数可能来自尚未编写的模块C。


1
我称之为不好的编码。您期望将来会需要它,但是这种情况很少发生。亚尼 编写一个if总是评估为true会浪费精力,并且使必须添加很可能不同功能的人感到困惑。下个月即将到来的参数可以等到下个月添加。现在,混乱的代码毫无意义。
安迪(Andy)

1
如果(language ='en'或language = th')-也许下个月我们尝试中文?if(!isset($ TITLE))-应该为所有模块设置$ TITLE,但也许某天某人会拼写错误。if(file_exists($ TARGET))-好的代码已经创建了该文件,但是可能是系统错误并且未创建。我的标准PHP / MySQL接口代码始终检查错误,即使我从未发现过该错误。
安迪·坎菲尔德

3

我已经看过几次了,实际上就是在昨天,我必须将一些老板代码合并到我的新应用程序中。就他而言,这归结于普遍缺乏技能和理解力,以及他认为自己相当熟练的开发人员的信念。

“没有价值的方法或函数调用。” 和“如果语句始终评估为真”是他的代码的主要问题。


3

我怀疑尽管许多人看到了存在这些问题的代码,但很少有人愿意编写相同的代码。很有可能,您看到的是累积的软件腐烂-有人添加了某些东西,但实际上是行不通的,下一位维护者在链中进一步添加了保护性代码,以防止第一次未正确检查的情况地点; 然后有人得到了问题报告,并针对特定问题实例添加了更多的防护措施;另一个人添加了更一般的检查,却忘记删除以前添加的一些旧代码,这些旧代码处理更具体的症状等。

然后是代码清理的问题:没有特别的动机来删除看似无效的代码,并且没有这样做的巨大动机,因为如果您不完全理解代码,则对代码“不起作用”的评估会有缺陷,您将最终破坏系统。


2
  • 没有价值的方法或函数调用。

不一定坏。基类中的方法通常调用空方法,这些方法被用作子类的覆盖点。示例:Cocoa Touch的UIView具有一种-didAddSubview:方法,在默认版本中该方法被记录为不执行任何操作。UIView的-addSubview:方法-didAddSubview:即使不执行任何操作也必须调用,因为子类可以实现它以执行某些操作。当然,不做任何事情的方法及其原因都应记录在案。

如果出于历史原因显然存在空的或无用的功能/方法,则应将其切除。如果不确定,请查看源代码存储库中代码的早期版本。

  • 在单独的类文件,对象或方法中进行的冗余检查。

很难说,没有上下文就可以了。如果出于相同的原因明确地进行了检查,则可能意味着没有明确的职责分离,需要进行一些重构,尤其是当两个检查都导致采取相同的操作时。如果两次检查的结果都不相同,那么即使条件相同,两次检查也可能出于不同的原因而进行,这可能还可以。

  • if语句始终评估为true。

之间有很大的区别:

if (1) {
    // ...
}

和:

if (foo() == true) {
    // ...
}

哪里foo()总是回来true

人们调试时,第一种情况经常发生。if (0) {...在尝试隔离错误时,可以使用和临时删除一部分代码,然后将更0改为1来恢复该代码。在if一旦你完成,当然,应该被删除,但它很容易忘记这一步,或错过一个或两个,如果你已经在几个地方做了。(最好是在注释中标识这些条件,以便以后搜索。)唯一的危害是将来可能引起的混乱。如果编译器可以在编译时确定条件的值,则会将其完全删除。

第二种情况是可以接受的。如果foo()需要从代码中的多个位置测试所代表的条件,则将其分解为单独的函数或方法通常是正确的选择,即使foo()现在总是正确。如果可以想到foo()最终可能会返回false,那么在方法或函数中隔离该条件是识别代码依赖于该条件的所有位置的一种方法。但是,这样做确实会带来一些风险,那就是该foo() == false条件将未经测试,并可能在以后导致问题。解决方案是确保添加显式测试false案例的单元测试。

  • 分离的线程不执行任何操作。

这听起来像是历史的人工产物,可以在代码审查期间或通过软件的定期分析来识别。我想它可能是有意创建的,但是我很难想象有人会故意这样做。


1

它发生了。实际上经常。

有时,这些编码的死胡同更像是古老的山羊路,当在它们周围安装了更高效/现代/快速的高速公路时,它们已经不堪重负。

在其他情况下(可能对此我有罪),当要求提供简要但未经确认的一组功能部件时,它们是软件扩展的基础。(有时在最初的版本中进行一些工作,为以后计划进行“螺栓固定”的工作提供句柄之类的工具,可以使生活变得更轻松(发生时),或者如果进一步的工作不做则更加困难/混乱。最后。)

而且,很多原因直接归因于旧的“如果没有破裂,就不适合它”。有时,撕下您不理解或认为未使用的代码可能会导致“蝴蝶效应”的编程版本。曾经发生过一两次。


1

有时我会将全局布尔值设置为true,然后在代码中将if(bool)设置为true,然后在运行时,我可能会在if语句中设置断点,并切换布尔值以进行测试。


0

我反对将if true语句随意分类为“无意义代码”。

if (1) { ... }在C代码中使用一个块的合理情况是,要么希望与坚持将变量定义放在函数的开始的C标准兼容,要么只是希望将局部变量定义为尽可能地局部。

switch (i) {
    case 23:
        if (1) {
            /* I can declare a local var here! */
        }
        break;
}

5
不需要'if(1)',为什么不只有块呢?
FigBug

3
C / C ++和C#都可以,而且我很确定Java(以及许多其他类似的语言)都允许匿名语句块;没有必要的ifwhile或类似的结构。它不太可能很干净,但是根据语言规范,它肯定是允许的。
的CVn

0

我的一位教授有一天向我们讲述了一个故事,即前任雇主会根据完成的行数向他们付款。因此,他们编写了几个从未调用过的多行内联函数。似乎是编写无意义代码的重要原因。


0

正如@Andy所提到的,我看到的一个很大的组件正在破坏YAGNI

有人从所有元素将是什么而不是许多元素可能需要的假设开始,这是对“是”“具有”关系的混淆。

这种混乱导致了继承的僵化结构,因此它们是未实现的方法,因为它们从未真正被调用过,需要调整某些部分的重复逻辑,以及通常不符合业务模型的奇怪工作流。

像这里的许多其他人一样,我还没有看到过这样做的恶意,但是由于缺乏良好设计的知识以及试图使它看起来对他人如此的尝试。有趣的是,似乎知识渊博的开发人员似乎在这方面做得更好,因为至少他们的代码不会以过度设计的方式结束。(KISS原则

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.