分享

程序员的四重境界文稿整理

 天道酬勤YXJ1 2016-07-08

程序员的四重境界文稿整理

在7月6日我们迎来了第一期微课堂分享,是由陈勇老师带来的程序员的四重境界,可能有的朋友当时没有参与进来。下面是根据老师的口述,整理的一篇文稿。

程序员的四重境界

先说一说程序员的四重境界是哪里来的?大约在2001年的时候,我参加了一个非常好的团队,在这里遇到了我的师傅。在遇到我的师傅之前我已经工作了6年了,但是我的编程水平怎么样呢?我当年做的是C++,我不会写泛型,不会用虚函数,还不会删内存。然后我的师傅用了两个星期就教会了我所有这些东西,也正是在这个团队里,我们偶然地看到了一篇当时csdn上面的帖子,帖子的名字就是程序员的四重境界,很可惜印象中他并没有解释是从境界的含义,但是写了这4个名字。

第一重,编写可用代码,第二重,编写高质量代码

好了说完这两个级别我就先一个级别一个级别解释,待会儿咱们再看剩下的两个级别。

第一个是可用代码,这是个什么状态呢?就是大家想一想,本科程序员毕业之后基本上就在干这件事情。因为在我们正式的大学教育里面,我们只需要学会语法就可以,并没有要求我们去编写出多么高质量的产品了。确切的说,我们大学里边写的所有程序,没有一个经过测试的。所有的考试都是你能编出什么东西来就可以了,这里面是不是要做异常处理,谁去管呢。

下一个问题是我们日常工作中实际上无时无刻都会面临这样的程序员,他们刚刚加入了我们的团队,那么这个时候,关键的一个问题是让他们的编码水平和它们的代码分离。借用诺顿所说过的一句话,就是用不可靠的程序员编写可靠的代码,所以有的时候我们也经常听说,让某某某来负责一个小模块,独立完成工作锻炼一下。但这种做法其实是错误的,设想一下我们编写的,是航天飞机里面的软件,有人想让他去锻炼一下吗?虽然多数时候我们编写的软件还不至于这么吓人,但是我也很难在我一生之中做的所有软件里挑出一个来,是可以允许用来把锻炼人当作其中一个目的的。所以,对于编写可用代码级的人,很重要的一点就是要有人去知道、有人去把关,看到他编写的每一行代码,并且确保每一行代码都是可以放到正式产品中的。

那这个呢,我们常用的一种方法就是139团队。139团队指的就是1个大的领导、3个师傅、9个新手徒弟。这样的话,新手就可以在可用代码级别上,但是他们所编写的所有代码,都必须经过自己相应的师傅,审查之后才可以进入产品,同时这个审查过程也是一个知识传递的过程,也就是说我们改进的并不完全是代码,还包括徒弟的编程水平。我当年的师傅,手底下只有我们3个徒弟,他每天都会看我们编写的代码,并且当场指出我们存在的问题,除了改好代码之外,他还会告诉我们如何永久地避免这个问题,这些避免问题的方法本身就是我们要学习的内容。如果徒弟的代码经过了审查才进入代码库,那么就相当于徒弟的水平和他编写的代码已经分离了。换言之,他正在以师傅的水平编写代码。

第二个级别我们再说一说高质量的代码确切的说有些人可能会非常惊讶,因为我们一共说了四个层级,居然高质量只能排在第二。那么什么样的代码?可以被称之为高质量的,整体上包含两个层面的东西。

第一个层面,是现在的代码编出来就是高质量的,因此,在现在的测试环节以及和别人、多联机调试的时候不会出现太多缺陷。所以这一点上我们有的时候会看到有些程序员说什么呢?他们说你们尽管测吧测出来缺陷我改,这种程序员,第二级都达不到,确切的说是连2.1级都达不到,那么要达到2.2级,还需要保证我们的软件在未来没有缺陷。有很多代码刚刚编出来的时候还是可以的,但后来,自己改来改去或者别人改来改去,就开始出现问题。原因主要是,代码可能在一致性、可维护性上,存在问题。

比如我问大家一下,有一个类,有5个成员变量,每次声明这个类之后,5个成员变量都要被初始化一下。但是后来,出现了第6个成员变量,因此有的地方忘记了修改,没有进行初始化,请问大家应该怎么办?

