如何为Delphi创建模块化插件

如何为Delphi创建模块化插件,delphi,plugins,architecture,modular-design,Delphi,Plugins,Architecture,Modular Design,使用Delphi2010,我需要编写一个程序来支持模块或插件。虽然有点做作,但假设我有一个可以转换数据文件/文本文件的应用程序。它将支持30种输入格式以及与输出相同的30种格式。第一个版本可能只实现其中的一些格式。我的挑战是我想要一个数据驱动的流程流 例如,假设我有一个PARSE_文件例程。如果我的输入数据文件格式是“format_A”,那么当我调用PARSE_file时,它应该知道使用PARSE_file_format_A,而不是PARSE_file例程的其他29个不同版本 PARSE_文件只

使用Delphi2010,我需要编写一个程序来支持模块或插件。虽然有点做作,但假设我有一个可以转换数据文件/文本文件的应用程序。它将支持30种输入格式以及与输出相同的30种格式。第一个版本可能只实现其中的一些格式。我的挑战是我想要一个数据驱动的流程流

例如,假设我有一个PARSE_文件例程。如果我的输入数据文件格式是“format_A”,那么当我调用PARSE_file时,它应该知道使用PARSE_file_format_A,而不是PARSE_file例程的其他29个不同版本

PARSE_文件只是一个例子。我可能会有60个不同的通用函数,LOAD_FILE,GET_DELIMITER,PARSE_FILE,等等,但是对于30种不同的格式,这些函数中的每一个都会略有不同。我可以使用什么技术来加载格式为_a的文件,这60个不同的常用例程中的每一个都使用这60个例程的正确“版本”

请记住,我只从5种输入格式开始,稍后将添加其他格式,因此我需要一种集中定义此“映射”的方法,因此在我的代码中使用这些例程的任何地方,都将使用正确版本的例程,即使我称之为泛型版本

  • 定义您需要每个插件的一组标准例程 要在接口类型中实现的模块。例如,
    IFileFormatHandler
    , 它包含一个PARSE_文件函数等
  • 遵循接口设计和职责分离的原则,避免将函数放在某些实现不感兴趣或无法实现的接口中。在实现类可以选择实现或不实现的单独接口中定义可选函数。例如,如果您预计会有应用程序将读取但由于各种原因无法写入的文件格式,则应将读取操作放在一个接口中,将写入操作放在另一个接口中
  • 如果您将在Delphi中创作所有插件,则应使用BPL包共享通用类型。将您的
    IFileFormatHandler
    接口类型放在一个BPL包(即common.BPL)中,以便所有模块都可以引用通用接口类型。每个插件模块本身也在其自己的BPL包中。(同一个BPL包中可能存在多个文件格式处理程序,但基线示例是每个BPL一个)
  • 如果这是你第一次尝试构建插件架构,不要试图同时涵盖多语言支持。现在就坚持使用Delphi。在Delphi中编写应用程序和模块。在Delphi中完成模块化项目,然后退一步,花一些时间理解COM或二进制接口要求,以支持mo用Delphi以外的语言编写的DULE
  • 在您的通用BPL包中,还定义一个全局函数,模块可以调用该函数使宿主应用程序了解它们自己-
    RegisterPlugin(名称:string;实例:IFileFormatHandler)
    例如,这会在一个内部列表中注册插件名称和实例,主机应用程序可以使用该列表查找可用插件并调用它们
  • 对于每个文件处理插件模块,定义一个实现共享BPL包中定义的公共接口的类。在该类的单元初始化中,调用RegisterPlugion()函数向主机应用程序注册该类
  • 主机应用程序使用公共包,模块包各自使用公共包
  • 主机应用程序仅通过公共接口中定义的功能与模块实现交互
  • 宿主应用程序可以使用IS来测试特定模块对象实例是否实现可选接口,并获取该可选接口
  • 接口在Delphi中是引用计数的,因此只要宿主应用程序保留对模块对象实例的引用(例如,通过RegisterPlugin),模块实例将保持活动状态并保存在内存中。当最后一个引用被释放时,模块实例将被丢弃
  • 宿主应用程序需要在运行时使用LoadPackage或类似fn查找并加载模块包bpls
  • 在一个应用程序中共享一个插件模块实例对大多数单线程应用程序来说都是很好的。如果你期望同时在多个线程中使用这些插件模块,考虑把这个设计转移到工厂模式中,而不是在内存中保存模块的单实例。共振峰通过在使用的线程内使用工厂按需构造实例来管理多线程,而不是创建一个必须能够安全地从多线程调用的实例

  • 我会使用接口,定义一个IMyPlugin接口,实现这个接口的类,每个*.dll导出一个名为“RegisterPlugin(AInterface:IMyPlugin)”的函数,当应用程序启动时,它扫描“插件文件夹”中的文件,对于每个“插件文件”(dll),你尝试加载它并调用它的“RegisterPlugin”“函数,当加载插件时,您可以调用接口上的方法来操作数据。我的2美分(:您可以选择DLL和COM兼容的
    i接口
    类型(仅限COM标准化的数据类型,这样当EXE和DLL具有不同的编译器版本或选项时,您不会使所有内容崩溃)。或者你使用BPL,就像VCL本身是制造的一样。JediVCL有一些简单的插件基础设施,但它的基础不是火箭科学。硬id规划API,这始终是你的创造性工作。有关于第一种方法的好文章,不知道你是否可以通过Google Translate阅读:这似乎是严格基于类的。我可以这样做吗使用非类函数?对于人为的示例,如果文件