“勿以技术论学识。” ——进入职场第一个敬佩的大佬给我的忠告
让你写几行代码实现一个小功能,同样几行的代码,在一部分人眼里是 “CURD”,在另外一些人脑子对问题的各种场景,分析抽象。这两种类型的人,区别在哪里?难道说第二批人更懂得使用一些”高大上”的概念来包装自己吗?更多时候只不过是我们一厢情愿罢了。
第二批人脑子里想的东西跟第一批是不同的,而我们就是要去学习这些人是怎么想的,以及它为什么回这么想,也就是所谓的思维方式,思考方式。
找高手切磋。下过棋,打个球的人都知道,你要想提升自己的技艺,你必需找高手切磋,在和高手切磋的过程中你会感受到高手的技能和方法,有时候你会情不自禁地哇地一下,我靠,还可以这么玩!
我把这几点写下来的原因,是我意识到,个体可能根据不同原因,例如背景、接触技术不久、小白、被媒体影响等因素,导致对技术有不同看法和观点。这些观点可能正确、也可能不对,我们需要接触更多对技术有热情、真正厉害的人,这些人可能就在你的身边,也可能在开源社区里,我们可以有意识地去关注他们,看看他们是如何看待、分析问题的,关注点放在哪里,学习他们的思维方式,并且思考他们为什么会怎么看待问题。
这样一来对自己水平的提升才快,不仅仅只是在技术上的眼界,思维方式在很多场景下是触类旁通的。而不是看一大堆书、视频,没有归纳总结,这样只是在原地踏步。
老祖宗说过:”兼听则明,偏听则暗”
正确地看待框架
我在大二的时候开始接触各种的框架,接触到的框架都是 Java 生态圈的,例如 Spring, Hibernate, Struts 等。有些现在已经不入流,各路开发对其嗤之以鼻。对于这些框架的学习无非就是记住许多的”步骤”,按部就班,例如 SSH 框架怎么搭建、怎么往 Spring 容器里整个 Bean。
结果导致大多数人就开始觉得学技术就是学各种个样的框架,越多越好,根据一些教程,按部就班地做,如果整套步骤你能了然于胸,那你总有一天就会成为技术大牛。
更可笑的是使用百度搜索关键词,诸如”如何配置 Spring 声明式事务”,各种网友纷纷给出自己的”独门秘方”,放着框架的文档在哪不去查。我想可能的原因就是大部分开发在刚开始学习技术的时候,英文水平都不咋滴,我也是其中一个。
疫情发生前,团队都居家办公了一段时间,那时候的第一个星期老杨安排我们研究 Spring 的文档,然后对看的内容进行总结。
一次只做一件事情
解析数据源配置的任务:根据数据接入时为数据源设置的配置项解析它的主键列集合(primaryKeyColumns)和 槽位(buckets),具体要求如下
- 数据源没有设置配置项时,主键列集合为空数组、槽位为 -1
- 数据源有设置配置项时
- 主键列集合从配置项中直接获取
- 槽位为 tableProps.buckets 的值。如果没有这个属性,那么就按主键列集合来解析,主键列集合为空时,buckets 为 4,否则就是 16
先来看看我写的代码,我当时的思路是按条件的优先级进行字段值的覆盖
public Map<String, Object> queryPartitionBuckets(String domId, String dsId) {
Map result = new HashMap();
result.put("primaryKeyColumns", Collections.emptyList());
Record r = DataSource.dao().getConfigByNodeId(domId, dsId);
if (r == null) {
throw new BIException("cannot find datasource");
}
Map config = JsonUtils.parse(r.getStr("config"), Map.class);
if (config == null) {
result.put("buckets", -1);
return result;
}
// buckets 不存在
List primaryKeyColumns = (List) config.get("primaryKeyColumns");
if (CollectionUtils.isEmpty(primaryKeyColumns)) {
result.put("buckets", 4);
}
else {
result.put("buckets", 16);
result.put("primaryKeyColumns", primaryKeyColumns);
}
Map tableProps = (Map) config.get("tableProps");
if (tableProps != null) {
Integer buckets = (Integer) tableProps.get("buckets");
if (buckets != null) {
result.put("buckets", buckets);
}
}
return result;
}
然后再来看看老杨写的代码
public Map<String, Object> queryPartitionBuckets(String domId, String dsId) {
Map result = new HashMap();
DataSource ds = DataSource.dao().queryDataSourceById(domId, dsId);
Map config = ds.getMap("config");
if (config == null) {
result.put("primaryKeyColumns", Collections.emptyList());
result.put("buckets", -1);
}
else {
List primaryKeyColumns = (List) config.get("primaryKeyColumns");
result.put("primaryKeyColumns", primaryKeyColumns);
Map tableProps = (Map) config.get("tableProps");
if (tableProps != null) {
Integer buckets = (Integer) tableProps.get("buckets");
if (buckets != null) {
result.put("buckets", buckets);
}
}
if (!result.containsKey("buckets")) {
if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
result.put("buckets", 4);
}
else {
result.put("buckets", 16);
}
}
}
return result;
}
不要重复造”轮子”
这里的轮子指的是已经实现的方法,而不是某类型的框架或库。添加功能时,先找找看功能是否已经实现了,不要再自己写一块,一定会影响后期的维护工作。
我写的代码
public Map<String, Object> queryPartitionBuckets(String domId, String dsId) {
...
Record r = DataSource.dao().getConfigByNodeId(domId, dsId);
if (r == null) {
throw new BIException("cannot find datasource");
}
Map config = JsonUtils.parse(r.getStr("config"), Map.class);
...
}
老杨写的代码
public Map<String, Object> queryPartitionBuckets(String domId, String dsId) {
...
DataSource ds = DataSource.dao().queryDataSourceById(domId, dsId);
Map config = ds.getMap("config");
...
}
还是前面那个解析数据源配置的任务,代码库中获取数据源这个操作是非常频繁的,因此直接使用 queryDataSourceById
这个方法就可以行了,这个方法被大量引用。因此我们实现更能时,对于一些比较通用的方法,首先要先去看看是否有现成的实现,而不是直接埋头开干。这样写出来的代码是 BUG 滋生的温床,可能会为后续修改埋坑。例如我这边要求系统中所以数据源在使用前都应该去检查数据源是否已失效,那么如果代码中有很多一样的实现,那么只要我们改动的时候漏掉了一个,那么 BUG 就来了。
培养总结和分享的习惯
分享会、写代码时产生的一些想法,我又不希望这些想法经过被时间的洗礼,而被我遗忘掉,所以我会将其记录到 iPhone 的备忘录中(后面我找到更好的方式,其实就是 M)。一般来说,我会将想法是断断续续地记录下来,然后当某个主题的片段到达一定层度的内容后,我会对它们进行归纳总结,然后组织成一篇文章,完成后放在 GitHub 的私有仓库中。
我把写的东西都丢在 GitHub 私有仓库的原因是害怕写的东西被别人看见。但是后面我想了想,觉得这是非常错误的想法,如果我写的内容有问题,那么其它人会帮我指出,我后面就会避免这个问题,这是其一。再者,这样能激励我写出多文章,对我的归纳总结和写作技术会是一个很大的帮助。
打磨阅读源代码的方式
第一次参加分享会的时候,我的分享内容是要向组内的其它小伙伴们分享业务系统如何通过作业调度引擎,来与后端的大数据系统进行交互的。这是系统的其中一个核心模块,由组内的高手和老杨写的,除了主干流程,异常情况的处理占据大多数,故源代码对我们这些应届生是个不小的挑战。
抽象:
接口抽象定义。 任何代码都会有很多接口或抽象定义,其描述了代码需要处理的数据结构或者业务实体,以及它们之间的关系,理清楚这些关系是非常重要的。
模块粘合层。 代码有很多都是用来粘合代码的,比如中间件、Promise 模式、Callback、代理委托、依赖注入等。理解这些代码模块间的粘合技术是非常重要的,因为他们回把本来平铺直叙的代码给分裂开来,让你不容易看明白它们的关系。
业务流程。 这是代码运行的过程。一开始,我们不要进入细节,但需要在高层清楚整个业务的流程是什么样的,在这个流程中,数据是怎么被传递和处理的。一般来说,我们需要画程序流程图或者时序处理图。
细节:
代码逻辑。 代码有两种逻辑,一种是业务逻辑,就是真正的业务处理;另外一种是控制逻辑,用来控制程序流转,例如:序列化反序列化的代码、远程调用的代码、异步控制的代码、多线程处理的代码、标志位(flag)的控制变量等。很多代码之所以混乱就是把这两种逻辑写在一起了,因此我们在写代码的时候,需要使用一些技术来对其进行”解耦”。
出错处理。 根据二八原则,20 的代码是正常的逻辑,80 的代码是在处理各种错误,所以我们在读代码的时候,可以把注意力放在干净和简单的正常逻辑的代码。排除干扰因素,才能更高效地读代码。
数据处理。 很多的代码就是在那里倒腾数据。例如 DAO、DTO,比如 JSON、XML,这些代码冗长无聊,不是主要逻辑,可以不理。
阅读代码的方法:
- 归类代码逻辑,业务逻辑、控制逻辑、错误处理等。排除杂音,主要逻辑才会更清楚。
- 画图。程序流程图,调用时序图,模块组织图等。
- 从整体到细节的读法
- debug 跟踪
写在最后
时间是我们非常宝贵的财富,我们每一年都会选择将时间投资在不同的东西上以期望获得相应的回报,无论是投入学习获得更多的知识和经验,还是投入娱乐获得得到放松和良好的心态,这都是我们自己做出的选择和规划。
正如每一年,2020 年对我来说也是很重要的一年,这一年最大的变化有两点首先是更加关注软件工程领域中的理论知识、其次是意识形态的变化。希望未来能够逐渐扩大自己的外延,更好地了解这个世界并认识世界运行的一些底层逻辑。
以上