1、为什么需要代码规范

任何系统性的项目都需要架构设计,而架构设计的核心命题是控制复杂度

但随着项目的不断迭代,复杂度就会不断上升,研发效率就会不断下降。

而代码规范正是对抗软件复杂度的有效手段,通过约定俗成的规则,降低复杂度,提升研发效能。

从团队角度来说,统一的代码规范有利于减少阅读成本和理解成本,并且能提高代码质量,长期来说,项目的稳定且可维护,也能更好更快速的支撑业务发展。

2、代码是怎么变坏的

2.1、重复的代码

同一个功能,在不同的业务域,大家用同一种技术甚至不同技术都去实现了一遍,撇开复用性不说,当我们要去修改底层的逻辑实现时,上层调用有一个漏改都是莫大的风险。

2.2、早期有效的决策不再有效

初期,我们有能力把一段代码写的简洁且逻辑清晰,但是当业务不断迭代,逻辑就变得越来越复杂,当其他同学来接手时,是改还是不改,改了出问题谁来背锅?这是一个灵魂的拷问,然后一边叹气一边新增自己的代码。。。

2.3、过早的优化

过早的优化是万恶之源,为什么这么说,百万和千万级别日活的架构肯定是不一样的,架构是需要演进的,如果一开始百万级别日活就想奔着千万级别的架构去,不仅脱离了实际的业务需求,还浪费大量的人力物力财力,反而得不偿失,从实际出发,适合自己的才是最重要的。

2.4、对合理性没有苛求

技术方案往往都不是单一的,当我们有「能跑就行」的想法时,就会选择最简单粗暴的方案,但是往往这种方案的复用性和扩展性都很差,当历史的浪潮不断向前推进,就会演变成技术负债,后人一边叹气一边又在屎山上拉了一泡。

2.5、过度设计

调用链路就像老奶奶的裹脚布一样,又臭又长,一层又一层,增加理解成本,降低开发效率。

2.6、没有设计

没有设计也相当可怕,一个函数动辄上千行,剪不断理还乱,然后后来者又是一边叹气一边。。。

2.7、排期紧

曾经看到这么一个问题,为什么大厂屎山也这么多,高赞的前两个回答是这么说的:

  1. 因为只允许有写一遍就成的时间
  2. 因为能用就行,需求都排不过来

进入一个死循环,屎山越堆越高。。

3、如何让代码变好

3.1、命名

大到项目名、模块名、包名、对外暴露的接口,小到类名、函数名、变量名、参数名,只要是做开发,我们就逃不过「起名字」这一关。命名的好坏,对于代码的可读性来说非常重要,甚至可以说是起决定性作用的。

3.1.1、命名多长最合适?

有两种情况,一种命名特别短,对于代码的编写者来说,自己对代码的逻辑很清楚,总感觉用什么样的命名都可以达意,实际上,对于不熟悉你代码的同事来讲,可能就不这么认为了。

另一种,命名很长,觉得命名一定要准确达意,哪怕长一点也没关系,但是,如果函数、变量的命名很长,那由它们组成的语句就会很长。在代码列长度有限制的情况下,就会经常出现一条语句被分割成两行的情况,这其实会影响代码可读性。

原则上,命名是以能准确达意为目标。多换位思考,以阅读者的视角去考量命名是否够直观。

3.1.2、命名要可读、可搜索

什么是命名可读,指的是不要用一些特别生僻、难发音的英文单词来命名,更不要随意造词。

虽然我们并不排斥一些独特的命名方式,但起码得让大部分人看一眼就能知道怎么读。而生僻、难发音的单词会严重影响交流沟通。

其次是可搜索,我们在IDE中编写代码的时候,经常会用「关键词联想」的方法来自动补全和搜索。比如,键入某个对象「.get」,希望IDE返回这个对象的所有get开头的方法。

3.1.3、行业规范

还有一些行业通用的规范:

  1. 接口前缀加「I」,表示一个Interface。比如IUserService,对应的实现类命名为UserService;
  2. 弹窗后缀加「Dialog」,表示一个Dialog。比如AppUpdateDialog;
  3. 工具类后缀加「Utils」,表示一个工具类。比如TrackUtils;
  4. 等等;

3.1.4、示例

