包的实现、包设计时的注意点和常见问题

包的实现、包设计时的注意点和常见问题
我们的程序中使用了大量的包,我把我觉得在设计包时需要注意的几点内容列出来,算作小小的提醒:
  Delphi的bpl与宿主和其他的bpl共享接口部分的声明,共享相同的单元,所以自然能共享内存管理器。因为Delphi在编译bpl时在其Exports段导出了所有的接口声明部分和初始化/终止化节,这样一旦一个bpl需要其他的bpl时Delphi也可以知道其他bpl包含了什么,提供了什么,也就可以在Imports段Import使用到的其他bpl的全局变量,函数,静态对象,初始化/终止化节。因此,使用包时,宿主和所有的包都对每个全局的内容拥有相同的引用,也都使用了同一份内存管理器,也就可以自由的使用对象和字符串等变量,且速度也会快于普通的dll,所以bpl与普通的dll相比才会有很多优点,使用bpl也使得开发可扩展性的系统变得容易。

  但是也是因为bpl的这个机制,导致了一些缺点,我们在设计包时应该尽量绕开这些缺点。

1. 包中不能包含同名文件

a)  如果包A包含一个名为U的单元,而包B也包含一个名为U的单元,则包B无法被编译。

在给单元取名时必须注意使用一些意义明确又不容易重名的名字,建议在包单元的名称中包含包特定的字符串以减小重名的概率,这也就是为什么第三方组件大多会在单元名前加上两到三个同样的字母的前缀(如RX,JCL等等)的原因。

还有一种方法就是使用弱引用。在我另外一封邮件中提及$WEAKPACKAGEUNIT这个指示字,如给不常用到的单元指明弱引用,则命名冲突的概率也会大大降低。

b)  如果包A引用了单元U,包B也需要引用单元U,但包B又不想引用单元A,则无法实现包B。

这种情况往往是因为设计包结构时设计不当引起的。遇到这种情况时,需要提取出U单元并把它放在一个新的独立的包中,让A和B两个包的依赖都转移到新的包中。但是如果单元U有需要引用到包A的内容,则又会发生重复引用的情形,所以在设计包时尽量保证公用的包是轻量级的,也就是尽量少依赖于太多其他的内容,可以只抽取出必须的内容,且尽量独立化,尽量让整个包层次的关系扁平化。

2. 某个底层包一旦被修改,则可能所有的使用此包的工程都要重新编译

由于包之间都是互相了解的,所以一旦某个公共的包需要修改,则所有设计到的包都会需要重新编译发布,要预防这种事情的发生,最好的方法就是在设计包结构时就能设计好。当这种变更的确是难以避免时,我们也可以只修改单元的实现部分代码而不改动单元的接口部分,这样因为上面提及的包实现的机制,我们也可以不需要重新编译发布使用到此包的工程,很多时候是可以通过某些技巧只修改实现而不修改声明就能达到目的的。

3. 如果包设计不当,可能导致编译高层包时速度慢且编译的文件可能不能替换

包的层次不当加上包的选项不当,就可以导致有时候某些单元的一点点变更也会让编译速度变慢,有时还会影响编译出来的文件。

在设计时,除了注意层次之外,还要注意区分设计时刻包和运行时刻包,也要注意选择编译选项。将组件包分为rtl和dcl两类是很有必要的,这样分层之后,rtl包用于发布,dcl包用于IDE环境,既减少了系统的冗余,也会适度的减小系统的大小,也会减少系统的依赖(dcl包往往需要一些额外的与运行时刻无关的包支持),也会轻微的提高性能。而某些底层的包,根本不会变化的,一定要确保是$IMPLICITBUILD OFF的,这样可以避免不必要的重复编译(也可降低Delphi编译出问题的几率),其实即使是对于比较高层的组件,不让Delphi自动重新编译也是可以的,因为组件发生了修改,就是需要明确的要求相关工程重新编译的。


  此外,也把一些经常会导致Delphi出些问题的状况提及一下,有时候遇到这种问题的确是很头痛的。

1. 搜索路径重复

所谓重复不只是字符串的重复,也可能存在着类似$(DELPHI)与C:Program FilesBorlandDelphi7的重复;出现此情形可能会使Delphi在编译时出现一些内部错误。注意,在工程选项的目录中也要尽量不要和环境设置的目录有重复。

2. 搜索路径中存在同名文件

如A目录下存在单元U,B目录下也存在单元U,当编译的工程需要引用到U时(有时候不是直接引用)就会出现一些问题,常见的问题如断点位置偏移,无法设置断点,编译变得很慢,甚至编译出错(如zizii发现我们的源代码中DEC系列组件就是重复的,这导致编译总是出现问题),如果两个单元只有轻微的差别时甚至会出现了编译正常,可编译出来的程序却总是与预期的运行不一致的情形。

我们也可以利用同名文件的特性来实现一些特殊的事情,如发现VCL中存在某些BUG,我们除了可以做一个修正BUG的替换外,还可以复制一份VCL的源代码到工程目录,在此复制文件修正BUG,只要不涉及接口部分的变化或添加删除初始化/终止化节,就可以编译并得到正确的结果了。

3. 条件指示字过于混乱

条件指示字过于混乱一般会影响到Delphi的代码提示(有时候也会影响Delphi的断点等),一些专家插件的源代码分析(一旦插件出错,很可能导致Delphi不稳定),源代码格式化工具的格式结果。也会使得代码难以阅读。

4. 专家插件或设计时刻包冲突

这会导致Delphi不稳定,如果专家导致Delphi内部出错,很可能除了影响稳定性之外,也会引起编译问题。建议在开发环境使用一些轻量级的稳定的插件(尽量少用CodeRush之类的专家,太影响IDE了),装多个专家时,需要禁用到功能重复的模块。合适的插件组合会是编码变得方便且愉快,否则会大大影响编码。

5. 有代码实现的include文件

除非万不得已要尽量避免这种做法,因为它会影响调试和代码的可阅读性。

posted @ 2011-04-23 18:22  gxch  阅读(502)  评论(0编辑  收藏  举报