什么是好代码

在我心目中,好代码必须要符合以下四条标准

  1. 正确
  2. 易懂
  3. 易改
  4. 高效

而烂代码,只有一个衡量标准,那就是你在阅读或修改代码时骂的脏话的程度与次数

1. 正确

这是最基本的要求,代码当然是满足需求,运行起来正确无误,这一点并不那么容易做到,尤其是运行环境比较复杂,各种异常情况较多的时候。

好代码要考虑周到,各种逻辑流程和意外情况的处理要面面俱到, 单元和模块测试要覆盖异常逻辑和边界。

对于服务质量 SLA 要考虑周全, 简单说起来就是满足用户的七大基本需求

  1. 功能性,2. 稳定性,3. 可靠性,4. 性能,5. 可维护性,6. 可移植性,7. 灵活性

一般正确性主要强调功能性实现正确无误,但是稳定可靠,快速灵活也是不可或缺的

我们的代码既要防错,也要容错,足够的健壮,不易出错,不怕出错,网络崩了,电源断了,磁盘满了等异常情况,都要有相应的应对措施。

2. 易懂

  • 好代码必须是看起来很舒服,很干净,容易理解

象一篇好文章,不罗嗦,容易懂,有头有尾。

各个层次,模块及函数分工明确,各司其职, 望文知义,代码无需注释即可自我描述。

接口即契约,要足够简单,易懂易用, 窄接口好过宽接口。

其实只要符合代码规范,命名简单易懂,代码就没那么丑。

参见 Google 的代码风格指南

有空翻翻"重构"那本书中的臭味介绍, 可以提高品味。

有一些基本软件开发的普适原则,能遵守尽量遵守。

KISS: Keep It Simple and Straight

保持简单和直接, 适当隐藏复杂性

或者

KISS: Keep It Simple and Stupid 保持简单, 象傻瓜一样, 不要让别人多加思考

软件接口或 API 的设计要让人一看就明白, 一看就知道作者的意图和想法, 如何使用它, 有什么结果和可能的异常及副作用. 看过很多代码, 踩过许多坑, 大多是作者或者我自己使用了出乎意料的实现, 不做好必要的抽象和封装, 复杂的判断和算法到处都是

DRY: Don't Repeat Yourself

别重复你自己, 如有重复代码, 请抽象或重用,切勿将重复的代码散落在多处,否则修改代码时就知道有多痛苦了

SRP: Single Responsibility Principle

单一职责原则: 就一类而言, 应该仅有一个引起它变化的原因,就一个函数来说,它应该只做好一件事,贪多嚼不烂。

也有一个别称 DOTADIW - Do One Thing and Do It Well 就做一件事并做好它

OCP: Open Close Principle

"software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

一个软件实体如类、模块和函数应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

我们可以在原有的类中添加字段或方法进行扩展,或者给函数添加新的参数(C++可赋予一个默认值, Java 干脆重载一个新函数),以及扩展出一个新的子类。

而对于已经暴露出去的API 以及接口, 类及公有函数,尽量不要修改它们,因为你需要说服你的客户(其他使用这块代码的模块)做相应的修改,而你往往并不清楚有多少客户在使用它。

LSP: Liskov Substitution Principle

LSP替换基类原则: 子类型应该可以替换掉它们的基类型,或者说父类型拥有子类型的全部共性,子类型拥有父类型的全部特征。

ISP: Interface Segregation Principle

ISP接口隔离原则: 不应该强迫客户依赖于它们不用的方法, 接口属于客户, 不属于它所在的类层次结构,接口是生产者与消费者之间的契约,应该精炼简约,生产者只通过接口提供服务,而消费者也只通过接口来调用服务。

DIP: Dependency Inversion Principle

DIP依赖倒置原则: 抽象不应该依赖于细节, 细节应该依赖于抽象。

我们要依赖于高层接口,而不是依赖于底层实现,接口是不会轻易改变的,而实现可以修改和替换 时下常用的插件机制,依赖注入方法都遵循这一原则

-- 上述 5 个原则常被统称为 SOLID 原则

3. 易改

好代码要易于测试和修改, 适当的抽象,封装和模块化有利于后期的修改和重构。

封装好复杂性,区分开经常变化与基本不变的代码, 适当抽取易变参数作为配置。 为未来的变化设计有限的灵活性,无需多改就能很容易的扩展新功能。

还是书里那句话,高内聚,低耦合。将依赖倒置,做好抽象,封装和模块化,代码就不难改。

例如最常用的 MVC 模式,为什么我们要分成模型,视图和控制器三块,原因之一就在于分开易变的与不易变的,分开不会在一起变化的部分,减小每次修改的范围。

