【CSDN 编者按】本文讨论了两种构建程序和编写代码的方法:自上而下和自下而上。作者强调了自上而下方法的重要性,认为自下而上方法容易导致不必要的复杂性和失败的项目。
原文链接:https://www.teamten.com/lawrence/programming/write-code-top-down.html
未经允许,禁止转载!
作者 | Lawrence Kesteloot 译者 | 弯月
责编 | 夏萌
出品 | CSDN(ID:CSDNnews)
构建程序以及编写代码的方式可分为两种:自上而下和自下而上。
自上而下设计指的是,从整个程序的愿景出发,考虑有哪些组件,以及如何将这些组件组织到一起,至于组件本身的功能暂时还比较模糊。你需要实现程序的高层版本,调用一些非常简单的组件(可能这些组件不执行任何操作),然后慢慢深入思考每个组件的细节。
另一方面,自下而上设计指的是从组件出发,考虑清楚这些组件的功能,至于它们如何组合在一起暂时不太清楚。你需要编写每个组件,对它们进行单元测试,然后将它们组装成一个完整的程序。
架构和编写程序的正确方法是自上而下。这不是品味或偏好的问题。自下而上的设计根本行不通,你不应该使用这种方法。我参与过的每一个采用自上而下设计的系统都成功了,而那些使用自下而上的系统统统失败了。Edsger Dijkstra 曾就这个主题撰写过一篇独白(https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD249.PDF)。我从未见过架构师提倡自下而上的方法。
采用自下而上的方式编写代码,你在编写组件时并不清楚如何将它们组合到一起。这会引发两个问题,一个小问题,一个大问题:
小问题是可能这些组件并不适合这项任务。可能是 API 不正确,也有可能是组件的工作方式不正确,或者你根本不需要这个组件。先编写组件,就有可能浪费时间。
大问题是最终你得到的程序会过于复杂,这也是自下而上编程的杀手。在开发组件时,由于不确定用法,你很容易编写出过于通用化的代码。而通用的解决方案往往比具体的具体解决方案更复杂。最终,将组件插入整个程序时,只有特定的用途有效,但额外的复杂性永远不会被消除,会永远保留下来,导致程序的代码量和复杂性不必要的增加。而复杂性是大型程序的杀手。
假设你正在设计一个大型网站:一家大公司的在线商铺。如果采用自上而下的编程方式,首先你需要(举个例子)导入 Tomcat,编写一个简单的、大部分为空的 servlet,并输出“Hello world!”。这样,你就能得到一个完整的网站。接着,你可以编译代码,尝试运行,并演示这个网站。你甚至可以将它部署到生产中。虽然这个网站还有各种问题,因为它只显示了“Hello world!”,并没有显示客户可以购买的产品。但你可以慢慢修复这问题,比如添加模板引擎和简单的主页模板。接下来,你可以添加数据库调用,获取一组产品,然后显示在模板中。然后再添加购物车。然后添加登录功能。每一步,你都拥有一个可交付的网站(尽管功能不完善),而且每一步都很简单,只需要实现必要的操作。你很清楚下一步要做什么,也很清楚下一步组件需要做什么。
另一方面,如果采用自下而上的编程方式,你需要考虑最终得到的组件。你需要通过某种方式来决定显示哪个页面、采用何种方式从后端服务获取数据,以及如何呈现模板。我们来看看模板。你将使用哪个模板引擎?还不确定,所以最好让它可以插入到抽象层。那么,模板需要将输出写到何处呢?你甚至还没有选择 Web 容器。所以,最好还是添加一个抽象层,然后还要为模板的输入再添加一个抽象层。你将如何获取有关请求的信息,例如页面类型、cookie、语言、设备类型等?最好还是创建一个可插入的可扩展系统来收集任意数据,将其存储在通用上下文对象中!将所有这些组件都组合起来,你就会发现有功能太多,太过于复杂,而且很难理解和扩展。几乎所有的通用性永远都不会被用到,但也不会被移除。
聪明的工程师很容易被自下而上的编程所吸引。设计组件是一项很容易处理的小任务,而且编写完成就结束了。就好像制作一件完美、漂亮、可重复使用的珠宝。所有工程师都喜欢编写组件。自上而下的方法没有这么好的特质,编写产品永远没有尽头。因此,对于某些人来说,自上而下的方式并没有太多吸引力。但你仍然应该坚持采用自上而下的方式。
大公司特别容易不自觉地采用自下而上的方法。采用自上而下的方法,需要对应用程序负责,完成很多构建工作,直至每个组件的功能都变得清晰起来,然后将它们外包给不同的团队。设想组件的功能,然后将它们外包出去,最后再集成起来,对于组织来说这种方式更容易。这就是为什么这么多大型项目都失败了,为什么这么多组件在使用之前就被丢弃了,以及为什么大公司似乎永远无法取得太大进展。
对于个人程序员来说,现代工具也鼓励自下而上的编程。例如,在 Eclipse 之类的 IDE 中,如果你调用的类型和方法已定义,那么编写代码就会更容易。但如果你先定义它们,那么就是采用了自下而上的编程。在 IDE 中采用自上而下的方式编写的代码会显示成红色,直到你实现这些方法。首先编写被调用的函数会让你感受到压力。
最后,还有单元测试。当采用自下而上的方式编写组件时,你没有应用程序可以尝试调用这些组件,因此你不得不编写单元测试,以确保它们能够正确地处理。这固然没有问题,但如果采用自上而下的编程方式,你只需运行应用程序即可检查组件是否正常工作。你不用承受太大编写测试的压力,而且事实上组件的编写方式甚至不太容易测试。很不幸,这是自上而下编程的一个副作用,但我本身就不看好单元测试,大多数的单元测试都没有提供任何价值。如果是能够提供价值的测试,则无论如何都要编写,尽管这时组件已经在应用程序中正常工作了。
无论从哪个层面来看,自下而上的编程都要面临一定的压力。所以,我们应该避免。相反,我们应该采用自上而下的方式,从 main() 或相应的部分开始写起,在编写代码的过程中想象所有组件都已编写完成。你可以先去掉这些组件,或者直接返回硬编码的结果,确保程序能够编译和运行。然后,逐步向下,尽可能让一切保持简单。如果代码对解决手头的问题没有任何帮助,则不需要编写。如此,你才有机会成功地编写大型的、能够正常工作的、长寿的程序。
推荐阅读:
▶按玩家安装量收费,知名游戏引擎 Unity 新收费政策惹争议!
▶QQ回应被罚100万:诚恳接受,坚决执行;ChatGPT可在七分钟内开发出软件;Godot引擎项目宣布成立开发基金|极客头条
▶iPhone 15系列来了:全系“上岛”,换上USB-C接口,最高售价13999元!