HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

阿里工程师告诉你:什么是好的代码?

  • 2019-08-27
  • 本文字数:3768 字

    阅读完需:约 12 分钟

阿里工程师告诉你:什么是好的代码?

一句话概括

衡量代码质量的唯一有效标准:WTF/min —— Robert C. Martin



Bob 大叔对于好代码的理解非常有趣,对我也有很大的启发。我们编写的代码,除了用于机器执行产生我们预期的效果以外,更多的时候是给人读的,这个读代码的可能是后来的维护人员,更多时候是一段时间后的作者本人。


我敢打赌每个人都遇到过这样的情况:过几周或者几个月之后,再看到自己写的代码,感觉一团糟,不禁怀疑人生。


我们自己写的代码,一段时间后自己看尚且如此,更别提拿给别人看了。


任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。—— Martin Fowler


所以,谈到好代码,首先跳入自己脑子里的一个词就是:整洁


好的代码一定是整洁的,给阅读的人一种如沐春风,赏心悦目的感觉。


整洁的代码如同优美的散文。—— Grady Booch

好代码的特性

很难给好的代码下一个定义,相信很多人跟我一样不会认为整洁的代码就一定是好代码,但好代码一定是整洁的,整洁是好代码的必要条件。整洁的代码一定是高内聚低耦合的,也一定是可读性强、易维护的。

高内聚低耦合

高内聚低耦合几乎是每个程序员员都会挂在嘴边的,但这个词太过于宽泛,太过于正确,所以聪明的编程人员们提出了若干面向对象设计原则来衡量代码的优劣:


  • 开闭原则 OCP (The Open-Close Principle)

  • 单一职责原则 SRP (Single Responsibility Principle)

  • 依赖倒置原则 DIP (Dependence Inversion Principle)

  • 最少知识原则 LKP (Least Knowledge Principle)) / 迪米特法则 (Law Of Demeter)

  • 里氏替换原则 LSP (Liskov Substitution Principle)

  • 接口隔离原则 ISP (Interface Segregation Principle)

  • 组合/聚合复用原则 CARP (Composite/Aggregate Reuse Principle)


这些原则想必大家都很熟悉了,是我们编写代码时的指导方针,按照这些原则开发的代码具有高内聚低耦合的特性。换句话说,我们可以用这些原则来衡量代码的优劣。


但这些原则并不是死板的教条,我们也经常会因为其他的权衡(例如可读性、复杂度等)违背或者放弃一些原则。比如子类拥有特性的方法时,我们很可能打破里氏替换原则。再比如,单一职责原则跟接口隔离原则有时候是冲突的,我们通常会舍弃接口隔离原则,保持单一职责。只要打破原则的理由足够充分,也并不见得是坏的代码。

可读性

代码只要具有了高内聚和低耦合就足够好了吗?并不见得,我认为代码还必须是易读的。好的代码无论是风格、结构还是设计上都应该是可读性很强的。可以从以下几个方面考虑整洁代码,提高可读性。

命名

大到项目名、包名、类名,小到方法名、变量名、参数名,甚至是一个临时变量的名称,其命名都是很严肃的事,好的名字需要斟酌。


名副其实


好的名称一定是名副其实的,不需要注释解释即可明白其含义的。


  /**  * 创建后的天数  **/  int d;
复制代码


  int daysSinceCreation;
复制代码


后者比前者的命名要好很多,阅读者一下子就明白了变量的意思。


容易区分


我们很容易就会写下非常相近的方法名,仅从名称无法区分两者到底有啥区别(eg. getAccount()getAccountInfo()),这样在调用时也很难抉择要用哪个,需要去看实现的代码才能确定。


可读的


名称一定是可读的,易读的,最好不要用自创的缩写,或者中英文混写。


足够短


名称当然不是越长越好,应该在足够表达其含义的情况下越短越好。

格式

良好的代码格式也是提高可读性非常重要的一环,分为垂直格式和水平格式。


垂直格式


通常一行只写一个表达式或者子句。一组代码代表一个完整的思路,不同组的代码中间用空行间隔。