如果我们构造的完成一次整体初始化,问题就解决了。当然这是一种比较简单的情况,类似的情况还会发生很多。比如最常见的switch-case。我们可能在好多地方都要进行分支,假设有10种不同的类型,在这个地方要使用上面提到的switchcase语法进行一次分支,在遥远的地方还要进行一次分支,在更远的遥远的地方还有好几次,那么类似这样的代码日后迟早会出错,因为人们改来改去就容易忘记其中一个地方。而且每次改变的什么东西都需要满街找相关的地方。这个大家觉得应该怎么办?

具体是哪个模式呢?最好能直接说出那种语法来,因为我其实不记得太多模式。不同的语言可能有不同的语法来处理,最简单的东西其实就是虚函数。虚函数就是多态,简单的说就是不同的类型处理这种方法,走到这一步的时候运行的东西不一样,那么我们可以把这件事本身起一个函数名,但是每个类型都重新写这个函数,因此运行的内容就不一样。是的继承,然后再写程序函数。面向接口也一样的,我们可以认为接口是一个虚函数。

C++和C#,要专门指出,哪些函数是虚的可以被重载的,Java似乎不需要。接口就是多态的实现方式之一,确切的说我发现,如果你想做多重继承但是这种语言又不支持,那么接口就是一个很漂亮的实现。我的代码里一个这个东西都没有,除了微软自动生成的3个,只有在一种特殊的环境里才需要他。所以刚才我们提到的这些语法他们之所以产生就是为了解决未来的质量问题,因为有些东西现在卸下来没关系,但如果他很难读很难维护,迟早会造成长期以后出现问题。有一种语法,和他是一样的,那就是if else if else。这个东西也一定要消灭干净。好了说完了高质量代码的两个子层次,我们再看看第三大层。

程序员的第三层境界叫做编写精美的代码

什么是精美的代码呢?简单的说就是两个字,第一个叫精,第二个叫美。尽量不要用或者说倾诉一下,仔仔细细看一下,可以用虚函数给它干掉。

什么是精呢?就是代码越少越好。不过这里说的少,不是说把那些非常多的代码给强行弄到一行代码里去。而是说在设计整个软件的时候,要想清楚如何通过分层、复用来使得多数代码被调用多次以上。分层的基本原则就是VCMD

V就是界面层,也就是说有些功能是在界面上的,但是和界面无关的东西,一定要分层到别处去。比如控件,比如业务逻辑。

C就是controller。这一层可能是最难理解的,如果大家看过微软的asp.net mvc官方代码,就会发他的每一个action方法里面,代码行数不会超过5行。因为这些代码只负责用来解决权限管理,正确的访问之后去哪里?错误之后去哪里?至于具体的业务,都在下一层里面,因此才给它起名字叫做controller。所以千万不要在里面跑业务单或者任何和数据库访问相关的东西。业务代码,只要碰到业务都写到model,但是做这一层,也要再分不同的层次。比如上层的业务,抽象之后类的业务等等。这些就是了,代码的层级变得很干净,即使代码的整体行数并没有因为分层而减少,但是我们会发现另外几个现象。

第一,我们可以选择性的阅读了,也就是说,如果我觉得业务有问题,那么我就选择业务代码,如果我觉得技术有问题,我就去读技术的嘛,如果我觉得控制流程有问题,我会再看上层的代码。第二,现在可以复用了,换言之本地代码并没有变少,但是别处省了很多代码。第三,维护起来简单多了,因为以前的时候很多地方都有散装的代码,但是现在他们都有统一的地方,在相应的层次上进行了封装,只要改这一个地方就可以了。合法性检查经过了若干代的变迁。

比如在现在的微软的m vc框架里面,他的声明是在model里面进行的,但是微软在形成前端代码的时候,会把声明放在前端进行检查。他认为只有编写模型的人才能真正理解,到底应该做哪些数据检查?而这些数据检查的时机,又应该是在前端提交表单之前,因此他才设计了这种机制。关于查询条件的组合,因为我们现在使用的是entity framework,所以我们把语法写在业务模型中,而最终执行却是微软自己控制的,我也不知道他在哪一层执行了。这是微软首推的方法所以我们也就采纳了,防住攻击也在ef中完成了。注入攻击,好了具体的技术可能每种语言发展的阶段不同、官方的策略不同都会有区别。但是关键的一点就是一定要利用分层把代码变的精致。

