作者:京东科技 文涛

前言

软件研发工程师俗称程序员常常对业界外的人自谦作码农,一来给本人不菲的支出找个不错的说辞(像农民伯伯那样辛勤耕耘挣来的血汗钱),二来也是自嘲这个行业的确辛苦,辛苦得没工夫捯饬,甚至没有驼背、脱发加持都说不过去。不过工夫久了,行外人还真就置信了程序员就是一帮没品尝,木讷的low货,大部分的文艺作品中也都是这么体现程序员的。可是我明天要说一下我的感触,编程是个艺术活,程序员是最聪慧的一群人,咱们的品尝也能够像艺术家一样。

言归正转,你是不是认为我明天要教你穿搭?不不不,这仍然是一篇技术文章,想学穿搭女士学陈舒婷(《狂飙》中的大嫂),男士找陈舒婷那样的女朋友就好了。笔者明天教你怎么有“品尝”的写代码。

以下几点可晋升“品尝”

阐明:以下是笔者的经验之谈具备局部主观性,不同意的欢送拍砖,要想体系化晋升编码功底倡议读《XX公司Java编码标准》、《Effective Java》、《代码整洁之道》。以下几点局部具备通用性,局部仅限于java语言,其它语言的同学绕过即可。

优雅防重

对于成体系的防重解说,笔者之后打算写一篇文章介绍,明天只讲一种优雅的形式:

如果你的业务场景满足以下两个条件:

1 业务接口反复调用的概率不是很高

2 入参有明确业务主键如:订单ID,商品ID,文章ID,运单ID等

在这种场景下,非常适合乐观防重,思路就是代码解决不被动做防重,只在监测到反复提交后做相应解决。

如何监测到反复提交呢?MySQL惟一索引 + org.springframework.dao.DuplicateKeyException

代码如下:

public int createContent(ContentOverviewEntity contentEntity) {    try{        return contentOverviewRepository.createContent(contentEntity);    }catch (DuplicateKeyException dke){        log.warn("repeat content:{}",contentEntity.toString());    }    return 0;}

用好lambda表达式

lambda表达式曾经是一个陈词滥调的话题了,笔者认为,高级程序员向中级进阶的必经之路就是攻克lambda表达式,lambda表达式和面向对象编程是两个编程理念,《架构整洁之道》里曾提到有三种编程范式,结构化编程(面向过程编程)、面向对象编程、函数式编程。首次接触lambda表达式必定特地不适应,但如果相熟当前你将关上一个编程形式的新思路。本文不讲lambda,只讲如下例子:

比方你想把一个二维表数据进行分组,可采纳以下一行代码实现

List<ActionAggregation> actAggs = ....Map<String, List<ActionAggregation>> collect =     actAggs.stream()    .collect(Collectors.groupingBy(ActionAggregation :: containWoNosStr,LinkedHashMap::new,Collectors.toList()));

用好卫语句

各个大场的JAVA编程标准里根本都有这条倡议,但我见过的代码里,把它用好的不多,卫语句对晋升代码的可维护性有着很大的作用,想像一下,在一个10层if 缩进的接口里找代码逻辑是一件如许苦楚的事件,有人说,哪有10层的缩进啊,别说,笔者还真的在一个微服务里的一个外围接口看到了这种代码,该接口被过多的人接手导致了这样的场面。零碎接手人过多当前,代码腐化的速度超出你的想像。

上面举例说明:

没有用卫语句的代码,很多层缩进

if (title.equals(newTitle)){    if (...) {        if (...) {            if (...) {            }        }else{                    }    }else{            }}

应用了卫语句的代码,缩进很少

if (!title.equals(newTitle)) {    return xxx;}if (...) {    return xxx;}else{    return yyy;}if (...) {    return zzz;}

防止双重循环

简略说双重循环会将代码逻辑的工夫复杂度扩充至O(n^2)

如果有按key匹配两个列表的场景倡议应用以下形式:

1 将列表1 进行map化

2 循环列表2,从map中获取值

代码示例如下:

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);    }});

用@see @link来设计RPC的API

程序员们还常常自嘲的几个词有:API工程师,中间件装配工等,既然咱平时写API写的比拟多,那种就把它写到极致@see @link的作用是让应用方能够不便的链接到枚举类型的对象上,不便浏览

示例如下:

@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic 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;}

日志打印防止只打整个参数

研发常常为了省事,间接将入参这样打印

log.info("operateRelationParam:{}", JSONObject.toJSONString(request));

该日志进了日志零碎后,研发在搜寻日志的时候,很难依据业务主键排查问题

如果改良成以下形式,便可不便的进行日志搜寻

log.info("operateRelationParam,id:{},req:{}", request.getId(),JSONObject.toJSONString(request));

如上:只须要全词匹配“operateRelationParam,id:111”,即可找到业务主键111的业务日志。

用异样捕捉代替办法参数传递

咱们常常面对的一种状况是:从子办法中获取返回的值来标识程序接下来的走向,这种形式笔者认为不够优雅。

举例:以下代码paramCheck和deleteContent办法,返回了这两个办法的执行后果,调用方通过返回后果判断程序走向

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();}

自定义SpringBoot的Banner

别再让你的Spring Boot启动banner千篇一律,spring 反对自定义banner,该技能对业务性能实现没任何卵用,但会给干燥的编程生存增加一点乐趣。

以下是官网文档的阐明: https://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/htmlsingle/#boot-features-banner

另外你还须要ASCII艺术字生成工具: https://tools.kalvinbg.cn/txt/ascii

成果如下:

   _ _                   _                     _                 _         (_|_)_ __   __ _    __| | ___  _ __   __ _  | |__   ___   ___ | |_ ___   | | | '_ \ / _` |  / _` |/ _ \| '_ \ / _` | | '_ \ / _ \ / _ \| __/ __|  | | | | | | (_| | | (_| | (_) | | | | (_| | | |_) | (_) | (_) | |_\__ \ _/ |_|_| |_|\__, |  \__,_|\___/|_| |_|\__, | |_.__/ \___/ \___/ \__|___/|__/         |___/                     |___/                             

多用Java语法糖

编程语言中java的语法是绝对繁琐的,用过golang的或scala的人感觉特地显著。java提供了10多种语法糖,写代码常应用语法糖,给人一种 “这哥们java用得通透” 的感觉。

举例:try-with-resource语法,当一个内部资源的句柄对象实现了AutoCloseable接口,JDK7中便能够利用try-with-resource语法更优雅的敞开资源,打消板式代码。

try (FileInputStream inputStream = new FileInputStream(new File("test"))) {    System.out.println(inputStream.read());} catch (IOException e) {    throw new RuntimeException(e.getMessage(), e);}

利用链式编程

链式编程,也叫级联式编程,调用对象的函数时返回一个this对象指向对象自身,达到链式成果,能够级联调用。链式编程的长处是:编程性强、可读性强、代码简洁。

举例:如果感觉官网提供的容器不够不便,能够自定义,代码如下,但更倡议应用开源的通过验证的类库如guava包中的工具类

/**    链式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");    }}

未完,待续,欢送评论区补充