扫码阅读
手机扫码阅读

分享实录|仝键老师:程序员的认知成长之路

501 2023-08-30

温馨提示

本文为仝键老师在5月21日的分享文字实录,全文14578字。预计阅读时长37分钟。请坐稳扶好~

主持人:大家晚上好,我是今天的活动主持人文博,一名正在成长中的敏捷教练,今天非常荣幸的能邀请到仝键老师到我们能达的社群分享。印象中好像仝键老师很少在社群做分享,所以今天来到会议室的小伙伴真的是赚到了。仝键老师是thought works的首席咨询师,也是思沃学院的创始人,在技术人员的培养上有非常丰富的经验。我也是仝键老师的一枚小粉丝,我记得大概在两三年前,就在我的印象笔记里面储存过仝键老师的文章,非常多的经典的文章,会经常去看,所以今天来直播间的小伙伴都非常的有福气,仝键老师非常少在社群分享。接下来的时间就留给仝键老师开正式开始我们今天的分享。

分享正文

好,大家好,我叫仝键,来自thought works,我大概是10年加入thought works,在thought works待都快10年了,从业10多年了,从程序员开始干,干过程序员,干过架构师,干过项目经理,做过一些基本部门负责人,做各种各样的事情,然后最近几年主要是在关注人员的能力成长。因为我早年是一个敏捷社区的一个小虾米,很多年前。当时混在敏捷中国邮件组里,然后一直都是敏捷的一个狂热粉丝。所以因此去了thought works,因为当年thought works就等于敏捷。在这个过程当中,一直在探索着说最好的软件开发的方法,最佳实践等等。

探索之后,最后就发现不管你采用什么方法,最后都会落到人的问题上。在公司内部,我们在讨论问题的时候经常会提到这句话——“最后都是人的问题”,但是我对这个答案非常的不满意,不满意在什么地方?因为我觉得这个答案是一个黑盒。当我们提到人的问题之后,思考就结束了,我们就说这是人的问题,然后没有然后了。人的问题有哪些呢?没有人仔细去分析分解。反正就是一个人的问题,就完了。所以我就开始去做能力建设的事情,正好我们内部当时的时候有大概有400来人,但是扩张开始了,然后我们的总经理看的比较远,他预计到未来人员扩张之后能力是个问题,所以他就投资了这个事情。

然后我就来做这个事情,我做了几年,从内部做,然后做能力建设,摸索到一个程序员怎么样才能培养出来?怎么样能培养出来一个我们觉得比较适合用敏捷开发的方式去做事情的——你不敢说是一个唯一的优秀的程序员——但是最起码按照我们认为的敏捷的方式去做事情的程序员,他应该具备什么样的思维模型,他的能力应该怎么锻炼出来,一直在摸索这样的事情。然后在做了几年之后,我就开始觉得要对自己的个人职业发展考虑,一直做这个事情,做久了可能职业发展都是个问题,因为一直在内部越做越像hr了。于是开始转向做对外,所以近几年就开始做对外的培训,做一些大客户。

然后我们就摸索了一些我个人的心得,这也不是一个成体系的理论,或者是有哪个人说过的,就是我个人的一些在培养方面的间接,因为我做的比较多了,从毕业生的培训到3~5年工作经验类的培训,到有非常多年工作经验的这些人的培训,我都做,然后我也亲自去上课,我也亲自去教学,我在里边摸索,所以我能够发现这些认知上的一个连贯性在里面,可能也是给了我这样一个视角,让我更好的去发现这样的背后的,程序员应该是需要有一个什么样的思维模型,然后一直一点一点的走向去思考,说一个好的程序员到好的架构师,或者不敢说好的架构师,最起码是一个技术负责人,他应该有什么样的思维模型?

然后今天给大家分享的就是我的一个阶段性的思考的结果,不敢说多么的正确,但是是我的一个自己摸索出来的一些认知。然后这些内容可能有点抽象,因为这是一个45分钟的分享,这里面其实每一块,每两三页PPT,我们展开都是一个两三天的培训,所以它不能保证大家掌握,只能给大家分享一下,像一个地图一样,给大家看看这个地图上都有什么能够更好的去建立起一个全局的认识,然后在这个里面大家如果有一些多年的工作经验的人,可能对某一块可能会比较感兴趣,所以我们在最后的时候,不管是在后面问问题的时候,还是说我们结束了大家群里面有一些问题的时候,想继续讨论的时候,都可以继续就某些话题展开去聊一聊。

好,那么我就不多说了,我开始进入到我们分享阶段。

当我开始去进入软件这个行业之后,我听到了非常多的词,就在这儿,最让我觉得不能理解的就是“高内聚低耦合”,到底怎么算内聚什么算耦合?但是你好像拿这句话一说出来之后,很多人都有一种不明觉厉的这个感觉,就觉得好像很高大上。但是你要细问怎么算耦合怎么算这个内聚,你会发现这就千人千面了。







