Browse DevX
Sign up for e-mail newsletters from DevX


Design for Extensibility : Page 4

Build extensibility and flexibility into your applications to simplify maintenance and accommodate changes.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Plug-ins are very similar to the provider model and, in fact, can be considered the same model, but used in a different way.

Whereas you can use a provider to determine which implementation to use for a certain process, you can use plug-ins to inject functionality into a process, swap that functionality out later, or make it grow.

Consider the example application again. Suppose I need to further enhance my three-step process with the ability to send out an email to some managers regarding the data the process has handled. I can easily just add that functionality to the client right after it logs the data—but what if I want to make that subject to change in the future? Or even better, what if I can anticipate that a manager will later ask me to do something else with the data, even after I've sent out an email, such as archive it somewhere?

Instead of adding this and any other functionality to my client, I'm going to inject a plug-in model and have the client interact with that. The path I'll take to do this is very similar to that of the providers; the first step is to define an abstraction for my plug-in. I'm going to call the plug-in after logging the data, so I'll name the interface appropriately: IPostLogPlugin. I don't really know what each plug-in will do; however, I do know that it will do something with the data I just finished logging, so the interface will define a single method that receives string data in an argument.

' In VB: Public Interface IPostLogPlugin Sub PerformProcess(ByVal data As String) End Interface // In C#: public interface IPostLogPlugin { void PerformProcess(string data); }

Now, before I actually write any classes that use this interface, I'm going to inject the client with code that runs these potential plug-ins. Where I put this code is called a "point of extensibility," which in this case goes is directly after the logging code. Also, the inserted code will take into account the possibility that more than one such plug-in might be installed. Plug-ins written to this new interface get installed in app.config just like the provider, but plug-ins will go in their own configuration section, called <postProcessing> and be read into a collection of objects that adheres to the details in the configuration section. Details on writing a custom configuration section is beyond the scope of this article, but the code I'll put here should be pretty self-explanatory.

' In VB: Dim s_Data As String = AcmeFactory.GetData() If s_Data <> "" Then AcmeFactory.LogData(s_Data) End If Dim section As Object = _ ConfigurationManager.GetSection( _ "postProcessing") Dim o_PlugIns As List( _ Of PluginInfo) = _ DirectCast(section, _ List(Of PluginInfo)) For Each o_PluginInfo As _ PluginInfo In o_Plugins Dim o As Object = _ Activator.CreateInstance( _ Type.GetType(o_PluginInfo. _ PluginType)) Dim o_PlugIn As IPostLogPlugin = _ DirectCast(o, IPostLogPlugin) o_Plugin.PerformProcess(s_Data) Next // In C#: string s_Data = AcmeFactory.GetData(); if (s_Data != "") { AcmeFactory.LogData(s_Data); } object section = ConfigurationManager.GetSection( "postProcessing"); List<PluginInfo> o_Plugins = section as List<PluginInfo>; foreach (PluginInfo o_PluginInfo in o_Plugins) { object o = Activator. CreateInstance( Type.GetType(o_PluginInfo. PluginType)); IPostLogPlugin o_Plugin = o as IPostLogPlugin; o_Plugin.PerformProcess(s_Data); }

The preceding code reads the collection of plug-ins from app.config into a variable called o_PlugIns.

I'm going to write two plug-in classes called EmailProcessing and ArchiveProcessing, and they'll go into the app.config file like this:

<postProcessing> <process name="email" description="Send notification emails" type="PlugIns.EmailProcessing, PlugIns" /> <process name="archive" description="Check for archive needs" type="PlugIns.ArchiveProcessing, PlugIns" /> </postProcessing>

Given the contents of this app.config section and the previous code, I'm looping through each plug-in definition, creating the class stored in the "type" attribute, and then casting it to the IPostLogPlugin interface. After which I simply call the PerformProcess method, sending in my string data.

The client doesn't know what the plug-in classes do nor how many plug-ins are installed. All that remains is to write the plug-in classes themselves. I'm not going to actually show any implementation details for sending emails or archiving data, but you can envision what's going on.

' In VB: Public Class EmailProcessing Implements IPostLogPlugin Protected Sub PerformProcess( _ ByVal data As String) Implements _ Core.IPostLogPlugin.PerformProcess ' take data and e-mail it ' somewhere End Sub End Class Public Class ArchiveProcessing Implements IPostLogPlugin Protected Sub PerformProcess( _ ByVal data As String) Implements _ Core.IPostLogPlugin.PerformProcess ' take data and archive it End Sub End Class // In C#: public class EmailProcessing : IPostLogPlugin { void IPostLogPlugin. PerformProcess( string data) { // take data and e-mail it // somewhere } } public class ArchiveProcessing : IPostLogPlugin { void IPostLogPlugin. PerformProcess(string data) { // take data and archive it } }

As in the case of providers, the assembly in which the plug-in classes reside is not directly referenced by the client; it merely has to be accessible (meaning just dropped into the client's Bin folder).

Though I'm not going to do it here, with a little creativity you can probably wrap some of the client's work into a factory or helper class as I did in the provider model.

You can use this plug-in model to inject as many "points of extensibility" in your application as you want. And you can do this even if you don't have any plans to write an immediate plug-in that you call at a specific point—when you're just securing your application for the future. The drawback comes when you have more than one type of plug-in, each of which may require its own interface. At this point, you may be giving your developers too many interfaces to try to remember. That's where extensibility modules come in handy.

Thanks for your registration, follow us on our social networks to keep up-to-date