Delphi中模块化编程的最佳方法

12

这是我在这里开始的讨论的延续。由于我在这个领域没有经验,我想找到最好的方法将Delphi源代码模块化。感谢您提出的所有建议。

让我发表一下我已经写过的内容:在这里

我们公司开发的软件包括100多个模块(大多数是不同设备的驱动程序)。它们中的大多数都共享相同的代码 - 在大多数情况下是类。问题在于,这些类并不总是放置在单独的PAS单元中。我的意思是,共享的代码通常被放入包含特定模块代码的单元中。这意味着,当您修复共享类中的错误时,仅仅将定义错误的PAS单元复制到所有软件模块中并重新编译是不够的。不幸的是,您必须逐个将修复的代码片段复制粘贴到每个模块中的适当单元和类中。这需要很长时间,我希望通过选择正确的方法来消除这种情况 - 请帮助我。

我认为使用随EXE分发的BPL将是一个好的解决方案,但它也有一些缺点,正如先前的讨论中提到的一样。最大的问题是,如果每个EXE需要多个BPL,则我们的技术支持人员必须知道哪个EXE需要哪些BPL,然后提供正确的文件给最终用户。在没有软件更新程序的情况下,这对于我们的技术人员和最终用户来说都将是一项重大工作。他们肯定会感到迷惑和愤怒 :-/。

还可能出现兼容性问题 - 如果一个BPL被许多EXE共享,那么对该BPL的修改可能对某些其他项目有好处,而对另一些项目有坏处。

那么,我应该怎么做才能更快地在这么多项目中修复漏洞?我考虑以下方法之一。如果您有更好的想法,请告诉我。

  • 将共享代码放入独立的PAS单元中,这样当其中一个单元有错误修复时,只需将其复制到所有项目中(覆盖旧文件)并重新编译所有项目即可。这意味着每个单元被复制的次数与它使用的项目数量相同。

对于很少修改的代码来说,这种解决方案似乎还可以。但我们也有通用函数和过程的pas单元,它们经常会进行修改。每次有人向此文件添加新功能时,执行相同的复制和重新编译操作是不可能的。

  • 为所有共享代码创建BPL,但将它们链接到EXE中,使EXE是独立的。

现在对我来说似乎是最好的解决方案,但也有一些缺点。如果我在BPL中进行了错误修复,每个程序员都必须在他们的计算机上更新BPL。如果他们忘记这样做怎么办?然而,我认为这是一个小问题。如果我们注意相互通知变化,一切都应该没问题。你觉得呢?

  • 最后一种想法是由CodeInChaos提出的(我不知道是否理解正确)。在项目之间共享PAS文件。这可能意味着我们需要将共享的代码存储在单独的文件夹中,并使所有项目在那里搜索该代码,对吧?每当需要修改项目时,它都必须与共享文件夹一起从SVN下载,我想。每个共享代码的更改都必须导致使用该代码的每个项目重新编译。

请帮我选择一个好的解决方案。我只是不希望公司因软件开发的愚蠢方法而浪费比必要更多的时间和金钱。到目前为止没有人关心过这个问题,你可以想象它会引起多少问题。

非常感谢。


1
永远不要有两个相同的函数副本。 SVN很难实现你想做的事情,但我认为你需要使用SVN外部引用。我建议转到Programmers,那里你应该能得到更好的回应。 - David Heffernan
1
@David 微软这样做,在他们的Office产品的源代码中。(Scott Berkun,《项目管理的艺术》)为什么?如果一个副本中引入了一个错误,其他产品将不会受到影响。 - mjn
@mjn 我简直不敢相信。我认为你把故事讲得有点扭曲了。 - David Heffernan
如果在一个版本中修复了一个错误,他们不会在其他版本中修复它吗? - Darian Miller
2个回答

5

您说:

  • 为所有共享代码创建BPL,但将它们链接到EXE中,使得EXE是独立的。

您不能将BPL链接到可执行文件中。您只需链接与BPL中相同的单元即可。这样,您实际上并不使用或甚至不需要BPL。

BPL旨在用作共享代码,即将共享的代码放入一个或多个BPL中,并从每个.exes、.dlls或其他.bpls中使用它。修复错误(如果它们不改变BPL的公共接口)仅需要重新分发该修复后的BPL。