还有很多,包括什么“正交的设计”,“对扩展开放,对收缩关闭”等等这些设计,包括“优雅的设计”,什么叫优雅?用上这个词的基本上都是很模糊的。所以我自己在刚开始做培养新人的时候,我也会用一些这样的词,用这个词的开始,你就会觉得好像还挺高大上,觉得自己挺高大上的。但从培养人的角度来讲的话,你自己说的很嗨,显得你很牛。这件事情从专业角度来讲是一个很低级的行为,就是你没有把这个东西让对方搞明白,讲了半天对方不明觉厉,这个其实是很失败的对吧?所以我就思维思考没有停滞于此,我就在想,程序员应该从最底层开始建立一个什么样的思维,然后到最上面,它怎么一点一点把思维给用指导自己写程序。
我也去看了一些书,里面会有一些对我有帮助的。那么首先对我的一个最重要的指引,输入输出模型
我之前看了一本书里面跟我讲,判断一个什么样的人适合当程序员呢,就是说他能不能在脑子里模拟程序的运行,如果他不能那么他可能就不适合当程序员。
怎么算模拟一个程序的运行,就是有个东西输入进来,然后在脑子里去想计算机会怎么算,最后输出是什么,他能够在脑中的模拟这个过程。给大家现在看的这张图,就是冯诺依曼体系的一个概念图。在计算进外面的是输入设备,输出设备,进来之后有一个控制单元,它控制着我们的算法或者叫逻辑单元,跟内存读写,处理完之后输出出来,这就是计算机。






所以我们想问题,第一个要形成的习惯,想问题先想清楚输入是什么,输出是什么。这个事情好像听起来很简单,根据我的教学的观察,绝大部分人并不是这么思考问题,我也不知道具体他是怎么思考问题,他有一个模糊的输入,有一个模糊的输出,他就开始搞中间这块。所以我们需要建立这个模型,中间这一块想之前,重点注意力先放在这两个字的部分,你的输入到底是什么,你的输出到底是什么?把这个问题想清楚,然后再开始工作。终于想清楚了之后,它最终会变成什么?就变成我们的验收条件,变成我们的测试——你有了一个测试的标准,你写代码才会是在正确的路上。
所以到这儿这就在我们敏捷社区里面极限编程的理念就有一个叫“测试驱动开发”,但是很多人可能不适应测试驱动开发的的方法,当然可能有人不太熟悉。它基本上这个思维就是这样,你把一个模型想清楚输入和输出,你已经确定了输入和输出了,你为什么不能先写测试?你写完测试之后,你自然就就能够确定你最终那个代码写完了,这个测试就是给定给定固定的输入,我能得到固定的输出,这个程序就算是ok了。这是一个非常朴素的想法,但是对很多人来说,很痛苦,做不到。
那TDD(测试驱动开发)就是一个我们在教学生的时候,首先要学会的。教输入输出可能非常简单,比如说我给你一个集合,你能够选出比如说指定ID的元素,然后给你一个集合,你按照比如说谁大谁小排个序,这两个东西就是非常简单的程序,但是实际上它跟我们的在你的手机上用的很多程序是一样的,比如你要查一下个人信息怎么查的,你点你个人的头像的时候,它是去服务器上取下来的,这就是跟刚才那个是一样的一个模型去思考问题。只不过中间链路更长一点,本质上是一样的。你从测试的角度看就是一样的东西,你给定这一个ID,最后一个查出来这个东西,这里边一堆用户把我这个查出来,然后你去点外卖对吧?他给你显示出来一个一个的你可以点的店,他也是一样的——那么多店,做个排序,然后把前几个发给你。是一样的。
你定好了输入和输出你就可以你就可以理解程序到底它是一个怎样的一块,你定好了之后,你就可以按照TDD的方式去写程序。我们用TDD方式去写程序的话,这地方写的就是单元测试,单元测试是其中一种,但其实整个模型是这样,这是一个完整的TDD的流程,我们一般教学生就先写测试用例,然后执行测试用例,然后得到期望的失败——这个有点诡异,不过一会我们给大家看一下什么期望的失败。然后如果没得到期望的失败,说明你前面的这个地方有些问题,所以你可能会回去改你的测试用例,测试用例可能有问题,或者你的执行环境可能有问题,把环境给解决掉。
如果得到你期望的失败,下一步写实现,写完实现之后继续执行测试,执行完之后你就问自己测试是不是通过了,如果没通过说明实现写的有问题,回来改实现。如果测试通过了,你就问自己要不要重构,如果答案是要重构,那你就回来改你的实现。如果答案是不需要重构,那你就回去写下一个测试用例,这就是一个TDD的复杂版的流程。
但是有很多人用TDD的测试框架或者其他测试框架的,还有一个简化版的流程。你首先第一步先得到一个红的东西,测试失败了就是红的,执行失败就是红的,然后你把它变绿,执行通过了就变绿,然后去重构,重构就再变绿或者变红,变红了就要让它变绿,就是这样不停的让红绿重构,这是一个完整的流程。你通过这种方式来解决已经定好了输入和输出,我就差把中间的实现写完,通过这样的一个方式能够把中间的实现写完。