public class Demo {    @Resource    private List<Handler> handlerList;    private Map<TypeEnum, Handler> handlerMap = new ConcurrentHashMap<>();
@PostConstruct private void init() { if (!CollectionUtils.isEmpty(handlerList)) { for (Handler handler : handlerList) { handlerMap.put(handler.getType(), handler); } } }
publicResult<Map<String, Object>> query(Long id, TypeEnum typeEnum) { Handler handler = handlerMap.get(typeEnum); if (null == handler) { return Result.returnFailed(ErrorCode.CAN_NOT_HANDLE); } return handler.query(id); }}
复制代码


如果去掉了空行,可读性大大降低。


  public class Demo {      @Resource      private List<Handler> handlerList;      private Map<TypeEnum, Handler> handlerMap = new ConcurrentHashMap<>();      @PostConstruct      private void init() {          if (!CollectionUtils.isEmpty(handlerList)) {              for (Handler handler : handlerList) {                  handlerMap.put(handler.getType(), handler); } } }      public Result<Map<String, Object>> query(Long id, TypeEnum typeEnum) {          Handler handler = handlerMap.get(typeEnum);          if (null == handler) {              return Result.returnFailed(ErrorCode.CAN_NOT_HANDLE);          }          return handler.query(id); }  }
复制代码


类静态变量、实体变量应定义在类的顶部。类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter 方法。


水平格式


要有适当的缩进和空格。


团队统一


通常,同一个团队的风格尽量保持一致。集团对于 Java 开发进行了非常详细的规范。(可点击下方阅读原文,了解更多内容)

类与函数

类和函数应短小,更短小


类和函数都不应该过长(集团要求函数长度最多不能超过 80 行),过长的函数可读性一定差,往往也包含了大量重复的代码。


函数只做一件事(同一层次的事)


同一个函数的每条执行语句应该是统一层次的抽象。例如,我们经常会写一个函数需要给某个 DTO 赋值,然后再调用接口,接着返回结果。那么这个函数应该包含三步:DTO 赋值,调用接口,处理结果。如果函数中还包含了 DTO 赋值的具体操作,那么说明此函数的执行语句并不是在同一层次的抽象。


参数越少越好


参数越多的函数,调用时越麻烦。尽量保持参数数量足够少,最好是没有。

注释

别给糟糕的代码加注释,重构他


注释不能美化糟糕的代码。当企图使用注释前,先考虑是否可以通过调整结构,命名等操作,消除写注释的必要,往往这样做之后注释就多余了。


好的注释提供信息、表达意图、阐释、警告


我们经常遇到这样的情况:注释写的代码执行逻辑与实际代码的逻辑并不符合。大多数时候都是因为代码变化了,而注释并没有跟进变化。所以,注释最好提供一些代码没有的额外信息,展示自己的设计意图,而不是写具体如何实现。


删除掉注释的代码


git 等版本控制已经帮我们记录了代码的变更历史,没必要继续留着过时的代码,注释的代码也会对阅读等造成干扰。

错误处理

错误处理很重要,但他不能搞乱代码逻辑


错误处理应该集中在同一层处理,并且错误处理的函数最好不包含其他的业务逻辑代码,只需要处理错误信息即可。


抛出异常时提供足够多的环境和说明,方便排查问题


异常抛出时最好将执行的类名,关键数据,环境信息等均抛出,此时自定义的异常类就派上用场了,通过统一的一层处理异常,可以方便快速地定位到问题。


特例模型可消除异常控制或者 null 判断


大多数的异常都是来源于 NPE,有时候这个可以通过 Null Object 来消除掉。


尽量不要返回 null ,不要传 null 参数


不返回 null 和不传 null 也是为了尽量降低 NPE 的可能性。

如何判断不是好的代码

讨论了好代码的必要条件,我们再来看看好代码的否定条件:什么不是好的代码。Kent Beck 使用味道来形容重构的时机,我认为当代码有坏味道的时候,也代表了其并不是好的代码。

代码的坏味道

重复


重复可能是软件中一切邪恶的根源。—— Robert C.Martin


Martin Fowler 也认为坏味道中首当其冲的就是重复代码。


很多时候,当我们消除了重复代码之后,发现代码就已经比原来整洁多了。


函数过长、类过大、参数过长


过长的函数解释能力、共享能力、选择能力都较差,也不易维护。


过大的类代表了类做了很多事情,也常常有过多的重复代码。


参数过长,不易理解,调用时也容易出错。


发散式变化、霰弹式修改、依恋情结


