现在的位置: 首页 > 综合 > 正文

如何阅读他人的程序代码【转载】

2013年08月05日 ⁄ 综合 ⁄ 共 5868字 ⁄ 字号 评论关闭

    近日,在互联网上游荡,偶然发现一篇曾经的文章,是关于如何阅读他人程序代码的,阅后颇为受益,于是乎重新整理了一下格式,将此文转载如下:

 

如何阅读他人的程序代码

/王建兴

 

作者简介: 王建兴,清华大学资讯工程系的博士研究生,研究兴趣包括计算机网络、点对点网络、分布式网络管理、以及行动式代理人,专长则是Internet应用系统的开发。曾参与过的开发项目性质十分广泛而且不同,从ERPPC GameP2P网络电话都在他的涉猎范围之内。

  

*  一、读懂程序代码,使心法皆为我所用

程序代码是别人写的,只有原作者才真的了解程序代码的用途及涵义。许多程序人心里都有一种不自觉的恐惧感,深怕被迫去碰触其他人所写的程序代码。但是,与其抗拒接收别人的程序代码,不如彻底了解相关的语言和惯例,当成是培养自我实力的基石。

 

对大多数的程序人来说,撰写程序代码或许是令人开心的一件事情,但我相信,有更多人视阅读他人所写成的程序代码为畏途。许多人宁可自己重新写过一遍程序代码,也不愿意接收别人的程序代码,进而修正错误、维护它们、甚至加强功能。

这其中的关键究竟在何处呢?若是一语道破,其实也很简单,程序代码是别人写的,只有原作者才真的了解程序代码的用途及涵义。许多程序人心里都有一种不自觉的恐惧感,深怕被迫去碰触其他人所写的程序代码。这是来自于人类内心深处对于陌生事物的原始恐惧。

 

读懂别人写的程序代码,让你收获满满

不过,基于许多现实的原因,程序人时常受迫要去接收别人的程序代码。例如,同事离职了,必须接手他遗留下来的工作;也有可能你是刚进部门的菜鸟,而同事经验值够了、升级了,风水轮流转,一代菜鸟换菜鸟。甚至,你的公司所承接的项目,必须接手或是整合客户前一个厂商所遗留下来的系统,你们手上只有那套系统的原始码(运气好时,还有数量不等的文件)。

诸如此类的故事,其实时常在程序人身边或身上持续上演着。许多程序人都将接手他人的程序代码,当做一件悲惨的事情。每个人都不想接手别人所撰写的程序代码,因为不想花时间去探索,宁可将生产力花在产生新的程序代码,而不是耗费在了解这些程序代码上。

很遗憾的是,上述的情况对程序人来说很难避免。我们总是必须碰触到其他人所写成的程序代码,甚至必须了解它、加以修改。对于这项需求,在现今开放原始码的风气如此盛行的今日,正如之前的「程序设计2.0」文中所提到的,你可以透过开放原始码学习到新的技术、学习到高手的架构设计,大幅提高学习的效率及效果。你甚至可以直接自开放原始码项目中抽取、提炼出自己所需的程序代码,站在巨人的肩膀上,直接由彼端获得所需的生产力。从这个观点来看,读懂别人所写的程序代码,就不再只是从负面观点的「被迫接收」,而是极具正面价值的「汲取养份」。

 

先了解系统架构与行为模式,再细读

倘若撰写程序代码是程序人的重要技艺之一,那么读懂别人的程序代码、接着加以修改,也势必是另一个重要的技艺。

如果你不能熟悉这项工作,不仅在遭逢你所不愿面对的局面时,无法解决眼前接手他人程序代码的难题,更重要的是,当你看着眼前现成的程序代码,却不知如何从中撷取自己所需,导致最后只能入宝山空手回,望之兴叹。

接触他人的程序代码,大致上可以分为三种程度:一、了解,二、修改、扩充,三、抽取、提炼。

