随着软件开发技术的不断进步,处理偶发复杂度的行为都有可能被自动化、被机器取代;只有处理本质复杂度的建模行为,无法被机器取代。 —— 熊大
引用上面一段话,是因为它很好地点出了这几年我一直专注的方向,如何让技术工作者专注于无法被机器取代的部分,而不是拼体力去和自动化机器抢生意。
再不努力你就要被机器取代!
为什么会有这种焦虑?并不是源于现在网络上一直吹嘘的人工智能,而是来自自我反省。
最近在研究一些代码质量分析系统的时候,其中有一项就是代码的重复率。意思就是你一直以来提交的代码里,在做多少重复的劳动?
且不论这个系统的科学性和准确性,但它的确给了我一个声音,我从直觉是感到自己平时也写很多重复代码。
好了,感慨完进入正题。首先,怎么找到重复劳动?
我们看一段代码:
import * as mongoose from 'mongoose';
enum Lang { js, java, c }
export interface IUserModel extends mongoose.Document {
name: string;
age: number;
language: Lang;
}
const schema = new mongoose.Schema(
{
name: {
type: String,
require: true,
},
age: {
type: Number,
require: true,
},
language: {
type: Lang,
require: true,
enum: [ Lang.js, Lang.java, Lang.c ],
},
},
{
usePushEach: true,
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
},
);
export default mongoose.model('user', schema);
如何识别上面的重复代码?表面看来,真正在运行中有用的部分,是 new mongoose/Schema(...)
这部分,但我们判断的依据并不在此。
代码编写实际上是体现了我们的主观意图。
抛开其他庞杂的知识体系,在这段代码里,我们的主观意图是定义一个用户类型的模型。再简化一下,就是定义用户类型。
所以我思考上面这几十行代码,哪些是代表我自己的主观意图,哪些是机器没法代替的部分呢?
其实仅仅是用户接口类型的定义:
enum Lang { js, java, c }
export interface IUserModel extends mongoose.Document {
name: string;
age: number;
language: Lang;
}
至于如何使用 mongoose
去定义一个 Schema
,再导出一个Model
实例,这些每次都要我翻看在线文档的操作,我恨不得交给计算机去做。
而接下来我就是要让计算机来完成这个任务。
🤡 这个真的可行吗?
🤖 理论上是可以的,只要你确定某些操作,完全没有注入自己的脑力创作元素,那它一定可以被计算机替代。
如果这真的可以做到,那么理想情况下,我们需要自己编写的代码,只剩下这些。
import * as mongoose from 'mongoose';
enum Lang { js, java, c }
export interface IUserModel extends mongoose.Document {
name: string;
age: number;
language: Lang;
}
很好,只定义了一个用户的接口类型,符合我们的主观意图。
然后使用程序分析这段代码:
// 因为讲解需要,对原始 AST 输出做了简化,突出重点
[
{
type: 'ImportDeclaration',
source: {
value: 'mongoose'
},
},
{
type: 'TSEnumDeclaration',
id: { type: 'Identifier', name: 'Lang' },
members: [ 'js', 'java', 'c' ]
},
{
type: 'ExportNamedDeclaration',
declaration: {
type: 'TSInterfaceDeclaration',
body: [{...},{...},{...}],
id: { name: 'IUserModel' },
extends: ['Document']
},
}
]
可见,我们只写了三行代码:
- 引入一个依赖 mongoose
- 定义了 Lang 枚举类型
- 导出了 用户接口,继承自 Document
从以上信息里,我需要机器人捕捉到的关键是第三行代码🔍。
我们可以根据AST
结构的特征,让机器人只处理 TSInterfaceDeclaration
并且 extends
自 Document
的,这个逻辑简单,自然是不需要贴代码的,大家都能理解。
然后我们需要让机器人明白我们的 TSInterfaceDeclaration
语句,定义了一个怎样的(包含哪些字段类型)的接口类型。
我们再展开一下上面的 body: [{...},{...},{...}],
// 因为讲解需要,对原始 AST 输出做了简化,突出重点
[
{
key: { name: 'name' },
typeAnnotation: {
type: 'TSStringKeyword',
}
},
{
key: { name: 'age' },
typeAnnotation: {
type: 'TSNumberKeyword',
}
},
{
key: { name: 'language' },
typeAnnotation: {
type: 'TSTypeReference',
typeName: { name: 'Lang' }
}
}
]
这部分如果比较晦涩,可以对照着 IUserModel
的定义,就很好读懂了。
主要就是表达了IUserModel
里三个字段的类型定义,已经能清楚看出name
是一个字符串类型,age
是一个数字类型,而language
则是一个Lang
的引用类型。
分析完毕,重复工作一次编写
代码分析环节到这里几乎就到了柳暗花明枯木逢春的境地了。
我们的主观意图很好地传递给了机器人,然后,我们让机器人根据 mongoose文档 的要求,书写后续的 Schema
和Model
。
因为 TS
的类型定义,比如TSStringKeyword
, 也就是我们平时书写的 :string
,总能找到一个对应的mongoose SchemaType
,比如 mongoose.Schema.Types.String
。
我们平时就是花了比较多的时间在写这种重复代码。
最后,因为本文并不是工具开发介绍,就不贴具体实现了。如果有人感兴趣,后续我视情况再写一篇《实践》。
送一个动图,证明我没说谎。