第一章、代码编写规范(强制要求)
1.1、代码中不要出现莫名其妙的数字
很多程序员在实现功能的时候会分配一个没有明确意义的数字,用于特定的目的,写完这个数字后没有即使去做注释,然后过两天当程序出现问题时,翻看代码碰到这个数字,连自己都忘了它的意义。当程序需要固定的数值参与时,给予特定的名称。
案例:
|
1.2、深度嵌套
有些程序需要使用到无数次的嵌套循环,看着看着就晕了,这时可以将所有循环提取到单独的函数中。假设有一个包含两层嵌套的数组,想要拿到最后一个数组的值?一般情况下开发者都会使用嵌套循环,但不是最佳的办法。可以尝试使用函数的反复调用来完成操作执行相同操作的,这样一来代码重复性更小,更易于阅读、重用性高。
对于单数组的多层循环,建议使用while(),注意控制退出条件,避免产生死循环
|
1.2.1补充,尽量不要用死循环,给循环一个退出条件。
我们做业务,不是做竞赛,写代码的目标是简介、清晰、可维护。不要写花里胡哨,但是实际上特别难理解的代码。一个简洁清晰的代码,是你看一遍就能明白它在做什么的代码。
很多算法书都会推荐死循环,也会在示例里用死循环,因为算法领域里面,需要缩减行数来达到最简。我们做业务,代码死循环,只能kill这个服务。否则,只能堆栈溢出或者把资源耗完,这种都是代价很大的。这还不考虑,死循环如果影响了数据库,造成的不可逆的数据错误。所以,给循环一个结束条件,让它能正常的结束循环。不要用while(ture),然后在循环里if去break,如果if的判断失效,就没有能解开循环的方法了,把判定条件写在括号里。
1.2.2补充,不要省略if和for的大括号
我知道这种写法很酷,但是,别人读代码,改代码,解决本地不能调试,只能线上调试的bug的时候,一定骂你的。
1.2.3补充,优先判定,提早退出的原则
|
1.2.4补充,防御性编程原则
你应该在方法内部判定完整,健壮。如果这个方法不会别的类用,你就声明private。对于public的方法,一定要多判定。因为你不知道,调用方会传什么参数进来。你现阶段看到可能入参,后面可能后面会变。最常见的例子,是你写的方法,现阶段入参一定不为空。后面业务调整,别人新加的代码调用你这个方法,这个值就是给你传空。一调就是空指针,他还得说你代码逻辑上有bug,不会说他业务调整导致的控制,甩锅给你。调你的方法,还要改你的代码,不要给他甩锅的机会。
1.3、注释
注释是一个老生常谈的话题,很多人不重视,为了快速完成项目忽略注释,导致后期修改和复用出现很大问题,得不偿失。
对于复杂的逻辑方法,应注明内部逻辑;对于方法应该写明方法的作用。
1.4、避免创建大函数
当一个函数或一个类需要实现的功能太多时,建议将其分成多个小的函数。
假设我们需要加减两个数字。我们可以用一个功能做到这一点。但是好的做法是将它们分为两部分。如果有单独的功能,则可以在整个应用程序中重复使用。
|
1.5、重复代码
重复代码也是常见的问题,如遇到此情况,将重复代码提取到函数中。继续用上面的第2点“深层嵌套”中的例子来举例。
|
1.6、变量命名
变量命名也是有讲究的,好的变量命名可以让代码更加通俗易懂,一般来说命名遵循以下标准,名称以小写字母开头,之后的每一个单词首字母都用大写,比如骆驼的大小写为camelCase函数和变量都必须遵循此规则。
示例代码:
|
1.7、for循环里面做rpc调用,或者是数据库交互
不要在循环里面做rcp调用和数据库交互,这样会造成系统性能和数据库压力。
1.8、方法体和类行数太多
一个方法体的代码行数最好不要超过100行,一个类文件不要超过1500行,太大的方法体和类,对于代码阅读来说都是一种灾难。
1.9、程序分层之间功能要明确。
程序分层之后,各个层之间的功能要明确清楚,
controller层:数据校验,参数预处理,返回参数的封装;
server层:数据调用和数据集合操作,以及相关的逻辑处理判断操作逻辑代码,不要添加跟数据库相关的代码,跟数据库相关处理的功能交给dao层来处理;
dao层:只负责和数据库的数据交换。
1.10、抽象父类方法的抽取
公共抽象父类和泛型父类的抽取,尽量以解决业务复杂度的目标来进行抽取,或者是统一工具处理类的方式来定义,尽量不要再controller、server、mapper层使用,导致业务使用过程中的代码混乱,以及调用参数不明确等一系列问题。
1.10.1、补充,抽象父类和方法要想的长远一点
不要看见一点公用的部分,就想抽象。抽象的时候,就抽象得彻底一点。别抽象的方法里还有定制代码。别人想继承,又继承不了。
|
1.11、类的定义的单一职责的原则
对于创建的java类对象,应该尽量遵守单一职责的原则,一个类只实现某一种或者是某一类相关的功能操作,例如:人员数据的增删改查,人员信息的统计,人员信息和角色数据的聚合。
1.12、服务之间调用规范
业务上涉及到服务内部或多系统之前的多业务数据处理业务,应该在程序设计时考虑到服务间调用的耦合关系,尽量降低服务间的耦合情况,能在一个类或者是一个方法里处理的单一功能,尽量在一个方法体或者类里面处理完。对于处理不了或者是本身就需要多服务调用的部分,
应采用例如:切片编程、链式编程、事件通知、事件回调、mq消息队列、spi等方式实现服务间的调用解耦。服务解耦后如果存在数据一致性问题,也应该充分的考虑数据的回滚等相关的操作异常的补偿机制。
注:对于耦合处理过的代码,应该在代码注释中明确的注明响应的隔离机制,及影响到的服务范围,降低代码升级带来的风险。
1.13、功能规范
功能设计之初,应该考虑到功能与服务内部其他功能之间的关联关系,和服务后期迭代升级过程中的难度和代价。对于独立性较强的功能服务,应该从原有服务相对的抽离出来,比如创建较为独立的模块(model)或者是建立相对独立的文件夹系统(package),保证独立功能的相对独立性。
1.14、统一异常处理
对于所有的异常,应该有相对应的异常类,异常类可以使用公有jar包里的提倡类,但是全局异常捕获必须在自己的方法里实现,不能直接使用共有异常捕获类。
捕获异常一定要用自己写的全局异常处理,不要用切面。
1.15、参数校验
参数校验统一使用@Validated 进行简单的传参校验,在统一异常处理里处理异常报错信息。
1.16、rest接口定义规范
新增数据接口,统一不传id,id由后端统一生成;
更新数据接口传全量数据,但是如果只修改关联数据,则关联数据应该有单独的更新结构;
删除接口默认只传id。
1.17、数据库脚本定义规范
每次服务存在sql脚本的变动,都需要在服务的doc目录下新建一下【日期+分支名】的文件夹,文件夹在保存脚本文件,脚本文件中应该尽量添加注释,说明功能和备注信息。
1.18、尽量不要使用Lambda 表达式
虽然Lambda 表达式能提升代码的编程效率,但是会导致在代码出现异常时,寻找报错行数造成一定的困扰,增加排除问题的难度。
1.19、rpc调用
尽量把所有的外部调用(grpc、dubbo、rest)放到统一的包或者是类里面,这样可以更加方便的做到外部调用管理。特别是通过httputils直接访问url的形式。很难做到调用链的溯源。