您的位置:首页 > 编程语言

如何阅读他人代码(五)

2010-09-03 01:07 387 查看
原文为繁体中文,地址:http://www.ithome.com.tw/itadm/article.php?c=48168
下文为经过Google翻译过的简体中文版,有翻译不准确的地方,请参照原文一起阅读:

阅读他人的程序码( 5 ) -找到程序入口,再由上而下抽丝剥茧

根据需要决定展开的层数,或展开特定节点,并记录树状结构,然后适度忽略不需要了解的细节─这是一个很重要的态度。因为你不会一次就需要所有的细节,阅读都是有目的的,每次的阅读也许都在探索程序中不同的区域。

探索系统架构的第一步,就是找到程序的入口点。找到入口点后,多半采取由上而下(自上而下)的方式,由最外层的结构,一层一层逐渐探索越来越多的细节。
我们的开发团队曾针对AOL的Winamp的iPod的插件进行阅读及探索,不仅找到入口点,也找出,并理解它最根本的基础架构。从这个入口点,可以往下再展开一层,分别找到三个重要的组成及其意义:
● init ( ) :初始化动作
●quit ( ) :终止化动作
● PluginMessageProc ( ) :以讯息的方式处理程序所必须处理的各种事件

展开的同时,随手记录树状结构

当我们从一个入口点找到三个分支后,可以顺着每个分支再展开一层,所以分别继续阅读的init ,退出,以及PluginMessageProc的内容,并试着再展开一层。阅读的同时,你可以在文件中试着记录展开的树状结构。
●init ( ) :初始化动作
● itunesdb_init_cc ( ) :建立存取iTunes的数据库的同步对象
●初始化资料结构
●初始化的GUI元素
●载入设定
●建立log档
● autoDetectIpod ( ) :侦测的iPod插入的执行绪
●quit ( ) :终止化动作
● itunesdb_del_cc ( ) :终止存取iTunes的数据库的同步对象
●关闭log档
●终止化GUI元素
● PluginMessageProc ( ) :以讯息的方式处理程序所必须面临的各种事件
●执行所连接的iPod的MessageProc ( )

这部分必须要留意几个重点。首先,应该一边阅读,一边记录文件。因为人的记忆力通常有限,对于陌生的事物更是容易遗忘,因此边阅读边记录,是很好的辅助。
再者,因为我们采取由上而下的方式,从一个点再分支出去成为多个点,因此,通常也会以树状的方式记录。除此之外,每次只试着往下探索一层。从的init ( )来看你便会明白。 以下试着摘要的init ( )的内容:

int init() {
itunesdb_init_cc();
currentiPod=NULL;
iPods = new C_ItemList;
…略
conf_file=(char*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GETINIFILE);
m_treeview = GetDlgItem(plugin.hwnd LibraryParent,0x3fd);
//this number is actually magic :)
…略
g_detectAll = GetPrivateProfileInt("ml_ipod", "detectAll",0,conf_file)!=0;
…略
g_log=GetPrivateProfileInt("ml_ipod","log",0,conf_file)!=0;
…略
g_logfile=fopen(g_logfilepath,"a");
…略
autoDetectIpod();
return 0;
}


因为我们只试着多探索一层,而目的是希望发掘出下一层的子动作。所以在的init ( )中看到像“ itunesdb_init_cc ( ) ; ”这样的函数调用动作时,我们知道它是在初始化( )之下的一个独立子动作,所以可以直接将它列入。但是当看到如下的程序行:
currentiPod=NULL;
iPods = new C_ItemList;


我们并不会将它视为的init ( )下的一个独立的子动作。因为好几行程序,才构成一个具有独立抽象意义的子动作。例如以上这两行构成了一个独立的抽象意义,也就是初始化所需的资料结构。

理论上,原来的程序撰写者,有可能撰写一个叫做init_data_structure ( )的函式,包含这两行程序码。这样做可读性更高,然而基于种种理由,原作者并没有这么做。身为阅读者,必须自行解读,将这几行合并成单一个子动作,并赋予它一个独立的意义─ ─初始化资料结构。

无法望文生义的函式,先试着预看一层

对于某些不明作用的函式叫用,不是望其文便能生其义的。当我们看到“ itunesdb_init_cc ( ) ”这个名称时,我们或许能从“ itunesdb_init ”的字眼意识到这个函式和iPod所采用的的iTunes数据库的初始化有关,但“循环”却实在令人费解。为了理解这一层某个子动作的真实意义,有时免不了要往前多看一层。

原来它是用来初始化同步化机制用的对象。作用在于这程序一定是用了某个内部的资料结构来储存的iTunes数据库,而这资料结构有可能被多执行绪存取,所以必须以同步对象(此处是视窗的临界区)加以保护。

所以说,当我们试着以树状的方式,逐一展开每个动作的子动作时,有时必须多看一层,才能真正了解子动作的意义。因为有了这样的动作,我们可以在展开树状结构中,为itunesdb_init_cc ( )附上补充说明:建立存取iTunes的数据库的同步对象。这么一来,当我们在检视自己所写下的树状结构时,就能轻易一目了然的理解每个子动作的真正作用。

根据需要了解的粒度,决定展开的层数

我们究竟需要展开多少层呢?这个问题和阅读程序码时所需的“粒度(粒度) ”有关。如果我们只是需要概括性的了解,那么也许展开两层或三层,就能够对程序有基础的认识。倘若需要更深入的了解,就会需要展开更多的层次才行。

有时候,你并不是一视同仁地针对每个动作,都展开到相同深度的层次。也许,你会基于特殊的需求,专门针对特定的动作展开至深层。例如,我们阅读AOL的Winamp的iPod插件的程序目录,其实是想从中了解究竟应该如何存取的iPod上的iTunes的数据库,使我们能够将MP3播放歌曲或播放清单加至此数据库中,并于的iPod中播放。

当我们层层探索与分解之后,找到了parseIpodDb ( ) ,从函式名称判断它是我们想要的。因为它代表的正是剖析iPod的数据库,正是我们此次阅读的重点,也就达成阅读这程序码的目的。

我们强调一种不同的做法:在阅读程序码时,多半采取由上而下的方式,而本文建议了一种记录阅读的方式,就是试着记录探索追踪时层层展开的树状结构。你可以视自己需要,了解的深入程度,再决定要展开的层数。你更可以依据特殊的需要,只展开某个特定的节点,以探索特定的细目。

适度地忽略不需要了解的细节,是一个很重要的态度,因为你不会一次就需要所有的细节,阅读都是有目的的。每次的阅读也许都在探索程序中不同的区域;而每次探索时,你都可以增补树状结构中的某个子结构。渐渐地,你就会对这个程序更加的了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: