导读

随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题。持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一。软件系统思维有句话“Less coding, more thinking(少编码、多思考)”,也有这么一句俚语“Think more, code less(思考越多,编码越少)”。所以,我们在编码中多思考多总结,努力提升自己的编码水平,才能编写出更优雅、更高质、更高效的代码。

本文总结了一套与Java函数相关的编码规则,旨在给广大Java程序员一些编码建议,有助于大家编写出更优雅、更高质、更高效的代码。这套编码规则,通过在高德采集部门的实践,已经取得了不错的成效。

1.使用通用工具函数

案例1: 字符串比较

现象描述:

不完善的写法:

更完善的写法:

建议方案:

案例2: 判断列表为空

现象描述:

建议方案:

主要收益

  • 函数式编程,业务代码减少,逻辑一目了然;
  • 通用工具函数,逻辑考虑周全,出问题概率低。

2.拆分超大函数

当一个函数超过80行后,就属于超大函数,需要进行拆分。

案例1: 每一个代码块都可以封装为一个函数

每一个代码块必然有一个注释,用于解释这个代码块的功能。

如果代码块前方有一行注释,就是在提醒你——可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。如果函数有一个描述恰当的名字,就不需要去看内部代码究竟是如何实现的。

现象描述:

建议方案:

案例2: 每一个循环体都可以封装为一个函数

现象描述:

建议方案:

案例3: 每一个条件体都可以封装为一个函数

现象描述:

建议方案:

主要收益

  • 函数越短小精悍,功能就越单一,往往生命周期较长;
  • 一个函数越长,就越不容易理解和维护,维护人员不敢轻易修改;
  • 在过长函数中,往往含有难以发现的重复代码。

3.同一函数内代码块级别尽量一致

案例1: 代码块级别尽量保持一致

现象描述:

很明显,睡觉这块代码块,跟eat(吃饭)和code(编码)不在同一级别上,显得比较突兀。如果把写代码比作写文章,eat(吃饭)和code(编码)是段落大意,而睡觉这块代码块属于一个详细段落。而在liveDaily(每日生活)这个函数上,只需要写出主要流程(段落大意)即可。

建议方案:

主要收益

  • 函数调用表明用途,函数实现表达逻辑,层次分明便于理解;
  • 不用层次的代码块放在一个函数中,容易让人觉得代码头重脚轻。

4.封装相同功能代码为函数

案例1: 封装相同代码为函数

现象描述:

建议方案:

案例2: 封装相似代码为函数

封装相似代码为函数,差异性通过函数参数控制。

现象描述:

建议方案:

主要收益

  • 封装公共函数,减少代码行数,提高代码质量;
  • 封装公共函数,使业务代码更精炼,可读性可维护性更强。

5.封装获取参数值函数

案例1: 把获取参数值封装为函数

现象描述:

建议方案:

主要收益

  • 把获取参数值从业务函数中独立,使业务逻辑更清晰;
  • 封装的获取参数值为独立函数,可以在代码中重复使用。

6.通过接口参数化封装相同逻辑

案例1: 通过接口参数化封装相同逻辑

现象描述:

建议方案:

主要收益

  • 把核心逻辑从各个业务函数中抽析,使业务代码更清晰更易维护;
  • 避免重复性代码多次编写,精简重复函数越多收益越大。

7.减少函数代码层级

如果要使函数优美,建议函数代码层级在1-4之间,过多的缩进会让函数难以阅读。

案例1: 利用return提前返回函数

现象描述:

建议方案:

案例2: 利用continue提前结束循环

现象描述:

建议方案:

特殊说明:

其它方式:在循环体中,先调用案例1的函数getUserBalance(获取用户余额),再进行对余额进行累加。

在循环体中,建议最多使用一次continue。如果需要有使用多次continue的需求,建议把循环体封装为一个函数。

案例3: 利用条件表达式函数减少层级

请参考下一章的"案例2: 把复杂条件表达式封装为函数"

主要收益

  • 代码层级减少,代码缩进减少;
  • 模块划分清晰,方便阅读维护。

8.封装条件表达式函数

案例1: 把简单条件表达式封装为函数

现象描述:

建议方案:

案例2: 把复杂条件表达式封装为函数

现象描述:

建议方案:

以上代码也可以用采用流式(Stream)编程的过滤来实现。

主要收益

  • 把条件表达式从业务函数中独立,使业务逻辑更清晰;
  • 封装的条件表达式为独立函数,可以在代码中重复使用。

9.尽量避免不必要的空指针判断

本章只适用于项目内部代码,并且是自己了解的代码,才能够尽量避免不必要的空指针判断。对于第三方中间件和系统接口,必须做好空指针判断,以保证代码的健壮性。

案例1: 调用函数保证参数不为空,被调用函数尽量避免不必要的空指针判断