如果某个方面的功能需要修改,最好是改个配置, 其次是扩展个类或函数, 或者传个不同参数,最差的就是改多个地方,要做多个判断, 还要做霰弹式的修改。

好代码要易于观测和调试,也就是说日志和度量数据要完备,可以分级,分模块进行日志和度量数据的调整与分析,自带度量 API, 调试 API 及控制台为佳

好代码还要与时俱进,自我蜕变,写代码时要将不变的与易变的分开,技术再怎么变,人性不会变,挣钱的业务变化也不会太大。一般来说,要封装好业务逻辑,核心业务不会大变,即使推到重写也要理解和参照老系统的业务流程。

人会变老,代码也会,新业务,新技术,新架构,新框架层出不穷,要大胆试验,小心引入,逐步演进,不必抱残守缺,也不要盲目冲动,把握住变与不变的平衡,经常变的地方应该尽量少,且尽量方便修改。

引述一下,Python 之禅,虽然说的是Python, 其实适用于多数编程语言

英文 中文
Beautiful is better than ugly. 美比丑好
vbnet 复制代码
Explicit is better than implicit.    | 明显比隐晦好
Simple is better than complex.       | 简单比复杂好
Complex is better than complicated.  | 复杂比难懂好
Flat is better than nested.			 | 扁平比嵌套好
Sparse is better than dense.         | 稀疏比稠密好
Readability counts.                  | 可读性很重要
Special cases aren't special enough to break the rules.  | 特例也不要打破这个原则
Although practicality beats purity.                      | 尽管实践会破坏纯洁性
Errors should never pass silently.                       | 错误还是不能让其悄然滑过
Unless explicitly silenced.                              | 除非你明确声明不用理会它
In the face of ambiguity, refuse the temptation to guess. | 别让人来猜测不确定的可能性
There should be one-- and preferably only one --obvious way to do it. | 应该有一个且只有一个比较好的明显的方法来做事
Although that way may not be obvious at first unless you're Dutch. | 尽管那个方法可能并非一开始就显而易见
Now is better than never. | 现在就做比永远不做好
Although never is often better than right now. | 尽管永远不做经常比马上就动手做好
If the implementation is hard to explain, it's a bad idea. | 如果实现很难解释清楚, 那它不是一个好主意
If the implementation is easy to explain, it may be a good idea. | 如果实现很容易说清楚, 那它是个好主意
Namespaces are one honking great idea -- let's do more of those! | 命名空间是个绝妙点子, 让我们那样做得更多

4. 高效

好的代码应该充分利用可用的资源,性能达标,没有无谓的浪费和等待。 使用合适的算法和数据结构,对 CPU, 内存,网络等资源的使用有节制。

在关键算法,关键路径上要做算法复杂度分析,应用大 O 分析法以及Amdahl加速定律等进行定性和定量分析,再结合性能度量数据进行调优。

我们不但要给业务数据制定 KPI, 也要给性能数据制定 KPI, 例如通常用的到 UPS - Usage, Performance 和 Saturation, 通过观测,度量和分析这些 KPI 再做有的放矢的调优。

压力测试(stress testing)和性能分析(performance profiling)是必不可少的方法。 一般来说,高效率要在设计,实现和测试多方面都要做有针对性的考虑,通过度量驱动,想清楚代码上线怎么生成和收集度量数据,如何通过度量进行调优。

参考资料

  • Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin
  • Clean code by Robert C. Martin
  • Refactoring: Improving the Design of Existing Code Martin Fowler

本作品采用 [知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)进行许可。

相关推荐
Cyrus丶2 天前
前端组件化开发指南(二)
前端·代码规范
FrozenMoments3 天前
编写可读代码的艺术
代码规范
陈陈陈建蕾6 天前
回顾前端 - 为多个小程序设计一个MonoRepo架构
前端·架构·代码规范
记忆深处的声音6 天前
vue2 + Element-ui 二次封装 Table 组件,打造通用业务表格
前端·vue.js·代码规范
y0ungsheep8 天前
CTF中的phar反序列化 [SWPU 2018]SimplePHP
运维·web安全·网络安全·php·代码规范
前端郭德纲13 天前
如何修改远程分支?修改了会影响什么?
git·github·代码规范
帅次14 天前
基于云平台的智能家居管理系统设计与通信协议分析
设计模式·重构·软件工程·软件构建·需求分析·代码规范·设计规范
是Yu欸21 天前
【深度学习代码调试5】标准化数据集:TensorFlow Datasets (TFDS)自动化数据加载与预处理
人工智能·python·深度学习·tensorflow·代码规范·1024程序员节
稍敛风翼22 天前
【知识】正反例分析面向对象的七大设计原则(超详细)
c++·代码规范·1024程序员节
PetterHillWater1 个月前
GitLab集成GPT进行自动化CodeReview实战
后端·gitlab·代码规范