回顾大约7年的程序员生涯,从一开始的小白,到现在成长为一个可以去帮助他人的程序员,虽然离大牛还差得远,但还是有些东西想写一写,就当思绪的偶尔停留。如果能对他人有所启发,就是意外的收获了。
这里不写具体的编程语言、技术内幕,而是写一些普遍适用的,甚至不止适用于编程领域的内容。这些所谓“进阶思维”,有些是我在成为程序员之前就具备的,有些是我后来慢慢学会的。按照惯例,先总体罗列出来:
下面我将逐一阐述。
提到并行,就不得不提“串行”。
我们经常去银行办理业务,有些银行中午也上班,但会关闭一些窗口,如果只留一个开放窗口,客户只能依次办理业务,这种情形就是串行。
中午过后,工作人员陆续回来了,其它窗口陆续开放,客户可以同时办理业务了,这种情形就是并行。
很明显,开放的窗口越多,银行接待客户的效率越高,客户等待的时间越短。用一个专业词语来说,开放窗口越多,并行度越高。
有些大型超市,在购物高峰期会增加收银通道,这也是提高并行度的例子。
以上是生活中的场景,跟写程序无关,现在假定你需要处理1000万条数据,经过初步测试,从头开始逐条处理,预计需要10个小时能处理完。实际情况需要你想办法在1个小时内处理完,此时你应该能从银行或超市得到启发。
逐条处理,相当于只开了一个窗口(程序),如果开10个窗口(程序),速度就可以乘以10倍。
这里是按数据ID的维度并行处理,是否还有其它的维度呢?
故事还是发生在银行,时间到了中午,客户依然在排队,开放窗口只剩下一个了。这时有一些不守规矩的客户,又排了一个队出来,一个窗口,两个队伍,工作人员很为难。
大堂经理看到情况不妙,过来劝说,让这一波不守规矩的人排在原有队伍的后面,成功解围。
一个窗口,两个队伍,对于窗口来说,这种情形就是并发。
从程序的角度看,典型的场景是多个进程同时去修改同一个资源,例如多个进程同时去修改一个计数器。假定业务实例编号的自动生成规则,新编号为已有的最大编号加1,这是否存在并发问题?
简单的想一下,当前最大编号为100,两个用户同时创建实例,如果不考虑并发,两个用户得到的编号都是101,这显然是一个bug。
如何判断代码是否存在并发问题,只需要判断:两个进程同时执行这段代码,是否会出问题。
如何处理并发问题?大堂经理的做法可以带来一些启发。
这次的故事发生在银行大堂经理身上。最近银行做了一次装修,柜台布局有些变化,经常有客户问xx业务在哪办理。经理在回答了10多遍之后,在银行入口处贴了一张告示:xx业务办理,左转倒数第二个柜台。
大堂经理这么做,是因为他不想重复回答同一个问题。
联想到写代码,DRY的通俗表达就是“封装”。有些“懒”的程序员,同样的代码写3遍就去封装了,有些人则比较“勤快”,不厌其烦地写着同样的代码。
看来,懒并不是一个贬义词,程序员应该懒一点。
DRY带来的另一个好处:减少代码冗余。重复10遍的代码,修改起来也需要10次;如果封装起来,修改只需要一次,维护成本急剧下降。
再联想到日常的工作沟通,如果发现自己总是在重复回答一些问题,不妨写一篇QA文档,如果再有人问,直接让他/她去看文档即可。
有一句不知出处的话:代码首先是写给人的,其次才是写给机器的。
软件代码总是被不断维护、重构,这些都是人的工作,所以代码首先是写给人的。
如果一段代码没人维护,只有两种可能:没用的,或无法维护的。
一段代码没人能读懂,或者读懂代码太花时间,还不如重写,即使它的性能很好,也是无用的垃圾。
显然,代码量越少,相应的维护成本就越低,这也是DRY原则提倡的。举个例子,一个方法本来有100行,重构后增加200行,性能提升10%,这时就需要在可维护性与性能之间作出取舍了。
100行的代码清晰易读,性能略差;300行的代码晦涩难懂,性能较好。你选哪一个?
大家都有乘车的经历,前段时间去千岛湖,我乘坐的大巴,总是紧跟着前车行驶,驾驶员假定前面的车不会急刹。
防御式驾驶,是假定他人的驾驶是不可靠的,行人是不可靠的,自身主动去规避风险。为了避免前车急刹导致追尾,需要保持车距。
引申到编程,防御式编程就是自身主动去规避他人的代码错误,例如参数传递错误,返回值错误等。
对于PHP这种弱类型语言,一个方法接受array类型的参数,如果在一开始就检查参数类型,就属于防御式编程。调用他人的API,拿到返回结果的第一时间就去校验是否符合预期,这也是防御式编程。
概括成一句话:防御式编程是自身主动去规避风险,避免他人的错误导致自身程序崩溃。
本文用生活化的语言、场景描述了5种程序员思维:
限于作者水平有限,阐述不准确之处在所难免,欢迎批评指正。