现象描述:

建议方案:

案例2: 被调用函数保证返回不为空,调用函数尽量避免不必要的空指针判断

现象描述:

建议方案:

案例3: 赋值逻辑保证列表数据项不为空,处理逻辑尽量避免不必要的空指针判断

现象描述:

建议方案:

案例4: MyBatis查询函数返回列表和数据项不为空,可以不用空指针判断

MyBatis是一款优秀的持久层框架,是在项目中使用的最广泛的数据库中间件之一。通过对MyBatis源码进行分析,查询函数返回的列表和数据项都不为空,在代码中可以不用进行空指针判断。

现象描述:

这种写法没有问题,只是过于保守了。

建议方案:

主要收益

  • 避免不必要的空指针判断,精简业务代码处理逻辑,提高业务代码运行效率;
  • 这些不必要的空指针判断,基本属于永远不执行的Death代码,删除有助于代码维护。

10.内部函数尽量使用基础类型

案例1: 内部函数参数尽量使用基础类型

现象描述:

建议方案:

案例2: 内部函数返回值尽量使用基础类型

现象描述:

建议方案:

此处只是举例说明这种现象,更好的方式是采用流式(Stream)编程。

主要收益

  • 内部函数尽量使用基础类型,避免了隐式封装类型的打包和拆包;
  • 内部函数参数使用基础类型,用语法上避免了内部函数的参数空指针判断;
  • 内部函数返回值使用基础类型,用语法上避免了调用函数的返回值空指针判断。

11.尽量避免返回的数组和列表为null

案例1: 尽量避免返回的数组为null,引起不必要的空指针判断

现象描述:

建议方案:

案例2: 尽量避免返回的列表为null,引起不必要的空指针判断

现象描述:

建议方案:

主要收益

  • 保证返回的数组和列表不为null, 避免调用函数的空指针判断。

12.封装函数传入参数

案例1: 当传入参数过多时,应封装为参数类

Java规范不允许函数参数太多,不便于维护也不便于扩展。

现象描述:

建议方案:

案例2: 当传入成组参数时,应封装为参数类

既然参数成组出现,就需要封装一个类去描述这种现象。

现象描述:

建议方案:

主要收益

  • 封装过多函数参数为类,使函数更便于扩展和维护;
  • 封装成组函数参数为类,使业务概念更明确更清晰。

13.尽量用函数替换匿名内部类的实现

Java匿名内部类的优缺点:

在匿名内部类(包括Lambda表达式)中可以直接访问外部类的成员,包括类的成员变量、函数的内部变量。正因为可以随意访问外部变量,所以会导致代码边界不清晰。

首先推荐用Lambda表达式简化匿名内部类,其次推荐用函数替换复杂的Lambda表达式的实现。

案例1: 尽量用函数替换匿名内部类(包括Lambda表达式)的实现

现象描述:

建议方案:

其实,还有一个更简单的办法。在调用函数sendWorkerSettleData(发送作业员结算数据)之前计算开始日期、结束日期,就直接可以用函数auditTaskDAO.statCheckerSettleData(beginDate, endDate)代替匿名内部类实现。

案例2: 拆分复杂匿名内部类实现接口为多个函数式接口

如果一个匿名内部类实现的接口几个函数间关联性不大,可以把这个接口拆分为几个函数式接口,便于使用Lambda表达式。

现象描述:

建议方案:

主要收益

  • 定义函数并指定参数,明确规定了匿名内部类的代码边界;
  • 利用Lambda表达式简化匿名内部类实现,使代码更简洁。

14.利用return精简不必要的代码

案例1: 删除不必要的if

现象描述:

建议方案:

案例2: 删除不必要的else

现象描述:

建议方案:

案例3: 删除不必要的变量

现象描述:

建议方案:

主要收益

  • 精简不必要的代码,让代码看起来更清爽。

15.利用临时变量优化代码

在一些代码中,经常会看到a.getB().getC()...getN()的写法,姑且叫做“函数的级联调用”,代码健壮性和可读性太差。建议:杜绝函数的级联调用,利用临时变量进行拆分,并做好对象空指针检查。

案例1: 利用临时变量厘清逻辑

现象描述:

这是精简代码控的最爱,但是可读性实在太差。

建议方案:

这个方案,增加了代码行数,但是逻辑更清晰。

有时候,当代码的精简性和可读性发生冲突时,个人更偏向于保留代码的可读性。

案例2: 利用临时变量精简代码

现象描述:

这么写,大概是为了节约一个临时变量吧。

建议方案:

主要收益

  • 利用临时变量厘清逻辑,显得业务逻辑更清晰;
  • 利用临时变量精简代码,看变量名称即知其义,减少了大量无用代码;
  • 如果获取函数比较复杂耗时,利用临时变量可以提高运行效率;
  • 利用临时变量避免函数的级联调用,可有效预防空指针异常。

