分析一套源代码的代码规范和风格并讨论如何改进优化代码
在上一篇博客中,已经交代过了,我所选的工程实践题目是物联网组网智能分析引擎。然后我分别借用百度、谷歌两大搜索引擎,并且在GitHub以及码云中查找了一番,发现要找到与题目相关联的代码还真是有些困难。但我又想到,我们工程实践的要求是后端使用Spring Boot,前端使用React或者是Vue框架,那么我这里就以一个开源的Spring Boot前后端分离的优质项目,来进行代码规范和风格的分析。
问题1
问:结合工程实践选题相关的一套源代码,根据其编程语言或项目特点,分析其在源代码目录结构、文件名/类名/函数名/变量名等命名、接口定义规范和单元测试组织形式等方面的做法和特点;
答:首先该项目的后端开发使用的是Java语言,虽然这个项目不算一个大项目,因为一个大项目定义是投入使用并且受众很广,但项目开发不都是从小做到大的吗,腾讯帝国也并非一蹴而就,所以我们开发项目的时候一般都会遵循一个规范来开发。说到开发规范,很出名的一个就是MVC模式,那么MVC是什么?MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
那么,来看一下这个源代码项目的目录结构如何?
可以很清晰地看到,这个项目所包含的目录有实体类bean,控制器controller,异常处理exception,接口mapper以及服务service。虽然与原始的MVC开发规范有所出入,但一接触到这个项目目录,就基本上能够分辨出这个目录中的代码文件在整个项目中是一个什么作用的部分。就比如bean中的文件,很容易就能够判断其定义的是项目中所需要使用到的实体类,那么再验证一下:
没错,这里定义了两个字符串变量,以及自动生成的构造方法和一系列get、set方法,很显然就是一个普通的实体类。
再来观察一下文件名/类名:ChatResp,所表达的意思很显然就是聊天,再结合类中所定义的变量msg——message,以及from——从哪儿来,再联想到该项目是一个人力资源管理系统,那么很容易就理解,哦!这是一个定义在线聊天功能的类。
再来看一个文件:ChatController,从文件名中很容易发现,按照规范化的编码,这个控制器显然与某个Service和Mapper对应,不过这里只分析代码规范,模式问题暂且不谈。那么这个文件的部分内容是:
这次重点观察一下类中的函数名定义,截图中总共包含三个函数,分别是:getAllHr(),sendNf()以及getSysMsg(),以初中英语单词储备量都能够大概猜出来这几个函数的主要功能是什么,无非是,得到所有的人力资源部的信息、发送某个信息以及获得系统信息。当然,函数具体的作用还要具体情况具体分析。
同时注意到,函数的命名通常是要小写字母打头,而后一个单词大写。对于类名来说则是首字母大写,后一个单词同样也大写。
问题2
问:列举哪些做法符合代码规范和风格一般要求
答:按照前面分析的结果,该项目在文件命名、变量命名、类名命名等方面是比较符合常规的规范的,对于没接触过这个项目的读者来说能够在很短的时间内读懂,这也是一个项目维护管理的基本要求。毕竟进了一个大型的公司,公司内部分工明确,一个项目需要很多人来共同完成,如果别人看了好半天,还没弄懂这家伙到底在表达什么,那还怎么对接?
其次,我们注意到代码的目录结构比较合理清晰,这也是一个大项目开发的必备要素。这样的观念在工程实践的过程中也应该树立起来,就是,我们写出来的代码,不是只给自己看的,还要与小组其他成员对接。我们个人所开发的往往是一个整体项目的几个零碎模块,就好像拼图一样,几块的样子终究不是全貌,只有凑在一起才会大放异彩。
最后,项目中还体现出一张风格,是每个Java程序员应该培养的,那就是缩进。缩进的作用,就如同语文中的标点符号,试想一下,去掉标点符号,朱自清的《春》是不是也没那么唯美了?看着实在难受。那么同样的,在编写代码的时候,适当地使用缩进和空行,能够很大程度上提高代码的可读性。
问题3
问:列举哪些做法有悖于“代码的简洁、清晰、无歧义”的基本原则,及如何进一步优化改进
答:在类、变量的命名中,微人事这个项目做到了简洁、清晰、无歧义,但是在函数命名上有一些还存在着歧义的表达,就比如我之前列举的getSysMsg()——获取系统信息,那么这里的系统信息是什么?在没看到函数主体内容之前,我可能猜想是前端的表单信息,但实际上这里获取的是分页的一些信息。所以将函数名改正为getSysMsgByPage()或许要清晰许多。
这里,同样暴露出项目的另一个问题,就是注释太少!前面说过,代码并非写给自己看的,同事和学习者都需要快速了解到这段代码的具体作用,特别是这个项目在GitHub上的Star已经超过10000的情况下。在认真阅读了这段代码之后,我简单地给出这样的注释:
文字并不算多,但对于阅读代码的读者来说,能够先入为主地对这段有一个整体认识。
问题4
问:总结同类编程语言或项目在代码规范和风格的一般要求
答:事实上,除了主观性的命名以外,其他的规范和风格都很容易能够通过IDE(比如IDEA)来实现统一。
但我们不能总是依赖着IDE,以至于以后手写代码都不会了,所以还需要了解一下规范的代码如何编写?
主要参考:Google Java编程风格指南 https://google.github.io/styleguide/javaguide.html
基本命名规范
包命名规范:包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。一般单词使用单数形式,但是类名如果有复数含义的话,则可以使用复数形式。
类,接口命名:类的名字必须由大写字母开头而单词中的其他字母均为小写;如果类名称由多个单词组成,则每个单词的首字母均应为大写例如TestPage;如果类名称中包含单词缩写,则这个所写词的每个字母均应大写,如:XMLExample,还有一点命名技巧就是由于类是设计用来代表对象的,所以在命名类时应尽量选择名词。
方法名:方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头。可以为动词或动词+名词组合。1. 设置/获取某个值的Method,应该遵循setV/getV规范;2. 返回长度的Method,应该命名为length;3. 测试某个布尔值的Method,应该命名为isV;4. 将对象转换为某个特定类型的Mehod应该命名为toF。比如:getDate(); length(); isReady(); toOracleFormat()。
类名:类名采用大驼峰的命名形式,所谓大驼峰就是首字母大写,例如UpperCameCase。抽象类命名使用 Abstract 或 Base 开头;异常类使用 Exception 结尾;测试类命名以测试的类名开始,以 Test 结尾。枚举类名带上 Enum 作为后缀,枚举成员名称需要大写,单词间用下画线隔开。
变量名:
(1)常量,在 Java 中,常量一般指 final 关键字修饰的变量。
1、全局常量和类内常量的命名采用字母全部大写,单词之间加下画线的方式。
所谓全局常量即类的公开静态属性,使用 public static final 修饰;类内常量指的是私有静态属性,使用 private static final 修饰。
2、局部常量则采用小驼峰的形式。所谓局部常量指的是方法内的常量。
(2)可变变量
可变变量一般常用小驼峰的命名形式,如 myName ,小驼峰和大驼峰的区别就是,小驼峰首字母小写,而大驼峰首字母大写。不过需要注意的是,针对布尔类型的变量,在命名的时候,不要用 is 做前缀,否则部分框架在解析的时候会引起序列化错误。
例如标识是否删除的成员变量 Boolean isDeleted, 它的 getter 方法也是 isDeleted(),框架在反向解析的时候,会误认为对应的属性名称为 deleted,从而引起错误。
注释
在每个程序的最开始部分,一般都用Javadoc注释对程序的总体描述以及版权信息,之后在主程序中可以为每个类、接口、方法、字段添加 Javadoc注释,每个注释的开头部分先用一句话概括该类、接口、方法、字段所完成的功能,这句话应单独占据一行以突出其概括作用,在这句话后面可以跟随更加详细的描述段落。在描述性段落之后还可以跟随一些以Javadoc注释标签开头的特殊段落,例如上面例子中的@auther和@version,这些段落将在生成文档中以特定方式显示
缩进与空格
一个缩进的距离等于四个空格的距离,但究竟是要使用 Tab 缩进来调距离还是用四个空格代替一个缩进来调距离,这个貌似存在争议,有些大佬建议用 Tab 键,有些大佬建议用空格。我在《码出高效Java开发手册》里,本书的作者是推荐四个空格缩进,禁止使用Tab键。
当然,你在使用IDE的时候,当你换行时,很多编辑器是会帮你自动缩进的,大多数IDE都是默认四个空格来缩进。
不过很多 IDE 工具提供了 Tab 键与空格之间的快速转换设置。例如对于 IDEA 这个工具,要设置 Tab 键为四个空格时,可以在设置那里勾选 Use tab character(setting->editor->Code Style->选择你想编辑的语言);而在 Eclipse 中,就得勾选 Insert spaces for tabs。
关于空格,我有以下建议:
1、二目、三目运算符的左右两边都应该加一个空格。
2、注释的双斜线与注释内容之间有且仅有一个空格。
3、方法参数在定义和传入参数时,多个参数逗号后边都应该加空格。
4、如果大括号为空,则简洁地写成{}即可,大括号中间无须换行和加空格。
5、左右小括号与括号内部的相邻字符之间不要出现空格。
6、左大括号前需要加空格。