bad:

fun getName(){}

good:

fun getUserName(){}

3.2、注释

命名很重要,注释跟命名同等重要。

3.2.1、注释应该写什么?

注释的目的就是让代码更容易看懂。只要符合这个要求的内容,你就可以将它写到注释里。

比如,阐述代码的逻辑,你为什么这么做,想要达到什么样的效果等等。

3.2.2、注释是不是越多越好?

注释太多和太少都有问题。

太多,有可能意味着代码写得不够可读,需要写很多注释来补充。除此之外,注释太多也会对代码本身的阅读起到干扰。而且,后期的维护成本也比较高,有时候代码改了,注释忘了同步修改,就会让代码阅读者更加迷惑。

当然,如果代码中一行注释都没有,那只能说明这个程序员很懒,我们要适当督促一下,让他注意添加一些必要的注释。

3.2.3、注释类别

3.2.4、示例

bad:

    /*** 取消*/protected fun cancelJob(job: Job?) {if (job != null && job.isActive && !job.isCompleted && !job.isCancelled) {job.cancel()}}

good:

    /*** 取消协程 会抛出CancellationException* @param job 协程job*/protected fun cancelJob(job: Job?) {if (job != null && job.isActive && !job.isCompleted && !job.isCancelled) {job.cancel()}}

3.3、代码风格

3.3.1、函数、类多大才合适?

函数的代码行数不要超过一屏幕的大小,比如50行。

3.3.2、一行代码多长最合适?

最好不要超过IDE的显示宽度。当然,也不能太小,否则会导致很多稍微长点的语句被折成两行,也会影响到代码的整洁,不利于阅读。

3.3.3、善用空行分割单元块

对于比较长的函数,为了让逻辑更加清晰,可以使用空行来分割各个代码块。

除此之外,在类的成员变量与函数之间、静态成员变量与普通成员变量之间、各函数之间、甚至各成员变量之间,我们都可以通过添加空行的方式,让这些不同模块的代码之间,界限更加明确。写代码就类似写文章,善于应用空行,可以让代码的整体结构看起来更加有清晰、有条理。

3.3.4、格式化

使用统一的格式化规则,比如空格、换行等,格式化规则不统一,容易引起不必要的变更,不利于代码评审和历史变更查询。

3.3.5、示例

bad:

AlertDialog.Builder(context).setView(0).setTitle(R.string.dialog_title).setMessage(R.string.dialog_message).setIcon(0) .create()

good:

AlertDialog.Builder(context).setView(0).setTitle(R.string.dialog_title).setMessage(R.string.dialog_message).setIcon(0).create()

3.4、编码技巧

3.4.1、把代码分割成更小的单元块

大部分人阅读代码的习惯都是,先看整体再看细节。所以,我们要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节,让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。不过,只有代码逻辑比较复杂的时候,我们其实才建议提炼类或者函数。毕竟如果提炼出的函数只包含两三行代码,在阅读代码的时候,还得跳过去看一下,这样反倒增加了阅读成本。

3.4.2、避免函数参数过多

我个人觉得,函数包含3、4个参数的时候还是能接受的,大于等于5个的时候,我们就觉得参数有点过多了,会影响到代码的可读性,使用起来也不方便。

一般有2种处理方法:

  1. 考虑函数是否职责单一,是否能通过拆分成多个函数的方式来减少参数。
  2. 将函数的参数封装成对象。

3.4.3、函数设计要职责单一

我们在前面讲到单一职责原则的时候,针对的是类、模块这样的应用对象。实际上,对于函数的设计来说,更要满足单一职责原则。相对于类和模块,函数的粒度比较小,代码行数少,所以在应用单一职责原则的时候,没有像应用到类或者模块那样模棱两可,能多单一就多单一。

整洁的代码只做好一件事,干脆利落,直接了当,易于阅读,易于维护。

3.4.4、移除过深的嵌套层级

代码嵌套层级过深往往是因为if-else、switch-case、for循环过度嵌套导致的。我个人建议,嵌套最好不超过两层,超过两层之后就要思考一下是否可以减少嵌套。过深的嵌套本身理解起来就比较费劲,除此之外,嵌套过深很容易因为代码多次缩进,导致嵌套内部的语句超过一行的长度而折成两行,影响代码的整洁。

