6

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

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

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

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

4

1 回答 1

5
  1. 定义您需要每个插件模块以接口类型实现的一组标准例程。比如说,IFileFormatHandler它包含一个 PARSE_FILE 函数等。
  2. 遵循接口设计和职责分离的原则,避免将某些实现不会感兴趣或无法实现的功能放在接口中。在实现类可以选择实现或不实现的单独接口中定义可选函数。例如,如果您预计您的应用会读取某些文件格式,但由于各种原因无法写入,那么您应该将读取操作放在一个接口中,将写入操作放在另一个接口中。
  3. 如果您将在 Delphi 中编写所有插件,您应该使用 BPL 包来共享通用类型。将您的IFileFormatHandler接口类型放在一个 BPL 包(即 Common.bpl)中,以便所有模块都可以引用通用接口类型。每个插件模块本身也在其自己的 BPL 包中。(多个文件格式处理程序可以存在于同一个 BPL 包中,但基线示例是每个 BPL 一个)
  4. 如果这是您构建插件架构的第一次尝试,请不要尝试同时涵盖多语言支持。现在坚持使用 Delphi。在 Delphi 中编写应用程序和模块。在 Delphi 中构建一个模块化项目以完成,然后退后一步,花一些时间了解 COM 或二进制接口要求,以支持用 Delphi 以外的语言编写的模块。
  5. 在您的通用 BPL 包中,还定义一个全局函数,模块可以调用该函数以使主机应用程序知道它们自己 -RegisterPlugin(name: string; instance: IFileFormatHandler)例如。这会在内部列表中注册插件名称和实例,主机应用程序可以使用该列表来找出可用的插件并调用它们。
  6. 对于每个文件处理插件模块,定义一个实现共享 BPL 包中定义的公共接口的类。在类的单元初始化中,调用RegisterPlugion()函数将类注册到宿主应用程序中。
  7. 宿主应用程序使用通用包,模块包各自使用通用包。
  8. 宿主应用程序仅通过公共接口中定义的功能与模块实现进行交互。
  9. 宿主应用程序可以使用 IS 来测试特定模块对象实例是否实现了可选接口,并使用 AS 来获取该可选接口。
  10. 在 Delphi 中,接口是引用计数的,因此只要宿主应用程序持有对模块对象实例的引用(例如,通过 RegisterPlugin),模块实例就会保持活动并在内存中。当最后一个引用被释放时,模块实例将被释放。
  11. 宿主应用程序需要在运行时使用 LoadPackage 或类似的 fn 查找并加载模块包 bpls。
  12. 在应用程序中共享一个插件模块实例列表将适用于大多数单线程应用程序。如果您预计在多个线程中同时使用这些插件模块,请考虑将此设计转换为工厂模式,而不是在内存中保存模块的单例实例。通过使用工厂在使用的线程中按需构造实例来管理多线程,这比创建一个必须安全地从多个线程调用的实例要容易得多,而且通常性能更高。
于 2013-02-26T18:36:46.383 回答