了解别人的程序代码是最基础的工作,倘若不能了解自己要处理的程序代码,就甭论修改或扩充,更不可能去芜存菁,从中萃取出自己所需,回收再利用别人所撰写的程序代码。

虽说是「阅读」,但程序代码并不像文章或小说一样,透过这种做法,便能够获得一定程度的了解。阅读文章或小说时,几乎都是循序地阅读,你只消翻开第一页,一行行阅读下去即可。但是,有许多程序人在试着阅读其他人的程序代码时,却往往有不知如何读起的困难。

或许找到系统的第一页(也就是程序代码执行的启始点)并不难,但是复杂度高的系统,有时十分庞大,有时千头万绪。

从程序代码的启始点开始读起,一来要循序读完所有的程序代码旷日费时,二来透过这种方式来了解系统,很难在脑中构建出系统的面貌,进而了解到系统真正的行为。所以,阅读程序代码的重点,不在于读完每一行程序代码,而是在于有效率地透过探索及阅读,从而了解系统的架构及行为模式。以便在你需要了解任何片段的细节实作时,能够很快在脑上对映到具体的程序代码位置,直到那一刻,才是细读的时机。

 

熟悉沟通语言与惯例用语

不论如何,有些基本的准备,是阅读他人程序代码时必须要有的。

首先,你最好得了解程序代码写成的程序语言。想要读懂法文写成的小说,总不能连法文都不懂吧。有些情况则很特殊。我们虽然不懂该程序代码撰写所用的语言,但是因为现代语言的高阶化,而且流行的程序语言多半都是血统相近,所以即使不那么熟悉,有时也可勉力为之。

除了认识所用语言之外,再来就是要先确认程序代码所用的命名惯例(naming convention)。了解命名惯例很重要,不同的程序人或开发团队,差异可能很大。

这命名惯例涵盖的范围通常包括了变量的名称、函式的名称、类别(如果是面向对象的话)的名称、原始码档案、甚至是项目建构目录的名称。倘若使用了像设计模式之类的方法,这些名称更有一些具体的表述方式。

命名惯例有点像是程序人在程序语言之上,另行建构的一组沟通行话。程序人会透过共通约束、遵守的命名惯例,来表达一些较高阶的概念。例如,有名的匈牙利式命名法,便将变量名称以属性、型别、说明合并在一起描述。对程序人来说,这种方式能够提供更丰富的信息,以了解该变量的作用及性质。

对程序代码阅读来说,熟悉这个做法之所以重要,是因为当你了解整个系统所采用的惯例时,你便能试着以他们所共同操用的语汇来进行理解。倘若,不能了解其所用的惯例,那么这些额外提供的信息,就无法为你所用。像以设计模式写成的程序代码,同样处处充满着模式的名称,诸如:FactoryFacadeProxy等等。以这些名称指涉的类别,也直接透过名称,表达了它们自身的作用。对于懂得这命名惯例的读者来说,不需要深入探索,也能很快捕捉到这些类别的意义。

当你拿到一套必须阅读的程序代码时,最好先取得命名惯例的说明文件。然而,并不是每套程序代码都附有此类的说明文件。另一个方式,就是自己到程序代码中,大略浏览一遍,有经验的程序人可以轻易发掘出该系统所用的命名惯例。

常见的命名方式不脱那几类,这时候经验就很重要,倘若你知道的惯例越多,就越能轻易识别他人所用的惯例。如果运气很糟,程序代码所用的惯例是前所未见的,那么你也得花点时间归纳,凭自己的力量找出这程序代码命名上的规则。

 

掌握程序代码撰写者的心态与习惯

大多数的程序代码,基本上都依循一致的命名惯例。不过运气更差的时候,一套系统中可能会充斥着多套命名惯例。这有可能是因为开发团队由多组人马所构成,每组人马都有不同的文化,而在项目开发管理又没有管控得宜所造成。最糟的情况,程序代码完全没有明显的惯例可言,这时候阅读的难度就更高了。