16.仅保留函数需要的参数

案例1: 删除多余的参数

现象描述:

其中,unused参数是无用参数。

建议方案:

案例2: 用属性取代对象

现象描述:

建议方案:

建议方案:

调用函数时,参数对象不需要专门构建,而函数使用其属性超过3个,可以不必使用该规则。

主要收益

  • 仅保留函数需要的参数,明确了调用时需要赋值的参数,避免了调用时还要去构造些无用参数。

17.仅在需要时才定义变量

案例1: 仅在需要时才定义变量

一些具有VB、C++、PLSQL编程经验的人,习惯于把函数所需的变量定义在函数体最前面。

现象描述:

建议方案:

主要收益

  • 变量在使用时定义,更容易让人理解变量的含义;
  • 如果函数存在提前返回语句,避免了一些不必要的变量定义和初始化。

18.后记

"众人拾柴火焰高"。如果有更多更好的观点,亦或有更好的代码案例,欢迎大家进行补充说明。

笔者希望以此文抛砖引玉,如果最终形成一套完善的Java编码规范,善莫大焉。

本文作者:陈昌毅,花名常意,高德地图技术专家,2018年加入阿里巴巴,一直从事地图数据采集的相关工作。

原文链接

本文为云栖社区原创内容,未经允许不得转载。

Java 函数优雅之道的更多相关文章

  1. 编码规范 | Java函数优雅之道(上)

    导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...

  2. 编码规范 | Java函数优雅之道(下)

    上文背景 本文总结了一套与Java函数相关的编码规则,旨在给广大Java程序员一些编码建议,有助于大家编写出更优雅.更高质.更高效的代码. 内部函数参数尽量使用基础类型 案例一:内部函数参数尽量使用基 ...

  3. Java函数优雅之道

    https://www.cnblogs.com/amap_tech/p/11320171.html 导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化 ...

  4. Java并发编程75道面试题及答案

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  5. 怎样用Java 8优雅的开发业务

    怎样用Java 8优雅的开发业务 目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类CompletableFuture 单机批处理多线程执行模型 ...

  6. java函数参数默认值

    java函数参数默认值 今天,需要设定java函数参数的默认值,发现按照其它语言中的方法行不通 java中似乎只能通过函数的重载来实现 函数参数默认代码

  7. JS调用Java函数--DWR框架

    (1)dwr与ssh框架整合教程dwr框架介绍. DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开发人员开发包含AJA ...

  8. Java函数参数传递方式详解

    在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简 ...

  9. JAVA函数的参数传递

    JAVA开发过程中写函数都是不可避免的于是乎参数传递经常会困扰我们,特别是有C/C++功底的童鞋总会纠结于"java到底是值传递还是引用传递?" 先来一段代码(和程序员交流最好的果 ...

随机推荐

  1. 面试系列 31 zk都有哪些使用场景

    大致来说,zk的使用场景如下,我就举几个简单的,大家能说几个就好了: (1)分布式协调:这个其实是zk很经典的一个用法,简单来说,就好比,你A系统发送个请求到mq,然后B消息消费之后处理了.那A系统如 ...

  2. CSS清除默认边距

    body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquo ...

  3. printk函数速率限制

    如果你不小心, 你会发现自己用 printk 产生了上千条消息, 压倒了控制台并且, 可能地, 使系统日志文件溢出. 当使用一个慢速控制台设备(例如, 一个串口), 过量的消息速率也 能拖慢系统或者只 ...

  4. memcache 使用手册

    Memcached 教程 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统. Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fit ...

  5. linux yum 安装 卸载

    安装一个软件时 yum -y install httpd 安装多个相类似的软件时 yum -y install httpd* 安装多个非类似软件时 yum -y install httpd php p ...

  6. 多线程MT和多线程MD的区别

    这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...

  7. MySQL系列(十二)--如何设计一个关系型数据库(基本思路)

    设计一个关系型数据库,也就是设计RDBMS(Relational Database Management System),这个问题考验的是对RDBMS各个模块的划分, 以及对数据库结构的了解.只要讲述 ...

  8. 自己编写jquery插件

    http://blog.csdn.net/chenxi1025/article/details/52222327 https://www.cnblogs.com/ajianbeyourself/p/5 ...

  9. windows环境下,svn未备份情况下重新恢复

    公司有个同事在未打招呼的情况下把公司服务器进行重新装系统,崩溃啊.SVN之前未备份,还好SVN的库(Repositories)还在,如下图: 恢复办法如下: 由于之前安装的就是VisualSVN-Se ...

  10. java.sql.SQLException

    java.sql.SQLException 出错:java.sql.SQLException: com.mchange.v2.c3p0.ComboPooledDataSource[ identityT ...