这是我们教学生的建立的第一个基本的模型。第一先梳理好输入输出,然后把输入输出用测试的方式写清,规定好你的边界,然后把它实现,通过TDD的方式把它实现。给大家看一个小的视频,只有其中一部分,只到了“期望的失败”。
我们可以看一下,这个就是加速放的——它会先写测试,我要写一个简单的测试,这个是它的编译是不过的都没有关系,先把这写完。写完了之后,我们再去写实现,想办法把它编译通过。然后你执行得到一个期望的失败,这时候一个测试就写完了,然后你再回去,说它要返回1,然后你再想办法来实现。这就是测试驱动开发,你确定好了,我这个东西要怎么做,然后你再去做它,这是第一个模型。
这个模型如果不建立起来,基本上后面写程序都是乱写,很多人当然都是乱写,见过的一个很有意思的例子,就是他的实现代码都是从网上粘过来贴上,然后不知道怎么改了就通过了,其中任何一行代码不敢删。还有一些学生他理解一块代码,比如理解一个for循环,是整体理解的,不能理解中间的一行一行执行的,执行完了变成什么,对什么产生影响,就是整块理解。这些东西如果在一开始的时候没有搞清楚,就一直带到工作当中,工作了好几年,你就会产生很多制造bug的程序员,这个是很严重的,所以从一开始就要建立最根本的模型。
我要知道一块代码的输入是什么,输出是什么,确定了之后,写测试,测试确定的时候,然后再来写这个实现。
那么细到一行,你可以理解这件事情,大到一个系统,你可以理解这个事情,然后中间你要理解一个一个的函数,因为函数里面可以调用其他函数,所以你根本不知道最后长什么样,所以这个模型要建立起来。已经有这个模型的人会觉得,这不是废话吗,但现实情况是,如果有听众里面有一些人是带团队的,你可以回去观察一下,尤其是新人的时候,毕业生这种,你就看一下他能不能做单步调试,一步一步的,有些人连单步调试都做不了,他不能理解这模型,或者他单步调试调到 for循环直接跳出去,不会进去一行一行的看。他不能理解这个输入和输出,他不能往细的理解,也不能往大理解。做每一件事的时候都是参数上的东西先不管,返回值那地方也不怎么管,就先写实现,写到最后再去改输出和输入。这种写法的话从很大程度上是有很大的风险。所以这个模型极其重要,但是很多人都会忽视,一旦具备了那个模型之后,接下来的问题就是问题复杂化的问题,具备输入和输出的模型之后,那么在那个场景下我们讲一个比如说查找一个元素出来,其实它就是一个函数,给你一个数组,你查一个元素出来,那就是一个函数。
很多时候这函数不止一个,比如你有一个场景,计算订单,能分几步,第一他计算里面的订单项,第二步计算优惠计算总计,最后对外处理。你要实现这样一个程序,从这里面看黑盒的时候,你只能看到这一部分,然后这后面东西都看不见,对你来说都是黑的。你要做的时候,你要是按TDD的方式,你从这儿开始开始驱动,测试驱动,就会很麻烦很累。关键问题是他后面只有三个,只有一层,后面如果再有很多层的话,基本上你就做不了,所以这是很多人TDD落不了地的原因。
首先,我们前面讲的是做一个基本单元的,当一个复杂单元的时候,干的其中一件事就是分解——你会先分解几个职责,在这几个职责里面,你分块的去列输入和输出,这是一个非常重要的一个思维模型,就当人脑是解决不了复杂问题的,人脑能解决的问题是非常有限的。当遇到复杂问题的时候,人脑只有一个办法,就是分,分治,没有其他办法。所以先分,你分的对不对呢?你分的多了自然就知道对不对了。一开始这也是没有万灵丹,吃下去就能保证你能分好。
你分开之后,你针对每一块,重新使用我们前面说的模型,就是输入和输出的模型,你分出来的每一个方块就是一个函数,是一个代码块,我们后来叫它是一个上下文,在这个里面它解决的是一个问题,比如在这儿计算单项,在这计算优惠,计算总计,所以你的一个函数里面就解决一个上下文的问题,这个上下文它有输入输出,你定义清楚这些输入和输出,就可以去写测试,可以把它分解为输入有哪些,输入的数据结构是什么,它输出了哪些,输出的数据结构是什么?具体有哪些例子,例子是什么意思?你比如说我我能下个订单,我能下个订单订单,这次比如你买了一个普通的东西,你下次买了个生鲜,这两个的数据都是不一样的,可能后面的流程都不一样,你可能就要去给两个输入用例,属于测试用例——所谓的例子就是测试用例。你这些测试用例就是你测试的数据,有了这些输入输出,分析清楚之后,有了这些数据,就可以复用我们前面的测试驱动开发的方式来写代码——把一个大的问题分解掉,我们就可以知道这个思维上哪对哪不对,其实很多时候分解能力是很欠缺的。
然后分解完了之后,有了一个这样的分解,每一个都可以生成一块,这一块的测试就好像一个功能的验收条件一样,一个块儿就是你要完成的一个任务,其实这就是一种任务分解,你把大程序拆成几个小程序,每个小程序的完成就是一个任务,这个任务它的输入确定了之后,你就有了任务的验收条件,它是一个可完成的任务。
然后你就得到了一个编程的一般套路,这是我们基本上要干的一个事。首先第一步需求来了之后就呈现需求,这个我们先不深入去聊,一会儿再聊;然后你把它分层分块,做大的分解,哪些是上层,哪些是下层的,比如哪些是处理输入的,哪些是做具体逻辑的,哪些就是处理输出的,你这一分中间就简化了,你中间做数据逻辑的就不用考虑输入输出的IO怎么办?各种问题不用考虑——这也是我们在读很多人的代码的时候,发现了一个很要命的事情:IO和逻辑混在一块,导致这个程序很难测,就告诉我没法测。
所以第一步先分开,分完之后,梳理输入,梳理完输入之后,你把输入输出映射为一个任务列表,每一个任务列表是有断言条件的,你可以定义每一块的验收条件。比如这个里边映射出来之后,它可能会有这么多要完成的代码,这一块它整个的可以映射为一个个的测试,其实测试我们有的时候也是分步写的,这一个任务列表的一个任务项,大概会分成这么几组测试,有的是通过测试驱动来定义接口,有的是通过测试驱动出它的业务逻辑的这些happy path——没有任何异常,很开心的走完的。然后这些再考虑一些sad path,一些很痛苦的有异常的情况,还有单元级的功能测试,单元级的功能测试是什么意思?就是你在单元级别写测试的时候,有的时候我们不是要测实现,而是要测功能,你需要考虑,把整个分成一块一块了之后,不是测这个里边的具体的实现——这个有机会再给大家聊,这个是一个提高测试的性价比的一个方法。完了之后你就做一些重构,重构的时候你会发现原来测试没写全,于是你就加点测试,有的时候重构的时候发现测试也要改,所以要改测试。所以整个的过程你就得到了一个编程的一般套路,这就是我们说的一个TDD的完整流程。
但是这样写代码,到此为止,感觉好像能写出个代码,但其实这个代码的复杂度还是不高。我们整个路径其实就回顾了我自己慢慢成长过程当中,代码越来越复杂的时候,我是怎么思考问题的。当你的代码越来越复杂的时候,你下一步你就不能只是这么想问题了,你像这样拆,它拆出来是个面向过程的一个代码,是过程式的,难听点就是“意大利面条式”的,就是一根根的面条,给揉在一块了,你就基本上解不开。
所以随着程序的进一步复杂,你假想一个场景,你有1000个数据,1000个过程,你怎么管理它呢?
这个时候我们需要引入一些面向对象的这些思维方式,很多人看什么函数式或者怎么着的,说“面向对象已死”,其实也未必。我自己在工作过程中发现函数式的思想也好,或者其他的一些编程思想也好,虽然非常有价值,但是面向对象还是有它的价值,它最大的一个价值就是代码管理
当你有这么多的代码的时候,人脑是天然的喜欢把一堆东西聚在一块,否则我就聚成一堆,这一堆就不用管了,否则我根本管不了那么多,你如果一大堆函数你怎么办呢?你还是要想办法把它给聚在一起。那么这个思想其实在面向对象里面还是做的更成熟一点。你会发现这些函数它会有一些是强相关的,你比如围绕学生可能有一堆函数,一堆方法,然后围绕班级,它可能会有一堆的一堆的过程,一堆的数据,一堆的函数,围绕老师有一堆。每一个都会有自己的相关性非常强的一部分。这个地方就是相关性,不是因果性——不是说因为它里面用到了学生,所以它必须要跟学生在一块。相关性就是说它出现的时候一般都会带着它,至于他们是不是因此他们就应该放到一个类里面,这个事也不好说。随着你的程序的变化,类的结果也会变。所以这也是代码管理的一个麻烦之所在,随着你的你设计出来这样一个聚在一起的这么一个类,这几个是函数,这几个是它的数据,它的属性,但是它后面还是会随着业务的变化复杂化。但起码这是一个思维方式。
到这个级别的程序员,他就要理解,我有很多的程序,我有很多的函数,这里函数里面有一些是数据,有一些是过程,这个参数是这些数据,有些东西是不是可以固化到一个地方,于是我找这一类函数都去哪找?找一类函数都去哪找,就不用去别处找。我们前一阵去的一个客户,还是给了我一个很大的震惊的,虽然他是用的他是用的Java的,基本上全部都是面向过程写的。文件夹展开,乱得那叫一个吓人。所以在这个情况下,要懂得面向对象的方式,重新组合和设计,让后续的人更好管理
因为前面这一块你可以认为到这为止,是说我个人写代码怎么写对,或失败。那么在后面的问题就是软件开发是一个团队协作的功能,所谓的代码管理,一个是我自己脑子别乱,当然你能碰到那种记忆力特别好的,他可以自始至终几万行代码都能背过,你跟这种人没脾气,但是大部分人是不行的,你肯定要做代码管理。你做代码管理的话,面向对象就是一个非常常用的手段,所以练完前面的就要开始练这块,练这块的时候那就更多奇奇怪怪的概念和似是而非的东西。
我们对这个事情一般是怎么看的?不要看那些乱七八糟的这些虚头巴脑的东西。你要考虑一个很简单的事情,就是当有一个需求过来,你所谓的编程就是把业务问题转化成代码,你所谓的设计就是在你写代码前就思考代码的结构。随着逻辑越来越复杂,我们可能需要一些思维工具来帮助我们思考。所以设计解决了一个问题——你要能够在在开始之前能想象代码写完什么样,这是一种思维模型。你如果不能想象,那么你就谈不上设计,因为如果你设计东西最终差了一点,落不到代码上,最后就是没法工作了。所以当你在软件里面去做这种设计的时候,必须要能够思考最后出来什么样,所以设计本质上是你在开始写代码之前,就已经在想未来怎么对我的代码怎么做管理的问题了。
那么当我们去用这个方式去思考的时候,会有很多的工具,我们一般会教学生什么工具呢?就两个,一个就是类图——他写代码之前要先想代码静态关系,类图是用来分析静态关系的:这里边有几个类,类里边有什么属性,然后它有几个函数,类和类之间是怎么互相关系的,函数之间传什么参数,参数都是什么关系。这跟我们前面那个模型也是很像的,只不过是加入了一个个的类,然后把这些数据和函数封装在了一起。
但是思考静态关系是不够的,很多人思考静态关系就只思考静态。人思考问题,一定要两件事儿交互着做,一个要思考静态的,也就是代码写完什么样,一个要思考动态的,也就是说代码执行的时候什么样——执行每一个场景是怎么执行的,所以我们会让他画第二个东西,就是时序图,通过时序图的方式来表达我的类之间是怎么交互的。这个时序图跟我们上一页画的,我们叫它上下文的图,其实是一体两面,一种信息的两种表达形式。我们一般在教学生的时候会让他们先画这个,因为我不确定他的面向对象的思维是不是已经建立起来了,这个就直接面向函数。但是一旦等他到了面向对象之后,我们会通过这种方式来表达,要以类为中心,然后这些都是上面的一些消息。