针对层级嵌套过深的代码可以使用多态简化逻辑,移除不必要的if或else,也可以使用策略模式,提前return退出嵌套等。

3.4.5、少即是多

无形装逼最为致命:

  1. 过度设计:有的同学为了炫技,各种设计模式咔咔往上整,反而增加复杂度;
  2. 隐式耦合:这种是想炫技但功力不够的,设计的不够优雅反而留下后遗症;

建筑师米斯.凡德洛曾说过,less is more,提倡简单,反对度装饰的设计理念。简单的东西往往带给人们的是更多的享受。

4、客户端的技术栈

上面介绍了一些通用的规范,然而时代在变化,技术在演进,客户端的技术更新也是日新月异,因此也需要针对不同的技术栈有系统性的规约及代码风格。

比如:

  1. Java的文件名遵循驼峰命名法,而在Flutter中文件名使用下划线隔开;
  2. Java和Kotlin、OC和Swift,不仅有类型推导上的区别,还有一些语法糖的特性;
  3. 等等;

下面是端上现有的一些技术栈:

语言 规范文档
Android Java Java开发手册(嵩山版)Google Java Style Guide
Android Kotlin Kotlin Coding conventions
iOS Objective-C Coding Guidelines for Cocoa
iOS Swift Swift Style Guide
跨端 Flutter Effective Dart
跨端 动态化xxx(json -> TypeScript) Google TypeScript Style Guide
其他 配置文件(xml、yml,json) Google XML Document Format Style Guide
其他 脚本、插件(Python、Shell、Groovy) Shell Style GuidePython Style Guide

google开源规约:https://google.github.io/styleguide/

5、治理手段

5.1、检测工具

通过一些类似Lint之类的检测工具,在编写阶段通过警告,把不符合规范的代码扼杀在摇篮里。

Alibaba Java Coding Guidelines

5.2、代码评审

代码评审也称code review,俗称cr,cr的意义在于,作为局中人,尽管我们在编写代码的时候小心翼翼,但也可能会在无意间犯下一个小错误,而此时cr的人作为旁观者,可有一语点醒梦中人的效果,而且从实际问题出发产生思想的碰撞,相互学习,也有利于提升团队整体的编码水平。

5.3、扫码行动

我们端上正在做一些代码的治理,删除无用代码,下线老代码,比如原有的灰度校验在全量之后理应下线老的逻辑代码。

5.4、代码重构

我们也正在做模块化的重构治理,把以前设计不合理或者不满足现状诉求的地方做改进和优化。

6、一点思考

治理行动我们可以一年来一次,但这很明显不是最好的解决办法,代码是人写的,工具是辅助是底线,如何长治久安,还是要从根源上着手下功夫,这就需要我们团结一致,从思想上认可,从行动上落实,认真做好code review,始终对代码保持敬畏,对自己的代码负责,做一个有信仰有追求的程序员。

7、相关书籍

  • 人月神话
  • 代码整洁之道
  • 架构整洁之道
  • 编程珠玑
  • 重构·改善既有代码的设计
  • 设计模式之美

8、参考文档

  • 腾讯工程师,万字长文说 Code Review
  • 如何编写垃圾代码
  • 设计模式之美
  • 对抗软件复杂度的战争

