码农如何提高自己的品味

京东云开发者
• 阅读 534

作者:京东科技 文涛

前言

软件研发工程师俗称程序员经常对业界外的人自谦作码农,一来给自己不菲的收入找个不错的说辞(像农民伯伯那样辛勤耕耘挣来的血汗钱),二来也是自嘲这个行业确实辛苦,辛苦得没时间捯饬,甚至没有驼背、脱发加持都说不过去。不过时间久了,行外人还真就相信了程序员就是一帮没品味,木讷的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
@AllArgsConstructor
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;
}

日志打印避免只打整个参数

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

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

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

点赞
收藏
评论区
推荐文章
codedecent codedecent
3年前
HelloWorld
Finallyfindthisplace.Nicetomeetyouall.关注helloworld社区也有一段时间了,由于是新晋社区资源较少,一直没有怎么真正的去使用,只是单纯觉得界面美观简洁,符合自己的审美,偶尔逛逛而已。今天终于有时间可以可以坐下来说说话,都说程序员的第一行输出是helloworld,在这个社区的第一篇博客我也
P8级别的顶级“并发编程”宝典,面试经历分享
为什么学Java?Java能干什么?每年转行想做程序员的人都不少,这个行业不像销售一样,你不必要懂那么多人情世故;也不像某些“安稳岗位”一样,工资较低。程序员靠技术恰饭,没有那么多职场周旋,工资也很可观,因此就成为了大多数转行人的第一选择。Java作为世界上最主流的编程语言之一,也是应用最广泛的编程语言,属于编程语言中老大哥般的存在,是绝大多数人的第一选择。
Wesley13 Wesley13
3年前
java从程序员走向架构师
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水平的Java程序员们。一、Java编程入门类对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基础语法和
Wesley13 Wesley13
3年前
37岁程序员失业投500份简历就3次面试猎头:超35岁不要
37岁程序员失业120天,投了500份简历就3次面试,猎头:超过35岁都不要在职场中,都说互联网吃青春饭,而35岁就是一个门槛,一旦到了这个年龄找工作都非常难,甚至被公司裁员的风险都很大,其实一个现象也不难发现,互联网公司几乎都是年轻化,高龄员工很少,这也算行业趋势。最近,一位程序员在职业论坛留言,分享了自己面试的经历,“37岁程序员失业120天,投
Wesley13 Wesley13
3年前
Java程序员月薪达到三万,需要技术水平达到什么程度?
最近跟朋友在一起聚会的时候,提了一个问题,说Java程序员如何能月薪达到三万,技术水平需要达到什么程度?人回答说这只能是大企业或者互联网企业工程师才能拿到。也许是的,小公司或者非互联网企业拿二万的不太可能是码农了,应该已经转管理。还有区域问题,这个不在我的考虑范围内,因为除了北上广深杭,其他地方也很难达到。还有人提到这个水平不止3w,其实工资是跟面试表现
Stella981 Stella981
3年前
2021年如何面试大厂?回顾今年字节,美团,华为,蚂蚁等Java岗面试经历!文末直接分享笔记
还有20天就是2021年了,回顾一下2010年自己面试过的大厂,这里我说下我的感受今年跳槽成功的难度比往年高了很多,很明显的感受就是:对于今年的java开发朋友跳槽面试,无论一面还是二面,都开始考验一个Java程序员的技术功底和基础。对源码解读和核心原理理解也是成了加分项,特别是对Java的一些核心基础知识点掌握的不够或者没有体系方向的朋友面试也
Wesley13 Wesley13
3年前
Java程序员的推荐阅读书籍
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水平的Java程序员们。  一、Java编程入门类  对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基
可莉 可莉
3年前
2021年如何面试大厂?回顾今年字节,美团,华为,蚂蚁等Java岗面试经历!文末直接分享笔记
还有20天就是2021年了,回顾一下2010年自己面试过的大厂,这里我说下我的感受今年跳槽成功的难度比往年高了很多,很明显的感受就是:对于今年的java开发朋友跳槽面试,无论一面还是二面,都开始考验一个Java程序员的技术功底和基础。对源码解读和核心原理理解也是成了加分项,特别是对Java的一些核心基础知识点掌握的不够或者没有体系方向的朋友面试也
Easter79 Easter79
3年前
Sql Server之旅——第一站 那些给我们带来福利的系统视图
本来想这个系列写点什么好呢,后来想想大家作为程序员,用的最多的莫过于数据库了,但是事实上很多像我这样工作在一线的码农,对sql都一知半解,别谈优化和对数据库底层的认识了,我也是这样。。。一:那些系统视图1\.系统视图是干什么呢?从名字上看就知道,系统视图嘛?猜的不错的话,就是存放一些sqlserver系统的一些信息,
kenx kenx
1年前
IT入门深似海,入门到放弃你学废了嘛
我一直觉得IT行业程序员行业。甚至觉得程序员人群是一个特殊存在的群体。入门到放弃,是真的,IT门槛高嘛。其实吧,IT编程门槛,是有的,但是对于感兴趣的,想学习IT编程同学来说,也是一件容易事情其实。我突然想讲一下我学编程的第一课,也是最难的。。。。。最近又