Interface-based Programming example employing the Factory pattern

When I demonstrate the use of interface-based programming in a class, I always give a demo of using interface-based programming combined with a factory pattern to add dynamic behaviors to an application. This is basically a scaled down version of what the provider model in ASP.NET 2.0 and the Enterprise Library do to allow you to externally configure components that will be called by the framework at runtime. ASP.NET actually uses abstract base classes instead of interfaces because they also provide some shared implementation, but the concepts are basically the same.



I use a simple little example of a client that defines an IDog interface (in a separate interface only assembly that it can share with component providers) that specifies the contract that providers are expected to implement. I then show how to build components separately that the client has no specific type information about, and load and invoke the behavior defined in those components dynamically through a factory and based on the interface definition that is the contract for how those components expose their behavior and some configuration file entries that the factory can use to load and instantiate the types. The client is able to do this without requiring any code modifications to accept new components, and can even have new behaviors added at runtime without needing to restart the application.



I have repacked the demo I normally give in class to:

Make it a little cleaner

Separate out the factory into a generic factory in a separate assembly that could be reused for any project

Use the new Settings features in the .NET 2.0 framework to enter the type information into a configuration file instead of using a separate XML file like I used to.



The factory method looks like the following:



publicstatic T[] ConstructType() where T : class

{

// Refresh the config cache in case the config file has been edited at runtime

Settings.Default.Reload();

// Load the collection of components from the string collection in config

StringCollection componentTypeInfoColl = Settings.Default.Components;

// Create a list to add the components to as they are created

List components = newList();

//
Loop through the config strings trying to create instances of the appropriate type

foreach (string componentTypeInfo in componentTypeInfoColl)

{

try

{

// Config entries should be in the form:

// Fully.Qualified.TypeName, AssemblyName

// Split into its two parts

string[] typeInfo = componentTypeInfo.Split(‘,’);

// Dynamic load the assembly

Assembly assem = Assembly.Load(typeInfo[1].Trim());

// Dynamic instance creation

T instance = assem.CreateInstance(typeInfo[0].Trim()) as T;

if (instance != null)

{

components.Add(instance);

}

}

catch { } // Just ignore invalid types

}

return components.ToArray();

}



So the only things you need to know to use this as a factory for other purposes than this demo is that it expects the type information to be entered in the client configuration file with an application settings section like the following:



<configuration>

<configSections>

<sectionGroupname=applicationSettingstype=System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 >

<sectionname=DynamicFactoryLibrary.Properties.Settingstype=System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089requirePermission=false />

sectionGroup>

configSections>

<applicationSettings>

<DynamicFactoryLibrary.Properties.Settings>

<settingname=ComponentsserializeAs=Xml>

<value>

<ArrayOfStringxmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

xmlns:xsd=http://www.w3.org/2001/XMLSchema>

<string>Animals.Dog, Animalsstring>



Animals.GermanShepherd, Animals

Animals2.Shitzu, Animals2

–>

ArrayOfString>

value>

setting>

DynamicFactoryLibrary.Properties.Settings>

applicationSettings>

configuration>



I’m just using a StringCollection as the type for the component type collection, so each component that you want to add to the collection should be added to the section in the form:



<string>Animals.Dog, Animalsstring>



You can see a couple of additional components commented out for dynamically adding them into the application (even while it is running). The type information uses a standard convention for specifying type information through a config file: specifically the fully qualified type name of the type, followed by a comma, followed by an Assembly name to load it from.



You can grab the whole sample here.



To demonstrate the example in action:

  • First build the InterfaceBasedProgramming solution, which builds the interface contract assembly, the factory library, and the client application. The default client config file has all of the type information for available components commented out.

  • Open the build output folder of the client (InterfaceBasedClient\bin\debug) and run the client by double clicking on InterfaceBasedClient.exe.

  • Push the button and observe that you get nothing because no types have been provided or plugged in though the config file yet. Leave the client running.

  • Next open the Animals solution, build it, and copy the build output (Animals.dll) from the bin\Debug folder into the client’s bin\Debug folder.

  • Edit the InterfaceBasedClient.exe.config file through an editor, and uncomment the Dog type information as shown in the snippet of config file above, save the file.

  • Hit the button in the client again, you should see a standard Dog Bark (through a message box).

  • Edit the config file again, uncommenting the type information for a GermanShepherd (which is also defined in the Animals assembly), and save.

  • Push the button again and you will see that type is dynamically used from the already loaded assembly.

  • Go open the Animals2 solution and build it.

  • Copy the Animals2.dll from its bin\Debug folder into the bin\Debug folder for the client.

  • Edit the config file for the client again to uncomment the type information for the Shitzu type, and save.

  • Click the button in the client again and you should see all three types of dogs bark.