第二个特点就是美。美,也是用分层来实现的。什么样的代码更美的呢?我觉得这个时候要看读代码的人的心情。他想读什么就读到了什么,这个时候在他眼中代码是美丽的。比如,我们当年曾经有一段代码描述的是数字电视中的码流,这些码流有一些奇特的特性,比如,布尔值必须使用1位,而数值不超过256的整数也只给你8位。总之最后导致我们的传进去的数据要精确的定位到位。而有一位程序员就手工计算了每一个位置,然后在代码中写了无数个常数,每当这些位置发生变化都是一场灾难直到后来出现了问题。为什么说这个代码是不美的呢?因为当他想看某一个代码,中间要插入一个新的数字的时候,他不只是要看那个数字,还要看后面被他影响到的所有的数字,影响到了人的心情。同样的,我们的业务发生变化我们就看业务代码,技术发生变化就看技术代码,加密算法升级了就改一个加密的算法,剩下的东西我们都不应该去看去找,这样的代码就是美的。最后怎么做的呢?最后我们做了一个自动打包器,你只要告诉他说,放进一个什么数字占多少位,它会自动计算当前的位置。以后要拿出来的时候,只要说请把它拿出来,它也会自动计算一下该到哪里去拿,所以我们有110行的打包器的代码,放在底层从来没有人动它,还有一些对打包器的调用放在上层,但是调用干干净净。代码里面我们重载了操作符进行了连写,因此你看见的东西很像一篇文档而不像一些代码。这使得原来的4000行代码只用了700行就实现了,4000多个常数被76个常数代替。这76个常数来自于数字广播标准,每一个都是不可或缺的,所以才需要被保留。有了这段代码以后每增减一个数字只需要一分钟左右,拷贝粘贴修改其中一行或者删掉它。代码不但变得少了而且更容易读了,因此我们说精和美,并不是两者只能选择其一。往往是两者相辅相成。因为我经常听到有些新手说,如果我们现在把代码缩小到更短可能就不好读了,其实是一种错误理解,真正的高手不会这样。

好了下面说一说第四层,叫做,编写思想深邃的代码。

什么叫做思想深邃的代码呢?在2001年我们看到还有这样一层的时候都笑了,因为对他不甚理解。后来我们逐渐意识到世界上的语言千变万化不断推陈出新,同一种语言也在不断的改进,每一次改进都是深邃的思想的结果。当然对我们普通的程序员而言,我们并不去设计这些语言我们只是利用他,那么我们如何表现出思想深邃的一面呢!

下面就看一看,我们普通程序员的,三小层思想境界。也就是说,第四层又分为三小层。

第一小层是与大师共振。所谓共振,就是当我们看到一个新的语法新的功能的时候,应该一见如故,因为我们也希望也期待着出现这个功能。很多时候我们会发现,一些新手对于一些语法很长时间都学不会,就像我之前说的自己不会泛型、不会虚函数。而每一种新的语法,都是为了解决一些可怕的问题才被设计出来的。但如果我们不把这些问题当问题,那么就永远学不会那种语法。比如,我在使用泛型和虚函数之前,代码中有很多重复的代码,很多已维护的else if, switch case。但是因为那个时候我的水平只有高质量的第一层,所,也不觉得未来维护他是一个重要而且痛苦的事情。如果思想中有这个想法,那么就很难理解泛型和虚函数到底要做什么,反而觉得他们很累赘。所以未来大家对自己编码中,让自己感觉很不舒服的地方一定要深恶痛绝,进而做到赶尽杀绝。这一点刚开始很难,但是逐渐的我们会突然发现,原来大师们设计的一些新语法,就是来帮助我们完成这个任务的。而当我们学会了这些新语法,这些让我们深恶痛绝的地方甚至连出现都不会出现了。比如我们现在的代码一共有2500行,里面没有任何一个switch case,没有else if,连else都只有两个。原因就是在赶尽杀绝他们的时候,我们突然领悟到了一些语法的真实含义,而熟练掌握这些语法之后这些东西根本就不出现,也不用费力去赶尽杀绝了。所以后来我没有猜测,某些让人不舒服的东西是有办法解决的,果然,有些新出现的语法或框架就解决了它。所以我们未来要牢记住一点,一定要把糟糕的东西当作糟糕的东西而不要姑息他们,这个时候我们就很容易学会那些,大师设计的用来解决这些糟糕的东西的方法。

