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

如何阅读他人代码(四)

2017年11月17日 ⁄ 综合 ⁄ 共 3073字 ⁄ 字号 评论关闭

原文为繁体中文,地址:http://www.ithome.com.tw/itadm/article.php?c=48058

下文为经过Google翻译过的简体中文版,有翻译不准确的地方,请参照原文一起阅读:


阅读他人的程序码( 4 ) -望文生义,进而推敲组件的作用

先建立系统的架构性认识,然后透过名称及命名惯例,就可以推测出各组件的作用。例如:当AOLWinamp尝试着初始化一个插件时,它会呼叫这个结构中的初始化函式,以便让每个插件程序有机会初始化自己。当AOLWinamp打算结束自己或结束某个插件的执行时,便会呼叫退出函式。

在阅读程序码的细节之前,我们应先试着捕捉系统的运作情境。在采取由上至下的方式时,系统性的架构是最顶端的层次,而系统的运作情境,则是在它之下的另一个层次。

好的说明文件难求,拼凑故事的能力很重要 

有些系统提供良善的说明文件,也许还利用UML的充分描述系统的运作情境。那么对于阅读者来说,从系统的分析及设计文件着手,便是快速了解系统运作情境的一个途径。
但是,并不是每个软体专案都伴随着良好的系统文件,而许多极具价值的开放原始码专案,也时常不具备此类的文件。对此,阅读者必须尝试自行捕捉,并适度地记录捕捉到的运作情境。 

我喜欢将系统的运作情境,比拟成系统会上演的故事情节。在阅读细节性质的程序码前,先知道系统究竟会发生那些故事,是必备的基本功课。你可以利用熟悉或者自己发明的表示工具,描述你所找到的情境。甚至可以只利用简单的列表,直接将它们列出。只要能够达到记录的目的,对程序码阅读来说,都能够提供帮助。或者,你也可以利用基于UML中的类别图,合作图,循序图之类的表示方法,做出更详细的描述。 
当你能够列出系统可能会有的情境,表示你对系统所具备的功能,以及在各种情况下的反应,都具备概括性的认识。以此为基础,便可在任何需要的时候,钻进细节处深入了解。 

探索架构的第一步─ ─找到程序的入口 

在之前,我们在一个开发专案中,曾经需要将系统所得到的的MP3音讯档,放至iPod的这个极受欢迎的播放设备中。 

虽然iPod的本身也可以做为可移动式的储存设备,但并不是单纯地将MP3播放档案放到中的iPod ,就可以让iPod的播放器认得这个档案,甚至能够加以播放。 
这是因为iPod利用一个特殊的档案结构( iTunes的数据库) ,记录播放器中可供播放的乐曲,播放清单以及乐曲资讯(例如专辑名称,乐曲长度,演唱者等) 。为了了解并且试着重复使用既有的程序码,我们找到了一个AOLWinampiPod的外挂程序(插件) 。 

AOLWinamp是个人电脑上极受欢迎的播放软体,而我们找到的外挂程序,能让的软件直接显示连接至电脑的的iPod中的歌曲资讯,并且允许的软件直接播放。 