想要阅读程序代码,得先试着体会程序代码作者的「心」。想要这么做,就得多了解对方所使用的语言,以及惯常运用的语汇。在下一回中,我们将继续探讨阅读程序代码的相关议题。

 

 

 

*  二、摸清架构,便可轻松掌握全貌

在本文中,我们的重点放在:要了解一个系统,最好是采取由上至下的方式。先试着捕捉系统架构性的观念,不要过早钻进细节,因为那通常对于你了解全貌,没有多大的帮助。阅读程序代码不需要从第一行读起,我们的目的并不是在于读遍每一段程序代码。

 

基于许多原因,程序人需要阅读其他人所写成的程序代码。而对程序设计2.0时代的程序人来说,最正面的价值在于,能读懂别人程序的人,才有能力从中萃取自己所需的程序,藉以提高生产力。

 

阅读程序代码的目的,在于了解全貌而非细节

想要读懂别人程序代码的根本基础,便是了解对方所用的程序语言及命名惯例。有了这个基础之后,才算是具备了基本的阅读能力。正如我之前提到的──想要读懂法文写成的小说,总不能连法文都不懂吧。阅读程序代码和阅读文学作品,都需要了解撰写所用的语言及作者习用的语汇。

但我们在阅读文学作品通常是采循序的方式,也就是从第一页开始,一行一行地读下去,依循作者为你铺陈的步调,逐渐进到他为你准备好的世界里。

阅读程序代码却大大不同。我们很少从第一行开始读起,因为除非它是很简单的单线程程序,否则很少这么做。因为要是这么做,就很难了解整个系统的全貌。

是的,我们这边提到了一个重点,阅读程序代码的目的在于了解系统的全貌,而不是在于只是为了地毯式的读遍每一段程序代码。

就拿面向对象程序语言所写成的系统来说,整个系统被拆解、分析成为一个个独立的类别。阅读个别类别的程序代码,或许可以明白每项类别对象个别的行为。但对于各类别对象之间如何交互影响、如何协同工作,又很容易陷入盲人摸象的困境。这是因为各类别的程序代码,只描述个别对象的行为,而片段的阅读就只能造就片面的认识。

 

由上而下厘清架构后,便可轻易理解组成关系

如果你想要跳脱困境,不想浪费大量时间阅读程序代码,却始终只能捕捉到对系统片段认识,就必须转换到另一种观点来看待系统。从个别的类别行为着手,是由下至上(Bottom-Up)的方法;在阅读程序代码时,却应该先采由上至下(Top-Down)的方式。对程序代码的阅读来说,由上至下意谓着,你得先了解整个系统架构。

系统的架构是整个系统的骨干、支柱。它表现出系统最突出的特征。知道系统架构究竟属于那一种类型,通常大大有益于了解系统的个别组成之间的静态及动态关系。

有些系统因为所用的技术或框架的关系,决定了最上层的架构。例如,采用Java Servlet/JSP技术的应用系统,最外层的架构便是以J2EE(或起码J2EE中的Web Container)为根本。

使用Java Servlet/JSP技术时,决定了某些组成之间的关系。例如,Web Container依据web.xml的内容加载所有的ServletsListeners、以及Filters。每当Context发生事件(例如初始化)时,它便会通知Listener类别。每当它收到来自客户端的请求时,便会依循设定的所有Filter Chain,让每个Filter都有机会检查并处理此一请求,最后再将请求导至用来处理该请求的Servlet

当我们明白某个系统采用这样的架构时,便可以很容易地知道各个组成之间的关系。即使我们还不知道究竟有多少Servlets,但我们会知道,每当收到一个请求时,总是会有个相对应的Servlet来处理它。当想要关注某个请求如何处理时,我应该去找出这个请求对应的Servlet

 

了解架构,必须要加上层次感