第二小层叫做手中无刀。这是一个很奇特的概念,大致的说法就是我们已经忘记了具体的招式,但是我们做出来的东西仍然不差。有的时候大家会随手用一个泛性,随手用一个虚函数,随手写下了什么东西。然后再放眼望去,代码中没有任何重复的难以维护的东西,至于我们到底用了什么模式?自己也说不清楚就进入了这个状态。这个时候,确切的说我们相当于大师们在创造设计模式的初期的收益水平。那个时代天底下还没有给设计模式起名字,但是设计模式却已经在那里了。所以未来我们真正学会一个设计模式,不是能记住他的名字、记住它的用法、记住它的使用场景,而是当那个场景出现的时候,我们会随手写出来。这就要求我们在骨子里,对于我们的产品的代码有着很高的要求。在精美之上仍然有更精美,时间长了设计模式自己会送上门来。当然不完全是设计模式。

第三小层是可以自主创新。很重要的一点,现在的所有语言、框架、工具、设计模式都是人类创造的。因此我们除了能够学习和掌握他们之外,我们也能创造它们。这一点国内的创新可能很少,因为我们的主要工作是使用这些东西,很少有人去真正研究他们。但在国外我们可以看到,很多普通的人他们设计了完全全新的框架,自动化测试的各种框架,m vv m等等。其实这些人也是凡人,只不过是他们总是在思考,并且在没有一个东西的时候,不是在等待而是自己亲自动手。所以未来我们也要把握住这一点,如果我们仍然感觉代码有点让我们不舒服,而世界上别人又解决不了,那么我们就亲自动手解决。确切说我们自己曾经做过一些改进,比如说在早期的asp.net mvc中,每一个action里面都有一个非常难看的try catch。我们觉得他很不舒服就想办法在全局去抓这个异常,从而使得代码变得更干净,到了第五代官方已经自己做了这些事情。在往上可能还会有更高的层次,但是我发现除非我们能达到那里,否则无法精确地描述他们。未来可能会有高人再画出更高的层次。

下面我再补充一点点内容吧,就是关于这4个层次到底给我们带来了什么样的启示?

我初三的时候很喜欢打乒乓球一直打到大学,到了大学里我们还专门报了乒乓球班,所以水平也不差。然而有一天学校里来了一个大人带着一个8岁的小孩,这个小孩可以轻松秒杀掉我们那一队乒乓球班的人,而那个大人也是他的父亲能够干掉我们校队的教练。到底发生了什么能够让8岁小孩战胜我们呢?我认为是一种职业训练,他在刚刚出道的时候,就被按照第四层的标准进行训练。而我们因为自己刚刚学,所以给自己定了非常低的目标瞎打瞎闹。所以尽管我们打乒乓球的时间比他长的多,但是水平却差得多。所以我认对于普通的程序员,如果他们能够在初学软件的时候就像思想深邃看齐,那么他们可以少走很多弯路。

那么如何找到思想深邃的人呢?我觉得远在天边近在眼前远在天边的人,是那些官方代码。有很多人学习编码是随便找一本书就看的,我觉得还是应该到官方里面去找真正的高手写的书。除了这些圣贤之书之外剩下的尽量少看,这样我们直接就可以学到编码的精髓。其次,身边一定会有各路高手,对于这路这些高手而言,他们可能未必能达到第四层,但是也一定有可借鉴之处。可以作为一个方向,对我们某些不能理解的东西答疑解惑。

问答:

问:那么if else如果大于3层,该怎么办?经常会用到知道很烦,但是不知道怎么去掉。

答:反正我们的产品里没有。或者说即使删掉这个语法,一样可以干活。和switch case是同一个东西啊,用相同的方法可以干掉。我们在1万行代码的产品里都没有的。现在的产品只有2500多行,也一个都没有。

问:如何判断是否变量是否为空?

