Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create modular plug-ins for Delphi

Using Delphi 2010, I am needing to write a program to support modules, or plug-ins. Although a little contrived, assume I have an app which converts datafiles/text files. It will support 30 input formats and those same 30 formats as outputs. The first release will probably only implement a few of these formats. My challenge is that I want a data driven process flow.

For example, assume I have a PARSE_FILE routine. If my input data file format is 'Format_A', then then when I call PARSE_FILE, it should know to use PARSE_FILE_Format_A, as opposed to the other 29 different versions of the PARSE_FILE routine.

PARSE_FILE is just an an example. I will probably have 60 different common functions, LOAD_FILE, GET_DELIMITER, PARSE_FILE, etc, but each of these functions will be a little different for each of the 30 different formats. What technique can I use so that if I am Loading a file with FORMAT_A, each one of these 60 different common routines uses the proper 'version' of these 60 routines?

Keep in mind that I am starting with just 5 input formats, and will add other formats later, so I need a way of centrally defining this "mapping', so wherever these routines are used throughout my code, the proper version of the routine will be used even though I call the generic version.

like image 348
user1009073 Avatar asked Feb 26 '13 15:02

user1009073


People also ask

What is a plugin module?

A: A plugin is a piece of functionality that can be installed, and once installed dynamically, it can be removed without requiring a rebuild of your application. A module has to be installed as part of the deployment or update process, and once a module is installed, it is very difficult to undo.

What is plugin architecture?

A plug-in is a bundle that adds functionality to an application, called the host application, through some well-defined architecture for extensibility. This allows third-party developers to add functionality to an application without having access to the source code.


1 Answers

  1. Define your set of standard routines that you require every plugin module to implement in an interface type. Say, IFileFormatHandler, which contains a PARSE_FILE function, etc.
  2. Following principles of interface design and separation of duties, avoid putting functions in an interface that some implementations will not be interested in or able to implement. Define optional functions in a separate interface that an implementing class can choose to implement or not. For example, if you anticipate that there will be some file formats that your app will read but for various reasons will not be able to write, then you should put read operations in one interface and write operations in a different interface.
  3. If you will be authoring all the plugins in Delphi, you should use BPL packages to share common types. Place your IFileFormatHandler interface type in one BPL package (ie, Common.bpl) so that all the modules can reference the common interface type. Each plugin module itself is also in its own BPL package. (Multiple file format handlers could exist in the same BPL package, but the baseline example is one per BPL)
  4. If this is your first swing at building a plugin architecture, don't try to cover multilanguage support at the same time. Stick with Delphi for now. Write the app and the modules in Delphi. Build a modular project to completion in Delphi, then take a step back and spend some time understanding COM or binary interface requirements to support modules written in languages other than Delphi.
  5. In your common BPL package, also define a global function that modules can call to make themselves known to the host application - RegisterPlugin(name: string; instance: IFileFormatHandler) for example. This registers the plugin name and instance in an internal list that the host application can use to find out what plugins are available and call them.
  6. For each file handling plugin module, define a class that implements the common interface(s) defined in the shared BPL package. In the unit initialization of the class, call the RegisterPlugion() function to register the class with the host app.
  7. The host application uses the common package, and the module packages each use the common package.
  8. The host application only interacts with the module implementations via the functions defined in the common interface(s).
  9. The host application can use IS to test whether a particular module object instance implements an optional interface, and AS to obtain that optional interface.
  10. Interfaces are reference counted in Delphi, so as long as the host application holds onto a reference to a module object instance (via RegisterPlugin, for example) the module instance will be kept alive and in memory. When the last reference is released, the module instance will be disposed of.
  11. The host application will need to find and load the module package bpls at runtime, using LoadPackage or similar fn.
  12. Sharing one list of plugin module instances across the app will work fine for most single-threaded applications. If you anticipate using these plugin modules in multiple threads at the same time, consider shifting this design to a factory pattern instead of holding singleton instances of modules in memory. It is far easier and usually more performant to manage multithreading by constructing instances on demand within the thread of use using a factory than to make one instance that must be safe to call from multiple threads.
like image 192
dthorpe Avatar answered Sep 29 '22 06:09

dthorpe