brady gaster

yadnb

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?

Comments

Andrew said:

If I were writing this (that), it would be one of those times when I ask myself if I should use the cache or just keep a private collection in a hashtable.

I tend to go with the cache, but I keep wondering.
Have you thought about this, is there a "most correct" answer?

For the cache key name, I am usualy really defensive , even going so far as fullNamespace + "_" + className + "_" + method + "_" + localValue
Does that make me sane or crazy?

Would it make a differece to only cast the object as a ConstructorInfo if you know the object from the cache is not null?
# November 26, 2003 7:25 PM

Steve said:

I've seen this done a number of different ways as well. In a couple of apps I've created a private static Hashtable to store intances of the "IPersonHandler" type objects. I've also stuffed the created instance in the Hashtable rather then just the constructor. This obviously shouldn't be done if you need a "unique" instance of the class every time, but its worked out in a couple scenarios for me.
# November 26, 2003 9:41 PM

Jeff Gonzalez said:

Another thing you might think about doing, is implementing IClonable. You could create an instance of the type one time, and store that instance inside your hashtable. Then on create, you check to see if that object exists in the hashtable, and clone it. If it doesn't exist, then call .Invoke and add it to the hashtable.
# November 27, 2003 1:45 AM

Jesse Ezell said:

I believe the use of IClonable is obselete. You aren't supposed to use it, because it doesn't specify whether a deep copy or a shallow copy will be made.

Its strange to me that you didn't just call Activator.CreateInstance. I believe .NET caches a lot of the type metadata itself (I believe Rotor has a System.Reflection.Cache namespace that handles this if you want to see a sample implementation). So you might not get any performance improvements from the additional work (in fact, it might even hinder performance, since you are doing double the work)
# December 2, 2003 12:17 PM

Jesse Ezell said:

PS: Jeff, that isn't to mention that copying objects would be much slower even if IClonable wasn't obselete, because it requires the construct that is already happening plus a bunch of read/writes for each property.
# December 2, 2003 12:21 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)