共计 4896 个字符,预计需要花费 13 分钟才能阅读完成。
此文首发于知乎
Talk is cheap, show me the code.
手里有码,心中不慌。源码敬上 ihongs/HongsCORE
按照一篇技术文章的惯例,先得定义名词、作出解释:
信息管理系统:信息管理系统_百度百科,往大了说,除了图书管理、仓储管理,电商、资讯网站和大部分的 APP 后台都属于信息管理系统。但此文并不讨论复杂的系统,只浅涉一点点基础的,也不牵扯到 OLTP/OLAP 之类概念,只讨论一个玩笑般的话题:增删改查。
自动化构建:我认为全自动的构建方式就是人向机器提出需求,机器直接给出完整方案,过程中机器可能会询问并等你作出抉择,但无需程序员干预细节;相对的,半自动化仍然需要人来确定枝枝蔓蔓的细节,但具体的数据流转逻辑无需编写代码。
不过,这不是论文,这不是论文,这不是论文!
从上述定义来说,本文谈到的系统只能算 ” 近半自动化 ”,当超脱那些简单但繁琐的基础逻辑后,仍然需要程序员去干预甚至覆盖重建。我试图让这个系统不是一个秤砣啃不动砸不烂,好让其可干预、可拆散、可重建。这不会生成逻辑代码,只会产生结构类代码,如果需要,可以过滤输入输出和覆盖重写逻辑,但不存在“修改”逻辑代码这么个操作,因为没有逻辑程序代码产生——没有这个必要。
数据与逻辑并不是天然分隔的,一个程序代码之所以成为逻辑指令,只有当被执行的时候才表现出来那些动态的判断和分支,但当它存储在硬盘里,不管里面写了多少动词,是文本还是二进制,他跟一篇文章或一个图片没有本质上的区别。所以,编程面对的就是把上游数据进行合理的解释,从一个数据结构转换成另一个数据结构,这个转换过程一般情况下由人来完成——码农就是这个转换的解释器。
说到转换,我就想到我们师徒四人换取通关文牒……抱歉抱歉,走错片场了。
举个例子,现在有个文牒、哦不、文档查询网站,首页提供了一个搜索框,上面有关键词、分类和标签三项,当您输入关键词、选择分类和标签后点击搜索,页面会向服务器重新请求并跳转,URL 从 http://xxx.com/document 变成 http://xxx.com/document?word= 催化 &type= 化工 &tags[]= 高新科技 &tags[]= 前沿技术,到服务器后,尾巴上的参数转换成 {word: “ 催化 ”, type: “ 化工 ”, tags: [“ 高新科技 ”, “ 前沿技术 ”]},再被转换成 SQL 查询语句 SELECT * FROM document WHERE word LIKE ‘% 催化 %’ AND type = ‘ 化工 ’ AND tags IN (‘ 高新科技 ’,’ 前沿技术 ’)。从 URL 的问号后的查询串转换为程序里的结构化数据,这个过程一般由服务容器或应用框架自动完成,这个过程是一对一的映射,基本不存在歧义,所以往往不用程序员手工编程处理;但是从请求数据转换成查询语句,就不太好”通用“了。type= 化工 和 word= 催化 有什么区别吗?都是一个等于号挑着两个串,谁知道前者对应的是 = 而后者就要变成 LIKE 呢?答案是程序员知道,因为产品经理让他这么干的。他会写代码:if (request.word) sql += ” AND word LIKE {request.word}”,这只是用伪代码举例,不用纠结看不看得懂,现实中出于分层、通用、规范甚至是装逼,可能在数据与查询之间再用一个额外的框架,框架又采用一种中间数据结构,然后由框架再转成实际的查询。
可以想象,程序员脑子里装了一份底板,如果产品经理说什么需求那么他就会写什么代码?把前面这个”如果“拿掉,就可以用一个映射来描述 需求 => 代码。当一个老码农阅码无数后找出来几种常用转换归类,那么,可以用结构化的方式描述底板:
{
“ 一般字段 ”: “`{字段名}` = ‘{取值}'”,
“ 关键词类 ”: “`{字段名}` LIKE ‘%{取值}%'”,
“ 多个值类 ”: “`{字段名}` IN (JOIN(‘,’,{ 取值}))”
}
如果进一步发掘共同点,还能总结出数字类型、日期类型,然后前端表单结构那边让一步,双方约定好规则,比如加 _lt 后缀的表示小于、加 _gt 后缀的表示大于,或是在值的格式上做文章,定义为类似数学区间的形式 (min,max) [min,max]。
这还不够,结构描述我们还可以搬进去更多东西,比如 docuemnt 表跟 author 表之间什么关系。说到关系……串场了串场了……对于关系,可以查阅 ERM (实体关系模型) 相关的资料,并不需要技术背景就能看明白。简单来说,我们可以将文档(资源、实体)之间的关系归纳为一对一、一对多、多对多,加上依赖方向,再衍生出几种不同的图例。反过去向下追究,到物理模型,就只有一种关系,可以用 UML 的类图表示为依赖,只需要一个箭头符号来指向被依赖方。
好久不画了、软件也没装,度娘搜来的 ERM 图例和 UML 类图太丑,略
起初,我的系统建立在关系数据库之上,这也是常见的教科书式的手段。数据库的表结构本身含有一定的描述,比如字段名、字段类型、取值约束等。但这显然还不够,于是定义了一个 xml 来描述表和表之间的关系;这部分其实也能从数据库本身取得,但获取方式在各数据库产商中并不像 SQL 那样有一致的规范,而想要统一就得进行封装,内部再来处理差异部分。这偏离了目标,因此仅在完成 MySQL 的结构提取后就终止了,改为自行描述关联关系。这样做顺便得到一个好处,很多时候可以不需要 JOIN 就能完成关联查询,甚至跨库跨数据库类型的关联的——尽管可能效率不够高。
这个关系数据库的描述文件见:默认配置,内部注释含写法;格式描述。
https://pic3.zhimg.com/v2-98d… 用户模块的关系描述文件
后来,NoSQL 兴起,有 Redis、MongoDB、CouchDB 等诸多选择,可惜这一次这些大厂似乎谁也没说服谁,无法形成统一的标准。如果定义一个资源对象就是一个文档(参考简历的结构),因此选择文档类数据库就再合适不过了。Redis 等键值库肯定不行了,弃之;我还希望这个库能嵌入我的系统,这样就可以像 Sqlite 那样一同打包、一并启动。于是 Lucene 成了不二的选择。
Lucene 非数据库?此话怎讲?阿里搞搜索的人说他们的业务部门希望他们的搜索系统像数据库一样即存即取,印象里他们还专门发文谈到过。坑他们都踩过了,虽不知道如何跳过去的,但只要知道能过去就行了。所以 Lucene 不但是数据库,还是很好的文档数据库;需要的话还可以是图(谱)数据库,Neo4j 底层即为 Lucene,我也有直接用 Lucene 开发过人脉关系引擎。
打断一下,现在你要搜 Lucene 首先进入的是 Solr 的站点,Lucene 已经成了 Solr 的一个附件。为什么不选择封装得更完备的 Solr?除了上面说的需要嵌入的原因,还因为将要谈到的描述结构 Solr 就那么干了。大爷我手里有枪,别拿你那大炮仗吓唬人。
文档数据库是没有固定结构的,怎么存怎么取你说了算。这很麻烦,但对我的需求来说,这太棒了。就意味着,只要做一个结构生成器,再也不必为如何生成和变更表结构伤脑筋了。
先把示例文件甩出来:默认配置,内部注释含写法;结构描述。
https://pic3.zhimg.com/v2-be2… 自动生成的资源描述文件
至此,码农脑子里那份底板已经抠出来最简单但也是最常用的部分了,码农们在这一瞬间得到了解脱。放心,失不了业;搞成平台除外,但也不必担心,真那样的话只会刺激市场释放更多需求,然后码农们搞得风生水起,“升值加薪,当上总经理,赢取白富美”……打住打住,“你有权保持沉默,但是你没权扯淡啊”。
然后呢?让产品经理去写配置?倒也是,这 XML 看上去也没比 PRD 难在哪嘛。但送佛还是要送上西天 D,拿键盘敲代码(尽管不是逻辑代码)这种蠢事让我们码农干就行了,还是要搞个图形界面好让人指点江山呀。
https://pic1.zhimg.com/v2-ed0… 资源设置界面
https://pic1.zhimg.com/v2-6ff… 资源管理界面
https://pic1.zhimg.com/v2-655… 资源编辑界面
https://pic2.zhimg.com/v2-8e0… 接口文档界面
https://pic1.zhimg.com/v2-758… 对外开放界面
https://pic2.zhimg.com/v2-ef7… 开放查看界面
第一幅图的设置完成后,后面的界面就自动产生了。这个依据是什么呢?产品经理、助理们会画出一个个原型图,程序员看了后想象数据该如何存储,然后倒腾脑海里那一堆代码底板,最终变成逻辑和页面。这要说回上十年以前,没有产品经理,没有 UI、UE,几个程序员在白板上比划两下就撸起袖子开干了;至于界面,顶多再拉个美工做几个水晶按钮,现实中相近的东西长什么样,软件就尽量做成什么样。
这个话题深入下去可以谈到“自顶而下”和“自底而上”两种设计方式。简单来说,一个是从表面看问题,最后确定一下如何将状态持久化;一个是从底层看问题,先确定数据如何存储然后向上逐层扩展直至界面。实际的情况多是两者相结合的,“自顶而下设计,自底而上实现”。
现在穿越时空回到过去,定义有什么样的结构就该有什么的界面,那么从那个表单配置 XML 出发,通过研究历史的界面和操作流程,进行总结并将过程固定下来,形成映射关系,就自然可以产生操作界面了。
具体点来说,总结发现一个普通的 UI 流转过程可以抽象为 加载、打开、发送 3 种类型,采用事件驱动,并在流程上规定“谁打开、谁负责”,让各大组件间可以用最通用、最简单方式交换状态。但与面向前端程序员的接口不同,UI 是面向人的,而人的思想和喜好具有很大的不确定性,因此,并未对 UI 部分提供更多的辅助设置。一个注定要被重新编写的东西,你做的越多那么定制的人改起来就越累,一点多余也不做,就是最好的选择。
当超越时间去看待问题,一切过程都是结构。
TODO:没人愿意看你的代码,这里应当放一个演示视频。
谈一个技术上有争议的事,为了实现这个系统,我打造了一个完整的应用框架,完全不同于 Spring,Struts 这些业界流行的产品。参考了 PHP 的松散数据结构,参考了 Spring 的反转控制,参考了 Python Django 的校验模型(有四五年没用了,不知道现在套路变了没)。而且大量使用集合框架,而很少去构造私有结构,这在部分程序员看来可能是过于松散,有些应该做只读隔离的却没有做封装。但熟悉 Java 的都知道,反射其实能跳过很多限制,有些隔离封装就是防君子不防小人。我没有必要对此上工作的程序员像防贼一样时刻惦记着,而只要一个约定即可:有些全局的东西你最好别去变更。
不管您认不认同,看在码了这么多年的份上,给我 GayHub 上可怜的星星数 ++
再补充一个,知乎上有谈到人工智能生成代码的话题。AI 很火,2018 年火得一塌糊涂,我所在公司的一个兄弟单位也在搞,一下子好像 AI 要统治世界。那么,这里面最让各路人员不爽的当然是码农兄弟们了,让你们做个软件磨磨唧唧的,如果机器能写代码了,那就不需要雇佣程序员了,再也不会跟他们因讨论识别手机壳换主题颜色的问题干仗了。可是,如果某个系统的产出是程序代码,那么他首先的用户不应该是码农吗?如果您的系统是为了解决某些普遍的问题,能生成代码为什么不直接执行解决问题呢?如果需要生成代码,那只有一种可能就是那个生成的代码不够完善没法直接应用于终端,需要码农去微调。
一二十年前的一些 UML 工具箱就能生成代码,人类似乎太健忘了。5 年前我还在魔都干着互联网广告的勾当(所以没准几年前您在各网站上瞎点时咱们就产生间接联系了),也写过一个小的报告系统,中间会生成报表代码,原因正是上面说到的,跟各不同报表需求部门扯不清楚,短时间内没法总结出普适的规则,所以生成脚本代码,以便在接到特殊逻辑后快速微调。
所以,虽然我认同 AI 的发展方向,并因不能全身进入而为自己的未来感到担忧;但是,我不认同用 AI 生产代码是个好主意。承载代码的是编程语言,而语言就是沟通的桥梁。AI 与 AI 之间可能在未来会产生属于他们自己的沟通规则,但那不应当是适合人类阅读的程序逻辑代码。