2.1 优雅防重
代码如下:
public int createContent(ContentOverviewEntity contentEntity) {
try{
return contentOverviewRepository.createContent(contentEntity);
}catch (DuplicateKeyException dke){
log.warn("repeat content:{}",contentEntity.toString());
}
return 0;
}
2.2 用好Stream
初级程序员向中级进阶的必经之路就是攻克Stream。Stream和面向对象编程是两个编程理念,《架构整洁之道》里曾提到有三种编程范式,结构化编程(面向过程编程)、面向对象编程、函数式编程。初次接触Stream肯定特别不适应,但如果熟悉以后将打开一个编程方式的新思路。本文Stream,只讲如下例子:
比如,如果想把一个二维表数据进行分组,可采用以下一行代码实现:
List<ActionAggregation> actAggs = ....
Map<String, List<ActionAggregation>> collect =
actAggs.stream()
.collect(Collectors.groupingBy(ActionAggregation :: containWoNosStr,LinkedHashMap::new,Collectors.toList()));
2.3 用好卫语
各个大场的JAVA编程规范里基本都有这条建议,但真正用好它的不多,卫语句对提升代码的可维护性有着很大的作用,想像一下,在一个10层if 缩进的接口里找代码逻辑是一件多么痛苦的事情。笔者曾在一个微服务里的一个核心接口看到了这种代码,该接口被过多的人接手导致了这样的局面。系统接手人过多以后,代码腐化的速度超出想像。
if (title.equals(newTitle)){
if (...) {
if (...) {
if (...) {
}
}else{
}
}else{
}
}
使用了卫语句的代码,缩进很少:
if (!title.equals(newTitle)) {
return xxx;
}
if (...) {
return xxx;
}else{
return yyy;
}
if (...) {
return zzz;
}
2.4 避免双重循环
List<WorkOrderChain> allPre = ...
List<WorkOrderChain> chains = ...
Map<String, WorkOrderChain> preMap = allPre.stream().collect(Collectors.toMap(WorkOrderChain::getWoNext, item -> item,(v1, v2)->v1));
chains.forEach(item->{
WorkOrderChain preWo = preMap.get(item.getWoNo());
if (preWo!=null){
item.setIsHead(1);
}else{
item.setIsHead(0);
}
});
2.5 用@see @link来设计RPC的API
程序员们还经常自嘲的几个词有:API工程师,中间件装配工等,既然平时写API写的比较多,那种就把它写到极致@see @link的作用是让使用方可以方便的链接到枚举类型的对象上,方便阅读。
示例如下:
public class ContentProcessDto implements Serializable {
/**
* 内容ID
*/
private String contentId;
/**
* @see com.jd.jr.community.common.enums.ContentTypeEnum
*/
private Integer contentType;
/**
* @see com.jd.jr.community.common.enums.ContentQualityGradeEnum
*/
private Integer qualityGrade;
}
2.6 日志打印避免只打整个参数
研发经常为了省事,直接将入参这样打印:
log.info("operateRelationParam:{}", JSONObject.toJSONString(request));
该日志进了日志系统后,研发在搜索日志的时候,很难根据业务主键排查问题
如果改进成以下方式,便可方便的进行日志搜索。
log.info("operateRelationParam,id:{},req:{}", request.getId(),JSONObject.toJSONString(request));
如上:只需要全词匹配“operateRelationParam,id:111”,即可找到业务主键111的业务日志。
2.7用异常捕捉替代方法参数传递
public RpcResult<String> deleteContent(ContentOptDto contentOptDto) {
log.info("deleteContentParam:{}", contentOptDto.toString());
try{
RpcResult<?> paramCheckRet = this.paramCheck(contentOptDto);
if (paramCheckRet.isSgmFail()){
return RpcResult.getSgmFail("非法参数:"+paramCheckRet.getMsg());
}
ContentOverviewEntity contentEntity = DozerMapperUtil.map(contentOptDto,ContentOverviewEntity.class);
RpcResult<?> delRet = contentEventHandleAbility.deleteContent(contentEntity);
if (delRet.isSgmFail()){
return RpcResult.getSgmFail("业务处理异常:"+delRet.getMsg());
}
}catch (Exception e){
log.error("deleteContent exception:",e);
return RpcResult.getSgmFail("内部处理错误");
}
return RpcResult.getSgmSuccess();
}
可以通过自定义异常的方式解决:子方法抛出不同的异常,调用方catch不同异常以便进行不同逻辑的处理,这样调用方特别清爽,不必做返回结果判断。
代码示例如下:
public RpcResult<String> deleteContent(ContentOptDto contentOptDto) {
log.info("deleteContentParam:{}", contentOptDto.toString());
try{
this.paramCheck(contentOptDto);
ContentOverviewEntity contentEntity = DozerMapperUtil.map(contentOptDto,ContentOverviewEntity.class);
contentEventHandleAbility.deleteContent(contentEntity);
}catch(IllegalStateException pe){
log.error("deleteContentParam error:"+pe.getMessage(),pe);
return RpcResult.getSgmFail("非法参数:"+pe.getMessage());
}catch(BusinessException be){
log.error("deleteContentBusiness error:"+be.getMessage(),be);
return RpcResult.getSgmFail("业务处理异常:"+be.getMessage());
}catch (Exception e){
log.error("deleteContent exception:",e);
return RpcResult.getSgmFail("内部处理错误");
}
return RpcResult.getSgmSuccess();
}
2.8 自定义Spring Boot 的 Banner
图1 ASCII艺术字效果图
2.9 多用JAVA语法糖
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
2.10 利用链式编程
链式编程,也叫级联式编程,调用对象的函数时返回一个this对象指向对象本身,达到链式效果,可以级联调用。链式编程的优点是:编程性强、可读性强、代码简洁。
/**
链式map
*/
public class ChainMap<K,V> {
private Map<K,V> innerMap = new HashMap<>();
public V get(K key) {
return innerMap.get(key);
}
public ChainMap<K,V> chainPut(K key, V value) {
innerMap.put(key, value);
return this;
}
public static void main(String[] args) {
ChainMap<String,Object> chainMap = new ChainMap<>();
chainMap.chainPut("a","1")
.chainPut("b","2")
.chainPut("c","3");
}
}
2.11 优雅暂停线程
Thread.sleep(long timeout)暂停线程时必须捕获或向上层抛出InterruptedException,同时如果被中断,则满足不了预期的timeout时间,Guava包中有个方法:com. google .common. util. concurrent. Uninterruptibles #sleep Uninterruptibly,可以优雅的让线程暂停,不仅不用关心异常,同时如果被中断,还会补偿,直到time out时间耗尽。
2.12 适时使用元组(tuple)对象
接口返回值往往是成对的,或者是成三的,此时有几种做法:
1、通过map格式返回,
2、自定义新对象将返回的元素包装起来,此两种做法都能解决问题,
org.apache.commons.lang3.tuple.Pair
org.apache.commons.lang3.tuple.Triple
本文立足于编码规范之上,从研发角度探讨如何优雅编程,罗列一些策略,如卫语句使用、注解设计API、异常捕获特殊用法、链式编程等,基于这些策略可以使代码更加优雅易维护。
求分享
求点赞
求在看