《代码整洁之道》-函数

简介: 《代码整洁之道》-函数

网络异常,图片无法展示
|


本系列文中会有大量书中内容摘抄,都是个人认为很值得分享的内容。当然,也会有个人感悟,作为学习记录及简单分享。


短小 & 只做一件事


每个函数应该尽可能的短小,这样就保证了每个函数的逻辑不会很复杂,阅读和维护的成本也比较小。


短小的函数会促使每个函数只做一件事,同时只做一件事,也促使函数尽可能短小,所以我把这两点放在了一起。


试想一下,当你尝试阅读一个函数,结果发现它竟然有几百行那么长,这无异于一盆冷水泼下来。然后你硬着头皮尝试理解这段代码,发现里边的逻辑互相纠葛,68行的一个逻辑调用指向了145行,148行的逻辑调用又指向了47行...,最后你读完这段代码,用尽了一天的力气,更甚者这段代码比我描述的更糟糕,最后你放弃了去理解它。


而如果编写这段代码的小伙伴遵循短小及只做一件事的规则,那么这个难以理解的函数估计就会变成十个甚至更多清晰、简洁的函数。


每个函数一个抽象层级


什么是抽象层级?


我理解就是代码逻辑或者完成功能的颗粒度。


比如一个项目的开发过程大致是:


  1. 立项
  2. 需求产出
  3. 编程实现
  4. 发布


它们都是同一级别的流程,从上往下理解起来也比较简单,一下就弄清了整个过程,而如果这个过程中有了其他粒度的过程,理解起来可能就是另一种情况。


  1. 立项
  2. 需求产出
  3. 编程实现
  1. UI确定主色调
  2. 建数据库
  3. 前端技术选型
  4. 建数据库表
  5. UI确定设计风格
  6. 后端技术选型
  1. ......


可以看到,还没到开发阶段,逻辑已经有点乱了,如果这是一段代码逻辑呢?如果嵌套了更细粒度的流程呢?所以保证每个函数内的逻辑都是一个抽象层级,对于代码的理解和维护是很有必要的。


函数中混杂不同抽象层级,往往让人迷惑,读者可能无法判断某个表达式是基础概念还是细节。更糟糕的是,这就像破损的窗户,一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。


向下规则


所谓向下规则就是让代码拥有自顶向下的阅读顺序,让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能寻抽象层级向下阅读。读者可以自由选择先理解最高抽象层级的主逻辑,还是单独只看其中某一部分的完整逻辑,而且这种选择十分简单,且不用担心迷路。


函数参数


作者讲到最理想的参数数量是0,其次是1,再次是2,应尽量避免3。


参数不易对付,他们带有太多概念性。参数的含义往往不是那么易于理解,读者需要像理解上下文一样理解参数的含义。


从测试的角度看,参数甚至更叫人为难。想想看,要编写能确保参数的各种组合运行正常的测试用例,是多么困难的事。如果没有参数,就是小菜一碟。如果只有一个参数也不太困难。有两个参数,问题就麻烦多了。如果参数多于两个,测试覆盖所有可能值的组合简直令人生畏。


输出参数比输入参数还要难以理解。读函数时,我们惯于认为信息通过参数输入函数,通过返回值从函数中输出,我们不太期望信息通过参数输出。所以输出参数往往让人苦思之后才恍然大悟。


无副作用


副作用是一种谎言,函数承诺只做一件事,但还是会做其他被藏起来的事。


比如在登录的函数中包裹设置全局变量的逻辑或者页面跳转的逻辑。


再比如在查询用户是否登录的函数中,包裹判断用户没有登录,就清空用户信息的逻辑。


这对于函数的调用者来说是有风险的,因为他很可能不知道该函数的副作用。同时这也违反了函数短小和只做一件事的规则,如果非要这么做,记得把函数名称重命名为 loginAndSetUserInfo checkLoginAndClearUserInfo,但是尽量不要这么做。


别重复自己


如果一个方法或者一段逻辑在超过一处调用,那么他就需要被抽离出来,这样不但会减少代码体积,更重要的是增加了代码的可维护性。当这段逻辑需要修改时,我们只需要修改抽离出来的函数就可以,而不必修改更多的位置,也就避免了忘记修改某处的风险。


如何写出这样的函数


写代码和写别的东西很像。在写论文或文章时,你先想什么就写什么,然后再打磨它。初稿也许粗陋无序,你可以对其斟酌推敲,直至达到你心目中的样子。


我写函数时,一开始都冗长而复杂,有太多缩进和嵌套循环,过长的参数列表。名称是随意起的,也会有重复的代码,不过我会配上一套单元测试覆盖每行丑陋的代码。


然后我打磨这些代码,分解函数,修改名称,消除重复。我缩短和重新安置方法。有时我还拆解类,同时保持测试通过。