这两个只是把这里面的一些文字搬到外面来,其实长的还是很像的,对吧?好处就是在于上下文图它更好的能够表达函数之间互动的细节,帮助初学者建立思维模型;然后时序图它是基于对象进行表达的,你不会迷失在海量的函数里面,看到一堆函数头都晕了。
所以有了这些东西,我们一般在教学生的时候,会让他们做这样一个练习:给你一个需求,然后给我画个类图,然后又画时序图,然后写一个框架代码,写一个大概的代码,这三个东西要能对上,就是你的时序图里边表达的信息类图里要有,类图表达信息,要能够体现在代码上,最终这三个东西能对上。你从入口处,我们之前讲过输入和输出了,从入口处出来,按照时序图能够一个一个走完,不会漏,也不会跳。能建立起这样一个思维之后,这个人就可以开始去做设计了。
而这个思维的建立其实一点都不容易。我们最近也在做一些这种3~5年的人的培训,也都做各种实验,实验的过程中发现真的并没有我们想象的那么容易,好像想一个静态关系,想一个动态关系,脑子里自然就会想到代码。经过我们实验发现这竟然是一个需要后天锻炼的能力。有些人其实这方面还是挺差的,他画的图是一个样,他写的代码是另一个样,他不觉得有问题。也是没有办法,因为大家在学的时候,一般对自己要求没有那么严格,所以最后导致的结果就是设计的时候设计的过于模糊,没有办法指导后续的工作。
那么当我们有这样一种面向对象的思维模型之后,马上会遇到下一个问题——要从进来的需求里边挖出来这个名词,来补充一个一个的类,进行建模
建模这件事情在我们最近实验过程中发现,也是一个很难的事情。
难其实并不是说你要很难才能学会,而是说很多人他其实没有有意识的去锻炼过自己,你会发现有些头脑很清醒的年轻人,比如刚毕业的毕业生,你给他说完他就能做得很好,因为他本来就是这么思考问题的,他长这么大一直都是这么思考问题——遇到一段话之后,他脑子里先想,都有哪些概念,之间什么关系,他虽然可能没有计算机科学领域这方面的有套路的去做过,但是脑子里基本上是这么个思维方式,你会发现他们做起来挺顺的。但有些人他就不知道是怎么思考的,这个事情就做得很混乱。所以大家如果有人是团队的负责人,可以回到团队之后去测试一下,看看是不是所有人都都这么清醒。这玩意不一定。
我们在过程当中就是说给你一个需求,然后让你给我建模型,建模的话,这个过程其实跟上面的也是很像的。我可以举一个例子,很多人都用过命令行,如果我们写一个命令行的解析器——当然这个稍微有点复杂——能够根据传进来这个参数进行解析,解析完了之后得到一些输入输出,校验输入是否合法。我们在建模的时候以它为例,通常是先梳理流程,先考虑说这个程序,第1步干什么,第2步干什么,第3步干什么,第4步干什么,这个要先处理输入输出,然后结构化,然后校验合法性,赋初值,然后到后面执行。
在这几步当中,我们每一步都是有一些输入,有一些输出的,每一步都是要处理一些内容的。所以你根据这个过程当中会面临到的数据,进行名词动词的抽离——数据有哪些名词?比如像我们要抽出来说有一个东西叫flag,它题目里面提了,有的东西叫value,那么有flag有value之后,下边这个东西叫什么呢?当然我一开始的时候可能不知道,没关系,接着往后去读其他的部分。你读完之后你发现 value还有类型,有字符串类型,有整形等,那这个概念叫做type。
这两个东西一个是它真正的值,一个是它的type,这两个东西可能抽象出一个类来,叫value,一个自定义类型。那 value加 flag它可能会有什么名字,好,于是我们发现这东西叫参数名字叫arg,就这样提炼出来。一点一点的把所有的过程都捋一遍,你就会得到一个完整的模型,你会发现你有arg,arg有它的定义definition。definition属于schema的一部分。definition里边每一个参数的定义中可能有一个定义的名字,默认值,类型的名字。这是你的一块专门用来处理校验和赋初值的模型。
然后还有一个专门用来处理输入的模型,raw up,raw的意思就是没有做任何处理,你会有一个集合对象来处理他们,会有两个边界上的模型,不是在一组模型里。所以整个过程是一个逐渐复杂化的过程,逐渐把这个模型给定义出来,定义完了之后,就得到了一个建好的模型,这个过程一会要用动态的思考问题的方式,一会用静态思考问题的方式,这两种模型就会变得非常复杂。前面的那些掌握不好,到这儿的时候基本上就掌握不好了,很难掌握好。
那么建完模型之后,就可以拿到一个需求,能够去好的模型化去思考这些需求,抠出名词动词概念,理解其中的关系。那么再往下就变得复杂了。到目前为止,你可以看到前面是写代码,能有质量的把代码写完,然后到这儿是能够很好地通过设计把代码管理起来。但是到目前为止,我们的练习都是停留在说你的认知是给你的输入都是清晰的,但是现实生活中的人并不是这样的。
现实生活中的人,你就会感觉到什么呢?过来个人跟你讲需求对吧?我跟你说我要做个啥,比那个什么还要牛,这边有什么东西,然后有什么功能,对吧?什么的时候你要怎么样,什么什么时候要又怎么样,听了就很晕。所以你在脑子里面一边听一边要抽象,对吧?你就发现,听起来这是个啥,那又是个啥,他们两个之间有关系吗?
你告诉我了a和b,然后根据你的描述,我觉得a和b之间还缺个东西?这种感知的时候,你需要做需求的分析,分析完之后,你需要把他的这些业务问题转成计算机可以计算的问题。我们前面练的所有的东西,都是在获得一种心智模型,让人能够像计算机一样思考。一旦像计算机一样思考这个能力建立起来之后,下一步的问题就是,你怎么把人的思维的东西转成计算机的思维东西,这里边就涉及到人这个动物不靠谱的所在——人说的话是没有逻辑的,这逻辑是后天训练出来的,先天是没有逻辑。所以他转给你的话,你要能够在里面发现有一些问题,你要去挖掘,你要去提问,这个时候就用到了程序员非常不擅长的一个思考。
大家很多人会觉得这个是不是需求人员的工作,我程序员不需要搞这个工作,这是不可能的。因为什么?因为最终不管需求人员怎么做,都是程序员来实现,程序员接到的需求里面一定会有不清晰的部分。你无论如何都需要具备这个能力,因为你是最终消费者,你是最终消费知识的人,消费完知识之后,然后把它输出成代码,所以你必须具备这个能力。
这个时候你就需要去提问,你提问的时候它就不是个逻辑的东西,因为你发现这个东西没讲那个东西,那个东西没提,等等,这些东西你都需要挖出来,都需要问他。这时候的思维方式就从我们前面的这些偏逻辑的思维方式,转成了后面偏启发式的方式
比如说经典的5w1h的启发是框架——你就问他这些数据数据从哪来的,什么事情会触发,到底谁能干这个活谁不能干,这种东西都属于启发框架,我就不给大家一一过了。这种启发式框架是不能保证你一定能问对问题的,但是你就会在脑子里想,我问了权限没有,我问了事件没有,问了时间点没有,我问了他怎么用没有?一个一个的给自己去提醒。
所谓的启发式框架就是一个结构化的拍脑袋,没有好办法。那么结构化的拍脑袋,其实也有很多前人留下的这些经验,有一本书叫《分析模式》,它里面其实就提供了十几个思考的维度。你从这些维度去问,从会计的角度思考,你从别的角度去思考,你可以从仓储有没有库存,有没有容量,有没有余量的角度去思考。
思考完这些东西之后,就可以很好的去做一些去挖掘,因为你的信息是不全的。当你挖完了之后,接着你会面临下一个问题,你挖出来的东西可能太复杂,大概是不全的一个东西。而且你挖出来一堆东西会很难理解,而它里面还会有二义性,模块太复杂了。比如说库存它有销售的时候的库存,销售的库存要求说,有一个人买你给我减3,仓储的库存要求的有一个就减一,这两个就是矛盾需求。为什么会有矛盾?是因为一个地方是销售领域的库存,一个领域是仓储里的库存,这是两个不同的领域,只是恰好名字一样,而且有点关系,这些问题都会造成你很难处理。
所以这个时候,我们好不容易把需求挖掘完了变成复杂系统,需要做的就是边界的划分,边界划分完了之后,就要问自己说哪些东西需要我自己做,比如物流你可能完全不需要自己做,找一个外面的做就可以了。支付你可能不会自己做。然后这些东西哪些可能是你核心要做的,哪些不是你核心要做的。然后分析完之后,你还可以梳理说谁跟谁是怎么依赖的,谁是上游谁是下游,怎么依赖的,搞清楚这些依赖之后,你就会搞清楚团队之间怎么合作。