同样的,以Java写成的Web应用程序中,也许会应用诸如Struts之类的MVC框架,以及像Hibernate这样的数据存取框架。它们都可以视为最主要的架构下的较次级架构。而各个应用系统,甚至有可能在StrutsHibernate之下,建立自有的更次级的架构。

也就是说,当我们谈到「架构」这样的观念时,必须要有层次感。而不论是那一层级的架构,都会定义出各自的角色,以及角色间的关系。对阅读者来说,相较于直接切入最细微的单一角色行为,不如了解某个特定的架构中,究竟存在多少角色,以及这些角色之间的互动模式,比较能够帮助我们了解整个系统的运作方式。

这是一个很重要的关键,当你试着进到最细节处之前,应该先试着找出参与的角色,及他们之间的关系。例如,对事件驱动式的架构而言,有3个很重要的角色。一个是事件处理的分派器(Event Dispatcher)、一个是事件产生者(Event Generator)、另一个则是事件处理器(Event Handler)。

事件产生器产生事件,并送至事件分派器,而事件分派器负责找出各事件相对应的事件处理器,并且转交该事件,并命令事件处理器加以处理。像WindowsGUI应用程序,便是采用事件驱动式的架构。

当你知道此类的应用程序皆为事件驱动式的架构时,你便可以进一步得知,在这样的架构下会有3种主要的角色。虽然也许还不清楚整个系统中,究竟会需要处理多少事件的类型,但对你而言,已经建立了对系统全貌最概观的认识。

虽然你还不清楚所有的细节,但诸如确切会有那些事件类型之类的信息,在此刻还不重要──不要忘了,我们采取的是由上而下的方式,要先摸清楚主建筑结构,至于壁纸的花色怎么处理,那是到了尾声时才会做的事。

 

探索架构的第一件事:找出系统如何初始化

有经验的程序人,对于时常被运用的架构都很熟悉。常常只需要瞧上几眼,就能明白一个系统所用的架构,自然就能够直接联想到其中会存在的角色,以及角色间的关系。

然而,并不是每个系统所用的架构,都是大众所熟悉,或是一眼能够望穿的。这时候,你需要探索。目标同样要放在界定其中的角色、以及角色间的静态、动态关系。

不论某个系统所采用的架构是否为大部分人所熟知的,在试着探索一个系统的长相时,我们应该找出来几个答案,了解在它所用的架构下,下列这件事是如何被完成的:一、系统如何初始化,二、与这个系统相接的其他系统(或用户)有那些,而相接的接口又是什么;三、系统如何反应各种事件,四、系统如何处理各种异常及错误。

系统如何初始化是很重要的一件事,因为初始化是为了接下来的所有事物而做的准备。从初始化的方式、内容,能知道系统做了什么准备,对于系统会有什么行为展现,也就能得窥一二了。

之所以要了解与系统相接的其他系统(或用户),为的是要界定出系统的边界。其他的系统可能会提供输入给我们所探索的系统,也可能接收来自这系统的输出,了解这边界所在,才能确定系统的外观。

而系统所反应的事件类型、以及如何反应,基本上就代表着系统本身的主要行为模式。最后,我们必须了解系统处理异常及错误的方式,这同样也是系统的重要行为,但容易被忽略。

之前,我们提到必须先具备一个系统的语言基础,才能够进一步加以阅读,而在本文中,我们的重点放在:要了解一个系统,最好是采取由上至下的方式。先试着捕捉系统架构性的观念,不要过早钻进细节,因为那通常对于你了解全貌,没有多大的帮助。

 

 

 

*  三、优质工具在手,读懂程序非难事

系统的复杂度往往超过人脑的负荷。阅读程序代码的时候,你会需要更多工具提供协助。使用好的集成开发环境(IDE)或文本编辑器,就能提供最基本的帮助。

 

阅读程序代码的动作,可以是很原始的,利用最简单的文本编辑器,逐一开启原始码,然后凭借着一己的组织能力,在不同的程序代码间跳跃,

抱歉!评论已关闭.