Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Design for Extensibility : Page 6

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

Adding Extensibility to the Client
The client will also have to be changed. To support multiple plug-ins, instead of having the client look for a specific plug-in type to instantiate, it will read in all the modules, and loop through them, calling each one's Initialize method. That process will build the invocation lists of any delegates I've wired in any of the modules.

' In VB: Dim section As Object = _ ConfigurationManager.GetSection( _ "dataProcessingModules") Dim o_Modules As List(Of String) = _ DirectCast(section, List(Of String)) Dim o_FilterEvents As New ModuleEvents() For Each s_ModuleType As String _ In o_Modules Dim o As Object = _ Activator.CreateInstance( _ _Type.GetType(s_ModuleType)) Dim o_Module As IAcmeModule = _ DirectCast(o, IAcmeModule) o_Module.Initialize(o_FilterEvents) Next // In C#: object section = ConfigurationManager.GetSection( "dataProcessingModules"); List<string> o_Modules = section as List<string>; ModuleEvents o_FilterEvents = new ModuleEvents(); foreach (string s_ModuleType in o_Modules) { object o = Activator.CreateInstance( Type.GetType(s_ModuleType)); IAcmeModule o_Module = o as IAcmeModule; o_Module.Initialize(o_FilterEvents); }

Notice that the preceding code declares the o_FilterEvents ModuleEvents object outside the loop, and sends the same object to each module. This way, by the end of the loop, the delegate properties in the object may contain zero or more method pointers.

All this happens somewhere near the beginning of my application, but I still have to decide where to insert extensibility points within the body of the application, just like the earlier example called the IPostLogPlugin-based classes from a specific point in the application.

Upon making these decisions, I'll check for the property in o_FilterEvents that corresponds to the extensibility point within my application. Checking that property for a value other than null (Nothing in VB.NET) is enough to determine whether there are items in the invocation list for the delegate. At that point, it simply becomes a matter of invoking the delegate. The following is the extensibility point inserted just after I obtained a "data source" (remember the GetSource method):

'In VB: Dim b_Cancel As Boolean = False If Not o_FilterEvents.CheckDataSource _ Is Nothing Then Dim o_EventArgs As _ CheckDataSourceEventArgs = _ New CheckDataSourceEventArgs(s_Source) o_FilterEvents.CheckDataSource.Invoke( _ o_EventArgs) b_Cancel = o_EventArgs.Cancel End If //In C#: bool b_Cancel = false; if (o_FilterEvents.CheckDataSource != null) { CheckDataSourceEventArgs o_EventArgs = new CheckDataSourceEventArgs(s_Source); o_FilterEvents.CheckDataSource.Invoke( o_EventArgs); b_Cancel = o_EventArgs.Cancel; }

Note the Boolean variable declared before this event fires, which is set to the Cancel value that comes back from the EventArgs class. I've designed this event so that the code you can inject has the option to set the Cancel property to true or false. An example of code that taps into this event would look like this:

'In VB: Public Class ProfanityFilter Implements IAcmeModule Public Sub Initialize( _ ByVal events As _ Core.ModuleEvents) _ Implements Core.IAcmeModule.Initialize events.CheckDataSource = New _ AcmeModuleDelegate(Of _ CheckDataSourceEventArgs) (AddressOf events_CheckDataSource) End Sub Private Sub events_CheckDataSource( _ ByVal e As CheckDataSourceEventArgs) If e.Source.ToUpper().IndexOf("BAD") _ > -1 Then e.Cancel = True End If End Sub End Class / In C#: public class ProfanityFilter : IAcmeModule { void IAcmeModule.Initialize( ModuleEvents events) { events.CheckDataSource += events_CheckDataSource; } void events_CheckDataSource( CheckDataSourceEventArgs e) { if (e.Source.ToUpper().IndexOf("BAD") > -1) e.Cancel = true; } }

This module taps into the CheckDataSource event and checks to see if the data source coming in contains the word "BAD." If so, it sets the Cancel property to true. Now the code added to the client earlier that worked with the b_Cancel variable should make more sense. Code that follows the preceding example would check the value of b_Cancel to determine if it should continue processing.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.