最后遵循本章列出的规则,我组装好这些函数。我并不从一开始就按照规则写函数,我想没人做得到。


这里作者讲了自己是如何编写代码的,其实整个过程就是重构的理念,先实现功能,并用单元测试保证功能的稳定性。然后对代码进行重构。一开始就按照本章的规则些函数,或者说直接写出简洁的代码是不现实的,好代码都是不断重构的产物。


总结


本章内容对于如何组织代码逻辑以及如何拆分函数进行了系统的介绍,提出了一系列编写函数应该遵循的规则,我认为十分有价值。


作者在最后的小结中讲到:大师级程序员,把系统当作故事来讲,而不是当作程序来写。他们使用选定编程语言提供的工具构建一种更为丰富且更具表达力的语言,用来讲那个故事。遵循本章中关于函数的规则,函数就会被很好地归置,然后将编写的函数干净利落地拼装到一起,形成一种精确而清晰的语言,帮助你讲故事。


我距离讲故事还有很长的一段路要走,所以从现在开始,编写好每一个函数吧!💪

相关文章
|
人工智能 Cloud Native 大数据
现代后端技术发展趋势与应用前景
随着信息技术的快速发展,现代后端技术在不断演进和创新。本文将探讨现代后端技术的发展趋势和应用前景,并深入分析其中的关键技术和未来发展方向。从云原生、大数据、微服务架构到人工智能等多个方面展开讨论,展示了后端技术在不断推动数字化转型和业务创新中的重要作用。
|
人工智能 自然语言处理 机器人
创新场景丨大模型时代,重塑智能终端新体验
大模型为智能终端带来的变革是全方位的,但挑战也同样显而易见。云侧部署的大模型加端侧应用的大模型是综合平衡性能、成本、功耗、隐私、速度之下的最佳选择。
|
Unix 网络虚拟化 C++
VS2022+Qt5.14.2成功编译MITK2022.10
使用VS2022和Qt5.14.2成功编译MITK2022.10的过程,包括编译结果的截图、遇到的编译问题的解决方法、两个重要的注意事项(patch文件格式的修改和ITK-gitclone-lastrun文件的存在),以及参考链接。文中详细描述了如何解决编译过程中遇到的错误C2220和警告C4819,以及如何修改文件编码和尾行格式。
921 1
VS2022+Qt5.14.2成功编译MITK2022.10
|
10月前
|
存储 监控 Java
招行面试: 分布式调度 设计,要考虑 哪些问题?
45岁资深架构师尼恩在读者交流群中分享了关于设计分布式调度框架时需考虑的关键问题。近期有小伙伴在面试招商银行时遇到了相关难题,因准备不足而失利。为此,尼恩系统化地梳理了以下几点核心内容,帮助大家在面试中脱颖而出,实现“offer直提”。
|
10月前
|
数据采集 人工智能 自然语言处理
魔搭社区每周速递(1.5-1.18)
🙋魔搭ModelScope本期社区进展:新增3239个模型,711个数据集,192个创新应用, 16篇内容
555 11
多功能在线二维码生成源码
上传即可使用,可以把电子名片、文本、wifi网络、电子邮件、短信、电话号码、网址等信息生成对应的二维码图片。
380 24
多功能在线二维码生成源码
|
前端开发 JavaScript
node反向代理,解决跨域(http-proxy-middleware)
使用node.js和http-proxy-middleware库实现反向代理,解决跨域问题,允许前端请求通过代理访问不同端口的服务。
592 3
|
SQL 关系型数据库 数据库
在 Postgres 中使用 Exists
【8月更文挑战第11天】
369 0
|
Kubernetes 调度 异构计算
Kubernetes 调用 GPU解析
Kubernetes (K8s) 支持调用GPU以利用其统一调度和分配集群资源的能力,管理异构计算,如加速部署、提高资源使用率和保证资源独享。通过容器化和设备隔离,K8s确保GPU高效、安全地被应用使用。要调用GPU,需安装NVIDIA GPU驱动、CUDA工具包和Device Plugin,然后在Pod配置中指定GPU需求。安装步骤包括:确保GPU节点、安装GPU驱动和NVIDIA容器运行时、创建GPU资源要求的Pod并部署到集群。
|
存储 数据采集 监控
Flume 拦截器概念及自定义拦截器的运用
Apache Flume 的拦截器是事件处理组件,位于Source和Channel之间,用于在写入Channel前对数据进行转换、提取或删除。它们支持数据处理和转换、数据增强、数据过滤以及监控和日志功能。要创建自定义拦截器,需实现Interceptor接口,包含initialize、intercept、intercept(List<Event>)和close方法。配置拦截器时,通过Builder模式实现Interceptor.Builder接口。在Flume配置文件中指定拦截器全类名,如`TestInterceptor$Builder`,然后启动Flume进行测试。
625 0

热门文章

最新文章