一般来说,代码规范是一个企业每个开发人员都必须遵循的规定,发布前会有统一代码自动检测,违反任何一条都不允许发布。个人认为,开发人员除了要遵循这些硬性规定,平常也应该要有良好的偏软性的代码习惯,就像生活中除了法律还有大家都崇尚的道德一样。两者的出发点都是为了确保更高的代码质量,只是有些规定不好自动化或者没有被考虑到,就只能作为一个良好的代码习惯存在着。Code Review阶段是审查代码习惯的一个好时机。下面罗列一下我自己在工作中总结的十几条比较好的代码习惯:
不涉及数据库查询的参数校验或参数修正,应该停留在controller层,只有涉及数据库查询的校验才可以放到service层(比如数据查重)。这样可以保持service层逻辑的整洁,避免被一大堆的校验代码污染可读性,同时清晰的代码责任分离有助于更快定位问题代码的位置。校验建议最好都使用注解或自定义注解。不过目前hibernate-validator的自定义注解写法实在丑陋,不易读不易维护不易复用,还不如直接在dto上直接写个validate的方法来的简单直接。
对entity的处理应该在entity中封装对应的构造方法或create方法或edit方法来处理,不应一大坨代码放在service。有些杠精可能会说应该避免充血模型,贫血模型才是正道,额,概念我不是特别懂,反正这些模型概念被创造出来就是为了更整洁可维护的代码,过分强调某个模型多少有点过度设计的味道,这里只会把entity的创建编辑等只针对当前entity的操作封装在里面,不涉及其他逻辑,方法也不会定义很多,足够简单,何乐而不为。
一个复杂方法的实现,看起来应该像一个大纲一样,第一步做什么,第二步做什么,第三步做什么,每一步基本应该只需要一两行代码完成,复杂方法(尤其是service层的方法)中不应该有每一步的具体逻辑,具体逻辑应该另外封装方法,如果另外封装的方法也较为复杂,应该再进一步列大纲分步封装方法。封装方法时,要考虑好方法应该放在什么位置,过多的在一个类中封装private方法也容易导致代码显得杂乱无章,这时候可能单独针对较为复杂的业务逻辑相关的方法抽离到一个辅助类中。
底层方法签名要尽可能通用,比如service,dao层的方法要尽量考虑复用,避免使用DTO复杂类型作为参数,避免使用Result这种接口规范响应参数或者VO作为返回值,避免使用Map/JsonObject这种字段不清晰的结构作为参数或返回值。当然个别情况无法避免,比如创建,编辑时参数过多,只能使用DTO,又比如service的返回是分页的结果,只能用Page包装等。但对于比如queryById等这种入参和出参可以定义成简单形式的应该保留简单形式,在controller层再进行Result的包装,尽管这个方法暂时没有被复用,也应该遵循这个原则。
尽管业务上规定有些情况可以写死,也应该考虑写死的位置应该尽可能保证影响最小化。比如实现通过页面创建数据库的功能是,业务规定建索引只能所以其他表的id,在对索引的数据建模时,应考虑按照通俗理解去定义索引的数据模型,在校验层去限制只能索引id字段,这样如果将来有变,也只需要放开校验即可,而且按照通俗理解的去定义,别人一看就能看明白。当然也要衡量这么做的工作量,如果额外工作量过大就没必要了。
善用通用工具类的封装去避免业务代码的啰唆,除了常规的各种StringUtils, CollectionUtils,在业务代码实现时也可考虑自行封装。比如分布式锁的try catch finally块就很啰唆,可以考虑使用Runnable把try catch finally搬到通用方法中,调用时只需传递Runnable参数即可。
针对每个提供的http接口,要清晰地定义接口的入参DTO和出参VO,维护好swagger,尽量避免使用Map/JsonObject,不应直接使用entity,也不要轻易复用DTO/VO,除非是完全一样的参数才可考虑复用,否则都很容易混乱,后期很难维护。
尽量避免在当前entityService写其他entity的数据库逻辑,其他entity的逻辑应该写到它所属的service中,通过注入该service类进行调用。
复杂逻辑一定要多写注释,review代码发现自己都看不懂时一定要及时补上注释。
单元测试,应该避免依赖数据库已存在的数据,以及跑完之后残留脏数据的情况。具体可以考虑封装相应的工具类,使用@Before在测试方法之前插入所需数据,通过@After或者spring生命周期的destroy方法删掉过程中创建的数据。比较好一点的方案是使用mybatis拦截器来收集过程中插入的数据以便删除。另外,插入的数据可以作特殊处理,比如对于自动生成的id加上某个固定的前缀,万一数据没有成功清理时可以比较轻易地手动清理。
写代码时尽可能以最直观的方式去实现,避免因为遇到困难而选择一些曲折的方案,这样会导致代码可读性差,维护难度大。遇到问题不要绕行,要弄清楚为什么会有问题,能否解决,不能解决的情况下再考虑使用其他方案,并做好注释。
遇到某些难以定位的bug时,不要乱,要善用排除法,比如先将代码恢复到正常的时间节点,然后一点一点的增加后来修改的逻辑,逐步定位到出问题的大概位置(比如WebConfigurer问题定位)。
虽然通常代码规范限制嵌套不能超过3层,而且有些大括号块还不算嵌套,比如try catch,但我个人觉得,3层就已经很丑了,应该尽量确保在一个方法内最多2层大括号嵌套,嵌套的代码可读性实在很差,能封装方法就封装方法。
软性规范,见仁见智,欢迎谈论。