Team-based Provider Model Goodieness
This is beginning to get really interesting to me. I didn't really dig the caching mechanism in that last sample. It just seemed kind of squirly, so I sat there staring at it for a little while. Then I received a comment with a really awesome suggestion on how to clean the caching logic up. So I did. Once I got home from work I was really thinking about how to improve on this model, and I notice a second comment from Frans suggesting that I might want to think about a factory pattern.
“Hmm, seems like a really interesting idea indeed.”
So off I went, creating the new, groovy, PersonHandlerFactory class. This little guy does a lot of the work that the Run() method was doing a little while ago. Type-casting all all sorts of things. The end result of the main method of this class, Create(), is to produce an instance of a Provider class to the caller, either from the cache or by late-binding - only this time, we completely remove the call to Activator.CreateInstance(). Quite interesting indeed - cleaner code, easier to read, and most likely a teensy bit faster than the previous iteration.
///
<summary>
/// Hander-outer of IPersonHandler Providers.
/// </summary>
public class PersonHandlerFactory
{
internal PersonHandlerFactory(){}
/// <summary>
/// Checks to see if the constructor has been added to the web
/// server cache and if not, it is simply returned. If the
/// ConstructorInfo instance has been added to the web server cache,
/// it is invoked and the new object is returned.
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
public static IPersonHandler Create(string typeName)
{
IPersonHandler provider = null;
if(HttpContext.Current != null)
{
Cache cache = HttpContext.Current.Cache;
// Check the cache for this type to see if we've already got the contructor
ConstructorInfo constructor = (ConstructorInfo)cache[typeName];
// If we don't already have the constructor, get it now
if(constructor == null)
{
// Get an instance of the specified type
Type t = Type.GetType(typeName);
// Get the default/empty constructor
constructor = t.GetConstructor(new Type[0]);
// Cache this constructor info for future lookups
cache.Insert(typeName, constructor);
}
// first, get the object from the constructor
object o = constructor.Invoke(null);
// convert that puppy!
provider = o as IPersonHandler;
}
return provider;
}
}
Of course, we're going to have to make a slight change to the Engine class's Run() method. This class has now become somewhat of a facade to the work that the factory's doing.
///
<summary>
/// Runs the collection of providers on a person instance.
/// </summary>
public class PersonEngine
{
/// we won't allow construction, as the static Run method will be enough.
internal PersonEngine(){}
/// <summary>
/// Executes the collection of providers' functions.
/// </summary>
public static void Run(Person personInstance)
{
// get the name of the type of provider we're supposed to use
string typeName = String.Empty;
PersonSettings settings = PersonSettings.GetSettings();
// set up a local variable to reference the person class
// so we can continue to act on it repetitiously
Person pTmp = personInstance;
for(int i=0; i<settings.TypesInProcess.Length; i++)
{
typeName = settings.TypesInProcess[i];
// make sure it's an implementor of the proivider interface
IPersonHandler handler = PersonHandlerFactory.Create(typeName);
// perform it's functionality
pTmp = handler.HandlePerson(pTmp);
}
}
}
Nice! This project is progressing along rather nicely. Any more suggestions?