代码规范-对抗软件复杂度相关推荐

  1. 对抗软件复杂度的战争

    作者:晓斌 阿里技术风险与效能团队 服务一个人的系统,和服务一亿人的系统,复杂度有着天壤之别.本文从工程师文化.组织战略.公司内部协作等角度来分析软件复杂度形成的原因,并提出了一些切实可落地的解法. ...

  2. 百度工程师手把手教你实现代码规范检测工具

    01 引言 代码规范是软件开发领域经久不衰的话题.在前端领域中,说到代码规范,我们会很容易想到检查代码缩进.尾逗号以及分号等等,除此之外,代码规范还包括了针对特殊场景定制化的检查.JavaScript ...

  3. IDEA工具(阿里巴巴)代码规范检查插件

    1.代码规范 因为软件是需要人来维护的.这个人在未来很可能不是你.所以首先是为人编写程序,其次才是计算机 不要过分追求技巧,降低程序可读性. 简洁的代码可以让BUG无处藏身.要写出明显没有BUG的代码 ...

  4. 对抗软件规模与复杂度的战争:救命、治病、养生(上篇)

    - 从Google的一页PPT开始谈起 - 大概在10年前,我在美国参加一个软件工程的会议,其中有一个来自Google的话题,内容具体讲啥我已经记不清了,但是PPT开头的第一页给我留下了深刻的印象.原 ...

  5. 射手科技公开课第一辑 『项目管理和代码规范』

    射手玩的东西越来越全面了,从当年的字幕下载站,到播放器,到射手科技,发展的思路值得借鉴和思考. 射手科技成立3个月以来,我们内部已经组织了不少培训.每次内部培训我们都留有录像和录音,以便后续参与项目的 ...

  6. 一篇走心的iOS代码规范!

    前言 关于代码规范的重要性这里不做过多解释,能看到这篇文章说明你已经开始重视代码规范了(代码规范看起来是在限制你的自由和发挥,其实它是在间接的帮助你变得更优秀.). 适当的代码规范和标准绝不是消灭代码 ...

  7. 阿里Java代码规范

    代码规范 一.编程规约 (一) 命名风格 (二) 常量定义 (三) 代码格式 (四) OOP 规约 (五) 集合处理 (六) 并发处理 (七) 控制语句 (八) 注释规约 (九) 其它 二.异常日志 ...

  8. 网站开发之前端代码规范

    前端代码规范 前言 一.唯一定律 二.前段代码规范 (一)命名规范 1.1.项目命名 1.2 .目录命名 1.3.文件存放位置 1.4.JS.CSS.HTML.PNG 等文件命名 1.4.命名严谨性 ...

  9. 浅谈代码规范基础调试几道面试题

    废话篇:本文由CSUST的FINAL实验室的LX创作,用途是给予CSUST的小鲜肉们一些关于C语言代码规范的一些基本知识,若本文有什么错误或是表述不清之处,欢迎留言讨论指正. 代码规范: 在讲代码规范 ...

最新文章

  1. python中保留两位小数的编写程序_Python中保留两位小数的几种方法
  2. java Spring 生命周期
  3. Java8初体验(二)Stream语法详解(转)
  4. 全网最经典26道Spring面试题总结,附面试题答案
  5. 001_SpringBoot入门
  6. 做了表分区以后的数据库表,在使用程序进行修改操作时报异常。
  7. android 动态矩形条,android – 从相机中动态检测不同形状(圆形,方形和矩形)?
  8. java蝇量模式_Head First设计模式——蝇量和解释器模式
  9. 游标、过程、函数、包
  10. OpenCV中绘制外围矩形框和圆框
  11. python 驱动级鼠标_罗技各系鼠标测评(2020年12月更新)
  12. LeetCode 1111. 有效括号的嵌套深度
  13. 因特网人群信息的应用-通过人群性格特点投放定制广告
  14. 如何将pdf转换成word文档,文件格式转换器选择
  15. 【MySQL】MySQL-主从复制-集群方案-数据一致性问题解决方案 MySQL备份的各种姿势...
  16. Error response from daemon: Cannot restart container mdet_jc: OCI runtime create failed(fork/exec /)
  17. 模式识别工具箱安装及使用
  18. 突破网络执法官封锁的方法及其原理
  19. 公众号关键字自动回复内容点击跳转小程序方法
  20. Vue-amap 实现获取定位功能

热门文章

  1. Unity【Multiplayer 多人在线】- Socket 通用服务端框架(七)、时间戳和心跳机制
  2. android 键盘上方浮动,【已解决】点击input输入框时Android端底部的Tab弹出显示在键盘上方...
  3. 案例|智慧金融:借助AI训练数据打造全新数字员工
  4. 这 5 个编程名言还请您收好
  5. 【调剂】四川师范大学2020年硕士研究生接收调剂专业
  6. HDFS——editLog文件
  7. ToolBar的用法
  8. 为什么HQST网络变压器外PIN要加镀一遍低温含锡锡
  9. UWB高精度定位落地:UWB(超宽带高精度定位)赋能智慧工厂人/车/物位置感知服务
  10. 用户体验的时机来源:ChinaUI 作者:Lytous