2019 年就这么结束了,2020 年也来临了,虽然我曾对过去 2019 年做了一份总结,但是认真的来说,其实我对自己的 2019 年的收获并不太满意,一个主要的原因是计划好好研读的几个开源项目的源码都没有去做。好在,2020 新的一年,不再像 2019 年创业一般忙碌,终于可以静下心来认真去把这些未完成的计划好好做完。
其实,我一直想找个机会和我的读者,好好讨论一下阅读开源项目源码这个话题的,我这里观点无任何含糊或者模棱两可,我旗帜鲜明的亮出我的观点——想在技术上有所造诣或者想成为某一技术领域的专家的同学一定要认认真真的研读几个开源软件的源码。下面我会具体来展开说下这个问题。
大家都知道,时下"知识付费"这个词非常火热,各大平台各个领域都推出了许多基于知识付费的课程,有图文版、语音版和视频版(包括在线实时教育直播)。当然,知识付费是一个好东西。众所周知,互联网信息的特点是信息量大、有用信息少、信息质量良莠不齐,各大平台推出的各种付费课程,精心制作,用心分类和梳理,读者只要花费一定的费用,就能省去大量搜索、查找和遴选信息的时间,直接专注于获得相关知识本身。
在各类知识付费课程中,有一类课程是介绍业界或者大家平常工作中用到的一些开源软件的原理的,进一步说,有的是分析这类软件的源码的,如 nginx、netty、Spring Boot。
我个人觉得,虽然你可以购买一些这样那样的开源软件的教程或者图书(包括电子书)去学习,但一定不要以这些学习材料为主要的学习这些开源软件的方法和途径,有机会的话,或者说如果你要学习的开源软件所使用的开发语言正好是你熟悉或者使用的编程语言,那么你应该尽量多去以阅读这些开源项目的源码本身为主。举个例子,如果你是 C/C++ 后端开发者,那么像 redis、nginx(它们都是使用 C 编写的)这样的开源项目的源码你应该认真的去研读一下;如果你是做 Windows C/C++ 客户端或者一名 QT 客户端开发人员,那么像 MFC、DUILIB、金山卫士等源码,你可以拿来读一读;如果你是 Java 程序员,netty、Spring 等源码是你进阶路上必须迈过去的一关。
为什么建议以阅读相关源码为主,而不是其他相关教程呢?
首先,任何其他相关教程介绍的内容都是基于这个软件的源码实现创作出来的,虽然能帮助你快速理解一些东西,但是不同的教程作者在阅读同样一份代码时的注意点和侧重点不一样,加上如果作者在某些地方有理解偏差的,这种偏差会被引入你所学习的教程或者图书里面,也就是说,你学习的这些东西其实不是第一手的,而是经过别人加工或者理解意译过的,在这个过程中如果别人理解有偏差,那么你或多或少的会受一点影响。所以,为了"不受制于人”,亲自去阅读一些源码时非常有必要的。
其次,如果你按照别人的教程大纲,那么你的学习该软件的开源项目时,可能会受限于别人的视野和侧重点,通俗的说,假设一个开源项目其可以学习和借鉴的内容有 A、B、C、D、E 五个大的点,别人的教程可能只写了 A、B、C、D 四个点,如果你只局限于别人的教程,你就错过 E 这个点了。
这里可以给读者讲一个具体的例子。我最初开始走上工作岗位时做的是 C/C++ 客户端开发,我无意中找到了一份完整的电驴源码,但是开始阅读这份代码比较吃力,于是我就在网上找相关的电驴源码分析教程来看。但是呢,网上的这方面的教程都是关于电驴的网络通信模块和通信协议介绍的,很多做客户端的读者是知道的,做客户端开发很大一部分工作是在开发 UI 界面方面的逻辑和布局,其实电驴源码中关于界面设计逻辑写的也是很精彩的,也非常值得当时的我去借鉴和学习。如果我只按照网上的教程去学习,那么就错过这方面的学习了。也就是同样一份电驴源码,不同的学习者汲取的其源码中的营养成分是不一样的。需要电驴源码的同学可以在公众号后台回复关键字【电驴源码】获取下载链接。
这应该是很多读者想知道的问题,先讨论几种老生常谈的阅读源码的方式。
第一种方式就是所谓的精读和粗读。很多读者应该听说过这种所谓的阅读源代码的方式,有些人认为有些源码只需要搞清楚其主要结构和流程就可以了,而另外一些源码需要逐行认真去研读其某个或者某几个模块的源码。或者,只阅读自己感兴趣或者需要的模块。
第二种方式,说的是先熟悉代码的整体结构,再去依次搞清楚各个模块的代码细节。
第三种方式是所谓的调试法,通过开源项目的一个或几个典型的流程,去调试跟踪信息流,然后逐步搞清楚整个项目的结构。
以上三种方式都是不错的阅读源码的方式,读者可以根据自己的水平、目的和阶段去使用。但是,我这里想说的并不是这些东西。
我个人觉得,一个技术人员如果想通过源码去提高自己,应该以一种"闲登小阁看新晴"的心境去阅读源码,这也许是在某个节假日的清晨,某个下过雨的午后,某个夜黑人静的深夜。看源码尤其是看高质量源码本来就是一种享受,像品茗。闲暇时间去细细品味一些开源软件的源码,和锻炼身体一样,都是人生中重要不紧急的事情,这类事情做的越多,坚持的越久,越能提高你的人生厚度。虽然阅读源码的最终目的是功利性的,但是阅读源码的心态不建议是功利性的,喜欢做一件事本身的过程,比把这件事做好的目标更快乐。
我从学生时代开始,就喜欢看一些开源软件的源码,当然,从现在的标准来看,看的很多源码都不是"高质量"的,择其善者而从之其不善者而改之,不是吗?有些源码可以学习其架构、结构设计,有些源码则可以学习其细节设计(如变量命名、编码风格等)。
看过的这些源码对我的技术视野影响很大。我上大学的时候,迷恋 Flash 编程,当时非常崇拜 Flash 界的两位前辈——鼠标炸弹(https://mousebomb.org/)和寂寞火山(现在已成币圈有名的大佬),另外还有淘沙网的沙子。多年后再看他们的代码可能质量没有那么高,但是我从他们开源出来的代码中学到了很多东西。举个例子,我喜欢在一些成对结束的花括号后面加上明显的成对结束的注释就是从沙子的代码那里学来的。虽然,现在的 IDE 会清楚的标示出来各个花括号的范围,但是这种注释风格在某些时候大大方便了代码阅读和 review。
//实例
class A
{
public:
void someFunc()
{
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
//some codes...
}// end inner-for-loop
}// end outer-for-loop
}// end method someFunc
}; // end class A
很多人阅读源码存在以下不当的习惯或者认知方式:
- 很多人阅读源码其实是随波逐流的,今天有人推荐阅读 A 项目的源码,他就去阅读 A 项目的源码,明天有人推荐阅读 B 项目的源码,他就去阅读 B 项目的源码。天下源码何其多呀,找到自己感兴趣的或者对自己有用的,不要随波逐流,适合别人的不一定适合你。
- 有些人阅读源码非要满足了"天时地利人和"才会去阅读。例如,有些人觉得自己不懂网络编程,所以就不方便阅读 nginx 的源码,有些人听别人说阅读某个项目的源码前必须先做 XX,而自己又不熟悉 XX,所以就放弃了阅读该项目。或者觉得当下时机不适合阅读某个项目的源码。再或者在阅读几个源码文件或者模块的代码时,因为看不懂就放弃了。其实这些做法都不可取,任何源码和你刚进入公司去接触一个新的业务项目的源码一样,只要慢慢熟悉,在这过程中针对性的补缺补差,坚持下来总会有所收获的。尤其是对那些走上工作岗位的读者来说,成年人的世界事情那么多,此生余年应该不会再有什么时间可以同时满足"天时地利人和"了吧。
- 代码的质量高低是相对的,不要因为一些项目的源码质量低或者不符合你的 style 就放弃。大多数完整的项目代码总有其可取之处,要学会吸取其有用之处。举个例子,很多做 Windows C++ 客户端开发的同学,应该会在网络的各个地方看到很多人抨击 MFC 的,然后一堆建议不要学习 MFC 的。从我个人的经历和感受来看,MFC 的源码还是很值得做 Windows C++ 客户端的同学学习的,尤其是其设计思想。当然,MFC 之所以被很多人抨击,是因为其臃肿笨拙,这有很多历史原因,MFC 不仅封装 Windows 界面逻辑那一套,同时实现了一套常用软件文档、视图模型的程序框架结构,同时自己实现了一套 STL 相关功能,以及其他一些常用功能(如对象的序列化和反序列化)。这些设计思想都被后来的各种软件框架借鉴和继承,例如 QT 和 Java 中的序列化和反序列化。一个开发者如果想成为架构师,其心中一定要对某个场景有一套可行的技术方案,如果你经验不足或者水平不够,拿不出来这样的方案,那就去借鉴和学习这些开源的软件。而不是只会抨击这些软件源码的缺点,而自己又无更好的解决方案。旧的方案虽然不好,但是我们需要去学习、熟悉,只有熟悉了之后,我们才能基于其去改造和优化。
最后,阅读源码不是做给别人看的,如果你之前从未意识到阅读各种大大小小的开源项目的源码的重要性,2020 年循序渐进,少买点在线课程,少囤点书,多读些源码吧。