你想这种问题的时候,就不用想原来所有人都聚在一个图上那么复杂的东西了,你搞清楚了,将它分成一块一块的,所以这就是在更系统的级别上复用我们前面说的分解的思维模型,去把我们一个大系统分解成一个一个的小块,然后这些小块再往下分成一个一个的子系统,子系统分解成模块,模块分解为单元,单元然后映射成代码实现,都是这样一层一层降下来的。
这个里面需要的就是一种归纳推理的能力。很多程序员开始具备的是一个演绎推理的能力,到这个地方又换了一种思维方式,使用的是归纳推理的能力,这种归纳推理的能力呢是比较反程序员的习惯,可能需要一些后天的练习。
好,整个东西走下来,我们来回过头来看一下,我这有一个另外一张图,因为这个东西比较多,对大家来说可能也是比较比较复杂,你可以理解为一个心路历程,就是首先我学会了TDD能够理解输入输出,能够把输入输出转换成测试,当程序再进一步变大,我的TDD就会没有点没有思路,有没有有套路的方法把我们的方式分解为一个一个可以PPT的单元呢?就要练习做任务分解,做一些模块的划分。然后拆成一个个函数之后,系统进一步变大,之前的任务分解出来的很容易写出这种意大利面条的代码,你要考虑如何做设计,你需要去做一些设计的这种基本的练习。这个时候有一个课题你就要思考了,如何不过度设计,这就是可以跟TDD有一个联系。然后当逻辑进一步复杂,一开始很多人都学过面向对象基本的书,里边介绍那点东西,你感觉不能用于复杂的场景,那是因为你没有去建模,所以你需要练习建模,你需要去做数据抽象,你需要考虑不同的生命周期,然后到这为止,前面的都是我们刚才说的,需求是确定的,只存在于你的认知能力有高低水平。但是从这一刻开始,你开始要面对另外一个不确定的因素了,那就是人。当你面对人的时候,你要去能够分析他说的话,能够发现他的说的话中靠谱的部分,不靠谱的部分,从里面要挖出需求来,少告诉你一个维度,你做的时候漏了一部分,都是因为你没挖出来。你挖出来之后,发现当这个领域太大,会出现一个二义性,你就要做边界划分。像刚才我们展示的一些边界的划分,这就到很多架构师或者做战略设计的人干的活。然后最后的最后就是会有一个问题,很多人到这就会想,整个过程太痛苦了,我们每次做完之后,我又不知道做的对错,我每次都要再重新分析,有没有一个稳定的60分的解,我这么写或者这么做,无论如何建模也不会太差,写出来代码也不会太烂,然后将来改起来了,最起码他在我60分的代码上改,比在一个负分的再往上改还是要好一点的。所以有没有这样的一个东西?
我个人的一些经验,发现有几个可以推荐给大家的,一个大家可以去了解一下“整洁架构”。整洁架构是一个很好的分层分解非常清晰的一个架构,你照着这种方式去去实现代码,指导你的设计,有一本书叫做《代码整洁之道》,这个是Robert C Martin的,这个里面讲到了很多设计的内容,你们可以去看看那本书,大概看完了也能理解这个东西了,以他的角度来去做重构,你将来代码就算写烂也不会太烂。
建模方面有两个,一个就是四色建模,四色建模强调的是以票据为核心来推导。有一篇文章,你们上网搜索四色建模能搜到我们公司有人写了一篇文章,可以在里面看到很多的思想,还有一本Martin Fowler写的另外一本书,《分析模式》,四色建模中用到的一些分析方式,在其中自然就有。所以如果还想更深入去了解,可以去看一看《分析模式》,这些东西加在一块,我会觉得你做出来的设计和做出来的这些代码不会太差。无论如何我们也没法做出一个非常完美的设计,甚至说比较好的设计,但是照着这个方式去写的,我们会发现总之不会差太远。
好,那么这个就是我今天晚上的分享,内容可能有点多,把时间交给主持人,看有没有什么问题。
Q:希望仝键老师推荐一些学习资料。
A:一个就是《架构整洁之道》这本书,我觉得作为程序员,Rober C Martin的那几本书都要买回来。还有Clean coder,《敏捷软件开发》
Q:今天主要说的是业务方面的业务逻辑怎么把它建模?现在业界其实有这么一种风气,大家以高并发高负载这些为荣,然后出去面试什么也是说有多少并发量,多少数据量级,好像只认为这些是技术,业务建模好像不是那么重视,所以我不知道仝键老师对这个是怎么看的。
A:你这是挺有意思的事情。我去过这些比如说大型的互联网公司,大型的这些通信公司,给他们做内部的访谈,然后去给他们建学习的模型。一开始的时候,你去问的时候,很多人都会给你提高并发高负载,问他越到最后他们越会觉得业务很重要,最后就开始提要要学这种业务的建模。这个原因是什么?实际上高并发并发高可用这些东西。在这些大型互联网公司里面,他们其实都已经变成一个内生的知识了,对他们来说其实并不是业务的痛点。
面试为什么要面这个?因为他不会面别的,还有一个说法就取其上得其中,这是一个没有办法的事情,因为你说我会建模业务,很难测。再一个就是他来了之后干的就是高并发的事情,你有这个我可以节省一定的培训成本。还有一个就是,主要要看这个人的sense,在这方面我们都熟悉的话,我们能交流一下,能看你这个人意识比较好,表达能力也比较强,也可以。
所以从这点上来讲,高并发高可用,虽然是这两年比较流行的,但是实际上你去互联网公司里面,他们其实最终也是需要这些东西(指业务建模),因为那些东西基本上就是那几套模式,一旦学会了就可以了。但是在业务建模业务分析的时候会出很多的问题,就包括做底层的那些人,他们其实并不是说只有订单这些东西叫业务,数据面控制面,包括中间的这些信号量这些东西,它都是业务,因为业务就是做设备,业务就是做操作系统,业务就是做协议,那就是他的业务。不能说只有人参与才叫业务,所以转过这个弯来之后,他们也会觉得业务很重要。
所以我做多了之后发现表面上都会讲高并发高可用,但实际工作中的时候,其实这个东西所占的不是最重,包括跟我要培训的也不是特别多,一开始可能会提一两句,后来就提的少。所以从我来讲,我是觉得这个(指业务建模)更重要一些。
ps:仝键老师分享的音频链接,请在公众号里回复「教练分享03期」,即可领取回听链接。

END

图片来源于PPT

录音整理:猴子

排版:周周




原文链接: http://mp.weixin.qq.com/s?__biz=MzI5Njc4MDQyMQ==&mid=2247485482&idx=1&sn=dbf78f026d2c5f840e759b59cf469915&chksm=ecbe5fc0dbc9d6d66c88eebb28ba8ca5db5de017c1b477010aa68f92d59abe933af6abc69ebf#rd