我们追踪与阅读这个外挂程序的思路及步骤如下,首先,我们要先了解外挂程序的系统架构。很明显的,大概浏览过原始码后,我们注意到它依循着AOLWinamp为插件程序所制定的规范,也就是说,它是实作成的Windows上的DLL的,并且透过一个叫做winampGetMediaLibraryPluginDLL的函式,提供一个名为winampMediaLibraryPlugin的结构。 
当我们不清楚系统的架构究竟为何时,我们会试着探索,而第一步,便是找到程序的入口。如何找到呢?这会依程序的性质不同而有所差别。 
对一个本身就是可独立执行的程序来说,我们会找启动程序的主要函式,例如对的C / C + +来说就是主要( ,而对爪哇来说,便是静无效的main 。在找到入口后,再逐一追踪,摸索出系统的架构。 
但有时,我们所欲阅读的程序码是类别库或函式库,它只是用来提供多个类别或函式供用户端程序(客户程序)使用,本身并不具单一入口,此类的程序码具有多重的入口 ─每个允许用户端程序呼叫的函式或类别,都是它可能的入口。 

例如,对AOLWinampiPod的插件来说,它是一个动态链接库形式的函式库,所以当我们想了解它的架构时,必须要先找出它对外提供的函式,而对的WindowsDLL来说,对外提供的函式,皆会以dllexport这个关键字来修饰。所以,不论是利用grep按或gtags之类的工具,我们可以很快从原始码中,找到它只有一个DLL的函式(这对我们而言,真是一个好消息) ,而这个函式便是上述的winampGetMediaLibraryPlugin 。 

系统多会采用相同的架构处理插件程序 

如果经验不够的话,也许无法直接猜出这个函式的作用。 
不过,如果你是个有经验的程序人,多半能从函式所回传的结构,猜出这个函式实际的用途。而事实上,当你已经知道它是一个插件程序时,就应该要明白,它可能采用的,就是许多系统都采用的相同架构处理插件程序。 

当一个系统采用所谓插件形式的架构时,它通常不会知道它的插件究竟会怎么实作,实作什么功能。它只会规范插件程序需要满足某个特定接口。当系统初始化时,所有的插件都可以依循相同的方式,向系统注册,合法宣示自己的存在。 

虽然系统并不确切知道插件会有什么行为展现,但是因为它制定了一个标准的接口,所以系统仍然可以预期每个插件能够处理的动作类型。这些动作具体上怎么执行,对系统来说并不重要。这也正是对象导向程序设计中的多型观念。 

随着实务经验,归纳常见的架构模式 

我想表达的重点,是当你涉世越深”之后,所接触的架构越多,就越能触类旁通。只需要瞧上几眼,就能明白系统所用的架构,自然就能够直接联想到其中可能存在的角色,以及角色间的关系。 

像上述的插件程序手法,时常可以在许多允许外挂程序码的系统中看到。所以,有经验的阅读者,多半能够立即反应,知道像这样的系统的软件,应该是让每个插件程序,都写成DLL的函式库。 

而每个插件的DLL的函式库中,都必须提供winampGetMediaLibraryPlugin )这个函式(如果你熟悉的Windows的程序设计,你会知道这是利用加载( )和GetProcAddress )来达成的一种多型手法) 。如果你熟悉设计模式,你更会知道这是简单工厂方法这个设计模式的运用。 
winampGetMediaLibraryPlugin )所回传的winampMediaLibraryPlugin结构,正好就描述了每个AOL
的Winamp插件的实作内容。 

善用名称可加速了解 

利用gtags这个工具,我们立即发现,这个插件它所定义的初始化,退出, PluginMessageProc这三个名称,都是函式名称。这暗示在多型的作用下,它们都是在某些时间点,会由AOL的Winamp核心本体呼叫的函式。 

名称及命名惯例是很重要的。看到初始化,我们会知道它的作用多半是进行初始化的动作,而退出大概就是结束时处理函式,而PluginMessageProc多半就是各种讯息的处理例程(过程通常是程序的简写,所以PluginMessageProc意指插件讯息程序)了。 

望文生义很重要,我们看到函式的名称,就可以猜想到它所代表的作用,例如:当AOLWinamp尝试着初始化一个插件时,它会呼叫这个结构中的初始化函式,以便让每个插件程序有机会初始化自己;AOLWinamp打算结束自己或结束某个插件的执行时,便会呼叫退出函式。当

AOL的Winamp要和插件程序沟通时,它会发送各种不同的讯息至插件,而插件程序必须对此做出回应。 

我们甚至不需要检视这几个函式的内容,就可以做出推测,而这样的假设,事实上也是正确的。

 

 

 

抱歉!评论已关闭.