如我所说,先决定DLL的公共接口,然后不要更改它。您可以添加例程、类型和类,但不应修改任何已在使用中的现有类、类型、接口、常量、全局变量等的公共接口。这样,可以轻松地分发修复后的BPL。

但请注意,BPL文件高度依赖编译器版本。如果您使用新版本的编译器,则还必须重新编译BPL文件。这就是为什么有意义给BPL文件添加后缀,如100、110等,具体取决于编译器版本。使用编译器版本15.0编译的可执行文件将使用带有后缀150的BPL文件,而使用14.0版本编译的可执行文件将使用带有后缀140的BPL文件。这样,不同版本的BPL文件就可以和平共存。后缀可以在项目选项中设置。

如何管理不同的版本?创建一个类似于我为ComponentInstaller BPL文件创建的目录结构(这是您可以在Delphi/C++Builder/RAD Studio XE IDE下的“组件”菜单中看到的专家 -> 安装组件):

Projects
  ComponentInstaller
    Common
    D2007
    D2009
    D2010
    DXE

Common目录包含每个版本共享的.pas文件和资源(位图等),而每个Dxxxx目录包含特定版本的BPL的.dpk、.dproj等文件。每个包都使用Common目录中的文件。当然,这可以同时针对几个BPL完成。顺便说一下,版本控制系统可能会使这个过程更加容易。只要确保为每个BPL版本添加不同的后缀即可。如果您实际上想要独立的可执行文件,则不使用BPLs,并链接单独的单位。选项“使用BPLs编译”管理此过程。

谢谢您分享您的经验。让我再问你一个问题。您的示例基于同一BPL的几个版本。每个BPL版本都使用相同的目录和.pas文件。但是让我创建另一个BPL,其中包含完全不同的代码,它引用您的公共目录中的某些单元。现在,它应该直接引用这些文件(附加到项目)还是仅使用其中一个公共BPL(通过在项目的需求列表中引用它)?您认为哪种方法更好? - Mariusz Schimke
5
同一时间,单位应该只存在于一个 BPL 中。需要此单位的其他 BPL 应该要求包含它的 BPL。应该仔细考虑哪个单位应该放在哪里。可以绘制一个图表,标明可执行文件和哪个单位需要什么,对其进行重新排列,直到满意为止,然后做出决定。 - Rudy Velthuis
好的,这正是我想要听到的。谢谢。 - Mariusz Schimke
让我总结一下,结束这个讨论。共享代码被放入BPL中。Pas文件永远不会被复制——只有BPL依赖项在这种方法中被接受。每个程序员都在他们的机器上安装所有共享的BPL,并在有人修改其中任何一个BPL时更新它们(连同属于它们的.pas文件)。只要使用SVN,这似乎很容易。这一切都正确吗? - Mariusz Schimke
1
如果其中一个BPL进行了错误修复,但是接口(其公开的函数、过程、类、类型、常量等)未被修改,则只需替换该BPL即可。无需替换依赖于它的BPL或可执行文件。您可以向BPL的接口中添加内容,但不能删除任何东西或进行修改 - Rudy Velthuis

3
从我的角度来看,试图管理Delphi单元、库和可执行文件等工件,你在错误的地方搜索。我建议你转身开始重构代码,基于设计模式实现。
例如,所有通用函数可以放置到一个Singleton类中,通用类的实例可以使用Abstract Factory构造,类可以通过本机Delphi接口实现而不是直接使用相互交互,等等。甚至您可以选择为项目中的所有通用部分实现Facade
当然,具体的模式选择和实现细节取决于项目特定情况,只有你才能决定什么适用于你的情况。我认为,在这种思路下查看项目后,你可以找到更自然的代码组织方式和解决问题的方法。
还有一些其他事情:
  1. 当然,你必须遵循@CodeInChaos的建议,并在所有项目之间共享源文件的一个副本,而不是手动将其复制到每个项目中。如果你采用一些标准的构建环境,这可能会很有用,这些标准对于所有开发人员都是强制性的(相同的文件夹结构、库的位置、环境设置)。
  2. 尝试分析构建和部署过程:对我来说,当解决方案未使用最新版本的代码进行构建并且在部署之前未经过测试时,它看起来很异常(这是针对你的“如果我在BPL中进行错误修复,则每个程序员…”短语的)。
  3. 独立可执行文件的变体看起来更好,因为它显着简化了测试环境和项目部署的组织。只需选择适当的版本约定即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接