如果一个类不是单一职责的,则不同的变化可能都需要修改这个类,说明存在发散式变化,应考虑将不同的变化分离开。


如果某个变化需要修改多个类的方法,则说明存在霰弹式修改,应考虑将这些需要修改的方法放入同一个类。


如果函数对于某个类的兴趣高于了自己所处的类,说明存在依恋情结,应考虑将函数转移到他应有的类中。


数据泥团


有时候会发现三四个相同的字段,在多个类和函数中均出现,这时候说明有必要给这一组字段建立一个类,将其封装起来。


过多的 if…else 或者使用 switch


过多的 if…else 或者 switch ,都应该考虑用多态来替换掉。甚至有些人认为除个别情况外,代码中就不应该存在 if…else 。

总结

本文首先一句话概括了我认为的好代码的必要条件:整洁,接着具体分析了整洁代码的特点,又分析了好代码的否定条件:什么样的代码不是好的代码。仅是本人的一些见解,希望对各位以后的编程有些许的帮助。


我认为仅仅编写出可运行的代码是远远不够的,还要时刻注意代码的整洁度,留下一些漂亮的代码,希望写的代码都能保留并运行 102 年!


本文转载自公众号淘宝技术(ID:AlibabaMTT)


原文链接


https://mp.weixin.qq.com/s/AjubL4vVhFa_FIlaopLVCA


2019-08-27 08:006590

评论

发布
暂无评论
发现更多内容

人民网《外企谈信心》| Denodo:加强数据管理技术合作 护航数字经济高质量发展

科技汇

如何让 Bean 深度感知 Spring 容器

江南一点雨

Java spring

Topaz Video AI 4.0.2 视频增强和修复工具

彩云

Topaz Video AI

大型企业财务共享建设避坑指南之场景化表单设计

用友BIP

财务共享

使用 Appilot 部署 Llama2,会聊天就行!

SEAL安全

Walrus llama-2 Appilot 企业号11月PK榜

Photoshop 2024 Mac最新资源 附 alpaca增效工具 可完美替代AI创成式填充

加油,小妞!

ps AI绘图 Photoshop 2024

「我在淘天做技术」1688的AIGC商业化落地实践探索

阿里技术

创意 校园招聘 1688 B端 AIGC

企业办公为什么要选择局域网im即时通讯软件

WorkPlus

大模型的“成本瘦身”运动

脑极体

AI

「最新」Rhino 8(犀牛 8)for Mac「三维建模」

彩云

三维建模 Rhino 8

Hexo+Github+Netlify博客搭建教程

Leo

博客

生态合作升级!用友Fast by BIP On 阿里云战略发布

用友BIP

CodeWhisperer 的安装及体验

亚马逊云科技 (Amazon Web Services)

Java Python 人工智能 云上探索实验室 Amazon CodeWhisperer

Python 正则表达式(RegEx)指南

小万哥

Python 程序员 软件 后端 开发

Rhino 8中文激活最新:Mac电脑强大的3d建模软件

mac大玩家j

Mac软件 建模软件 建模工具 3d建模

在Linux上玩原神!2023-10月武汉Linux爱好者线下沙龙(WHLUG)活动回顾

nn-30

Linux 操作系统 技术沙龙 deepin WHLUG

Docker打包前端vue代码推送镜像到远程仓库

javaNice

Java’ Docker 镜像

人人用数不用愁,动态数据脱敏为您解忧

华为云开发者联盟

数据库 大数据 华为云 数仓 华为云开发者联盟

企业数智化领先实践,学得会!

用友BIP

数智化转型

ps插件:Camera Raw for Mac 16.0中文版

加油,小妞!

ps插件 Camera Raw 16

WorkPlus 局域网即时通讯工具,加速团队协作,提升企业工作效率

WorkPlus

Java基础面试题【分布式】

派大星

分布式, Java 面试题

Mac电脑照片拼图软件 CollageIt Pro免激活最新版

胖墩儿不胖y

Mac软件 照片处理工具 照片拼贴软件

效果不好,为什么?

矩视智能

深度学习 机器视觉

掌握项目管理:8大风险分析工具与技术全解析

爱吃小舅的鱼

项目管理 产品经理 风险分析

最常用的4种光纤接口结构有哪些?

小魏写代码

阿里工程师告诉你:什么是好的代码?_文化 & 方法_马飞翔_InfoQ精选文章