为什么难读的代码可以成为一个好代码。 对“保持简单”通用编码建议的复杂批评
在经验丰富的软件开发人员可以证明他们认为是好的编码规则的地方,很容易找到文章和十诫。 通常,由于大多数其他作者的相似观点都加强了这些建议,并且由于它们显然来自诸如可读性,可维护性或简单性之类的合理目标,因此表达了强烈的信念。 谁能质疑这些目标? 由于它们是理想的目标,因此可能没有人,但是……它们是否可以客观地衡量?
经过多年尝试像机器一样思考,这肯定影响了我对复杂性的理解,我可以写一些建议的回应,这不是因为反对的信念,而是因为对这些建议如何实现目标的反思是必要。
我想从一个轶事开始
几年前,我从事一个研发项目,试图解决一个非常复杂的问题。 终于看到光了,我已经被封锁了五个多月。 一周后,我实现了相应的算法,并且有效。 经过最艰苦的战斗,我感到自己像是一名胜利的战士。
经过将近两年的时间,我注意到该算法未正确处理确定的因果关系,因此我不得不对其进行审查,没什么特别的。 我阅读了算法的文档,阅读了源代码,令我惊讶的是,我无法理解它! 怎么可能!,我想出了解决方案!,我实现了算法!,我注释了源代码!,我写了扩展文档!,然后为什么我无法理解我自己的程序在做什么?
我又读了几次,花了两个小时思考这个问题,直到我对解决该问题所采用的算法有所了解。 经历是如此令人恶心,我向自己保证这将不再发生。 在记录如此复杂的过程并评论其实现时,我必须更加小心和详尽。 然后,我准备重写扩展的文档,并且……令人惊奇地,我意识到我最初编写的文档正确地解释了问题及其解决方案,并且源代码中的注释是有用且足够的。
实际上发生的事情非常简单。 在花了很多时间处理其他不同的问题之后,我几乎完全忘记了这个问题的细节以及我如何解决它。 这个问题太过曲折,解决方案太复杂,以至于不能立即理解。 但是尽管有足够的初始文档,但我还是认为有必要在下一次需要维护代码时添加以下警告:
“ 警告:您必须在解决问题之前一个多小时对问题和建议的解决方案进行反思,以便重建理解该问题所必需的思维方式。 ”
“爱丽丝梦游仙境”与“广义相对论”
在“ Hello world”应用程序和最复杂的科学或AI工具之间,存在许多类型的软件项目,每种项目都提供不同程度的复杂性。 它们都具有一个非常明显的特征:它们都是用一种不同的可用编程语言编写的软件。 这种通用功能通常导致人们相信,任何具有相应编程语言经验的IT专业人员都可以维护任何软件。 尽管对于某些维护任务而言确实如此,但完整维护中的整个维护工作还需要其他内容。
为了使与作者有别的人成功地完成软件的完整,连续的维护工作,专业人员不仅应是开发软件所用技术的专家,而且该人还需要对软件的深刻理解。软件解决的根本问题反过来又揭示了要维护的代码的意义。 当这个问题很复杂并且对于个人来说是新问题时,沉浸在源代码中将需要付出一定的努力,耐心,并且可能还需要特定知识领域的额外经验。
您可以每分钟一页的速度阅读“爱丽丝梦游仙境”,但在尝试理解“广义相对论”时却无法做到这一点。 而且,您不能要求爱因斯坦保持简单,只是因为它并不简单。 加入新项目时,即使已经有详尽的文档记录,也可能需要几个月的时间才能熟悉现有的源代码。 而且,如果您期望找到“爱丽丝梦游仙境”,但又面对“广义相对论”,您将责备原始程序员,因为代码不可读,被混淆并且很难维护,他认为他没有遵循那些普遍性每个人都知道产生“好代码”的编程规则。
意大利面条的头脑
另一方面,也确实有一些灾难性的程序员,他们编写的代码不必要地曲折,文档记录不充分,并且在受到批评时可能会辩称他们被误解了,因为他们就像爱因斯坦一样。 如果您正在编写“ Hello world”应用程序,但没有人能够理解它,那么您应该查看编码方法。 但是,我不是在谈论这种情况,而是在说代码的曲折性源自底层问题的真实而不可避免的复杂性。 那么,是否存在一种通用的方法来“保持简单”?
Spaghetti代码是一个可怕的编程概念,其中太多事物混乱地相互连接,可能比我们通常认为的更自然。 奇怪的是,该软件是我们脑海中执行的过程的最好的人工表示之一。 实际上,我们的大脑存储系统是意大利面条式记忆(技术术语为hypergraph )。
不幸的是,这些心理过程中的大多数本质上比我们想要的要复杂,并且无法简化它们。
如果这是自然的方法,那么在处理多维对象时实现六个或更多嵌套循环就没有错。 如果您知道自己在做什么以及为什么方便,则可以修改函数的参数。 如果过程需要,编写具有500行代码的函数或类方法也不错。 将代码拆分为较小的功能可能没有什么帮助。 如果您不打算在多个部分或递归算法中使用该代码段,请不要将其封装在该函数内。 强制创建函数是创建使代码复杂化的新对象。 它破坏了代码的自然流程,并阻碍了重用已经实例化的变量的可能性。 并且要考虑到函数调用本身就是一个消耗计算资源的进程,因此请尽可能避免在循环内调用函数。
如果要提高可读性,最好用空行分隔与特定子流程相对应的代码片段,并为它们提供带有大写注释的标题。
结论
“有时,我们必须在有效解决复杂问题的复杂性中找到美”
有时,算法非常复杂,因为要解决的根本问题非常复杂,无法采取任何措施使代码易于理解。 一些方法和建议要求对函数,方法或类的大小进行限制,但是您应该质疑这如何才能真正使代码更简单。 使您的代码紧凑,优化,删除不必要的指令并进行详细记录。
复杂性之前的反应在很大程度上取决于心理学。 我们天生就习惯于以简单方式感知和谐。 但是有时候,我们必须在解决一个棘手问题的有效解决方案的复杂性和对广泛问题的详尽管理中找到美。
要应对恶魔般的vious回行为是一项肮脏的工作,但有时需要有人来做。 把事情简单化? 是的……当然可以。
先前发布在https://medium.com/lai4d/in-defense-of-deviousness-e8e877d8820d
From: https://hackernoon.com/in-defense-of-deviousness-when-keep-it-simple-didnt-work-so-well-9tds36qp