答:这种情况尽量使用一种结构叫做false false true。就是先处理异常情况,尽早返回错误,然后这样就是if,if,最后连else我用不上了。有没有缩进。这个和刚才说的情况不一样,它不是一种分支结构,是流程扭转。也就是说,尽量不要产生缩进。这会使得语句变长难以理解。扭转一下流程就好了。

问:如果业务逻辑大的话要分很多函数,这样读起来产生思维跳跃,不停找到定义它的函数来实现。

答:业务逻辑可以把上层业务和下层业务做一些分成。比如我们当年曾经有一个东西是做权限控制的,总公司的人可以看见分公司的、分公司的负责人可以看见部门的、部门的负责人可以看见团队的、团队的负责人可以看见小组的、小组的负责人可以看见自己写的模块、自己的模块的负责人可以看见模块下属的史诗故事、史诗故事的负责人可以看见用户故事、用户故事的负责人可以看见用户故事的测试用例、用户故事测试用例的负责人可以看见因为这个测试用例而产生的缺陷。这个东西如果用简单的写法一定会臭长无比,我们就做了一个底层类把刚才所有东西都设计了一个基类,所有类型都有一个父亲,父亲的负责人可以向下面所有的东西自动继承权限这样上面写应用的人就不用再管权限问题了。

如果担心思维跳跃,很重要的一点就是一定要跳过去那些我们不该读的东西。比如我要读的是上层业务,我就不看下场是怎样实现的,这样就不跳跃了。否则无论分层或者放在一起思维都难免跳跃,可能有的时候老想看看这个东西是怎么实现的,不过我现在对自己的代码经常都不记得,因为我们在1万多行的代码里大约有2000个函数,或者说每个函数只有5行。我是说有的时候我们程序员特别想看看代码内部是怎样实现的?但其实以后要放弃这个习惯,因为我们应该逐渐忘掉某些事情是怎样实现的,这样才能把精力集中在当前要实现的业务上。

问:刚才说的权限问题,有个问题是经过管理员授权你也可以看到任何层级的权限,这样的话有没有更好地方法。

答:那就在基类里面写上一个(或多个)被授权的用户。任何一个人都会问当前用户,是我的负责人吗、是我的父亲的负责人吗、是我的祖父的负责人吗等等等等,再问是一个被特殊授权的负责人吗?因此授权问题我们就永远忘记他,因为我们的基类可以解决它。剩下的就是我们做了授权到底要做什么呢?这才是我们真正要干的事情。这样做还有一个好处,就是任何一层都不可能出错,因为是他们是有共同的一个机制完成的,不会因为我们分工协作有些新手就把事情搞大了。

问:如果身边真的没有像样的高手指导呢?

答:嗯,那就多看书吧,一定要多学习官方编码,并且在自己的代码任何时候写的不像官方标准的时候都要更正,千万不要积少成多。我当年看了微软的副总裁写的m v c的代码,跟他学习了所有的编码规范,力求所写代码没有一个地方能让他认出来有问题。

问:你们项目对于一般接口的一系列的验证怎么做,难道是一堆if else吗?

答:是指检查接口送进来的东西是否符合要求吗?在微软的mc框架里面,他会尝试把送进来的数据封装到一个类里面。这个类每一个成员变量都有一个attribute对他进行检查,所以只要这个东西写好了,它就会自动对他进行全面检查。这样就避免了对于大堆散装数据的挨个检查。

问:Java应该也可以这样做吧?

答:我觉得应该有吧,虽然我没用过。因为连我都能想到的东西大师们不应该放在那里不做。是的这两种语言竞争很久了,应该在相同的水平上。有一段时间我去摸索移动端代码编程,我发现所有检查都还没有封装起来。甚至还有很多类似getViewByid。因为我用的移动端开发使用C#编写的,所以我就用反射做了一个自动映射,差不多70%的代码都可以省掉了,那个还是他们推荐的官方代码。如果官方能够派几个人多研究一下其他平台的编程技术的最新进展,他们早该自己完成这个工作,用的也是attribute。

此文章未经作者审阅!

想要收听更多的干货分享吗?那就加群吧!

加群方法

扫描下面的二维码加好友,接头暗号「我要进群」。瞬间就穿越到群里。

-- msup 研究院团队

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多