Wrap It in a Factory
As an added bonus, let me show you a really cool way you can wrap all the code that determines what provider to use, instantiates it, casts it, and accesses the interface members into a static (shared) factory class. This is the same pattern that ASP.NET uses with its Membership, Roles, and Profile classes. Describing the Membership class should give you a good feel for what I'm going to do in the application example.
In ASP.NET, you can create a user using the Membership class, using:
Membership.CreateUser(
all arguments here...
ASP.NET uses a provider model behind the Membership class in order to determine how it will create a user. When you execute a statement like the one above, you execute the static constructor of the Membership class. At that point, the application reads the
app.config and makes the decision as to what provider to use. By the time you call the
CreateUser method, the class has a member variable that you've declared as the membership abstraction (in this case, a base class called MembershipProvider) and instantiated as the appropriate provider class as defined in the
app.config. The
CreateUser method delegates the call to the
CreateUser method of said variable, which executes the appropriate implementation. That's what I'm going to do with the provider model.
So here are the steps:
- Create a class that will contain static (shared) members only.
- Declare a class-level private static variable of type IDataProvider.
- In the static constructor, execute the code that will fill the static IDataProvider variable.
- Set up static methods that mimic those of the IDataProvider interface.
- In each of the methods, call the corresponding method in the IDataProvider variable.
For example, if I called my class AcmeFactory, it would look like this
(note that Visual Basic modules behave like C# static classes):
' In VB:
Public Module AcmeFactory
Sub New()
o_Provider = GetDataProvider()
End Sub
Private o_Provider As IDataProvider = Nothing
Public Function GetDataProvider() As _
IDataProvider
Dim s_Provider As String = _
ConfigurationManager. _
AppSettings("dataProvider")
Dim o As Object = _
Activator.CreateInstance( _
Type.GetType(S_Provider))
o_Provider = DirectCast(o, IDataProvider)
Return o_Provider
End Function
Public Function GetSource() As String
Return o_Provider.GetSource()
End Function
Public Function GetData() As String
Dim s_Source As String = o_Provider.GetSource()
Dim s_Data As String = _
o_Provider.GetData(s_Source)
Return s_Data
End Function
Public Sub LogData(ByVal data As String)
o_Provider.LogData(data)
End Sub
End Module
// In C#:
public static class AcmeFactory
{
static AcmeFactory()
{
o_Provider = GetDataProvider();
}
private static IDataProvider
o_Provider = null;
public static IDataProvider
GetDataProvider()
{
string s_Provider = ConfigurationManager.AppSettings[
"dataProvider"];
Object o = Activator.CreateInstance(
Type.GetType(s_Provider));
o_Provider = o as IDataProvider;
return o_Provider;
}
public static string GetSource()
{
return o_Provider.GetSource();
}
public static string GetData()
{
string s_Source = o_Provider.GetSource();
string s_Data = o_Provider.GetData(s_Source);
return s_Data;
}
public static void LogData(string data)
{
o_Provider.LogData(data);
}
}
Now, to actually use the provider, the client simply has to do the following:
' In VB:
Dim s_Data As String = AcmeFactory.GetData()
If s_Data <> "" Then
AcmeFactory.LogData(s_Data)
End If
// In C#:
string s_Data = AcmeFactory.GetData();
if (s_Data != "")
{
AcmeFactory.LogData(s_Data);
}
Note that the code doesn't even call the
GetSource method here; instead,
GetData does that for me in the factory class.
You can inject the provider model in many places throughout an application. If you ever find yourself looking at a piece of code and wondering if it may ever be subject to change, think about using this pattern there. If you ever find yourself looking at a process and wondering if it may be one day swapped out, removed, or enhanced, you may want to look at a plug-in model.