Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementation of Class Factories in C#

0.00/5 (No votes)
16 Jan 2003 1  
Details five ways to implement a class factory in C#.

Sample Image - ClassFactory.gif

Table of contents

Introduction

There are several articles that describe how to write patterns in C#. However, the example implementations are usually simple in nature. Due to prior work with class factories, I became interested in the implementation of class factories in C#, but was not satisfied with the work I'd seen so far. This article provides five example implementations and compares their performance.

Background

In my previous job as a Delphi programmer, I wrote an XSLT processor that could apply an XSLT style sheet to an XML document. One of the tasks was to implement a class factory that would instantiate the correct class based upon the XSLT element just read from the style sheet. For example, if the XSLT processor encountered an xsl:value-of element in the style sheet then it would create an instance of class TXslValueOf to represent the element.

In Delphi, this was a trivial task because classes are objects. It was a simple matter to store a reference to the class in a hash table, keyed on the name of the XSLT element. When the element name was encountered, the class could be retrieved from the hash table and its Create method invoked.

I became interested in finding a similarly graceful solution in C#. A co-worker pointed me in the direction of System.Activator. After playing with it a bit, I investigated two other implementations to see if better performance could be obtained. The first version of this article led to two other people, Wesner Moise and M. Veurman, suggesting modifications and alternative implementations.

The test program

The test program is a console program comprised of five parts: a Director class, a Tester class, a Globals class to define static data, a number of classes representing XSLT elements, and several class factory implementations. The Director class instantiates Tester, tells Tester to run the performance tests, and then reports the average time for each performance test. A portion of the Director code is as follows:

    [STAThread]
    static void Main(string[] args)
    {

      Tester tester = new Tester(instances);

      Console.WriteLine(String.Format("Running {0} passes of each test, " +
        "creating {1} instances per test",
        passes, instances));
      Console.WriteLine(Environment.NewLine);

      // Run hash-switch test.

      Console.WriteLine("Running Hash-Switch test...");
      int Total = 0;
      for (int i = 0; i < passes; i++)
        Total += tester.CreateViaHashSwitch();

      // Report the average.

      Console.WriteLine("Hash-Switch: " + (Total/passes) + " ms");
      Console.WriteLine(Environment.NewLine);
      ...
    }

The Globals class defines a static array of XSLT element names. This data is used by some of the class factory implementations.

  // Defines globally-available, static data.

  public class Globals
  {
    // XSLT element names

    public static string[] ElementNames = new string[24]
      {
        "xsl:apply-imports",
        "xsl:apply-templates",
        ...
      };
  }

The classes representing XSLT elements don't do anything. They exist for the sole purpose of giving Tester something to instantiate. The XSLT element classes all inherit from class BaseElement and have names such as XslApplyTemplates and XslForEach.

The Tester class implements several test methods. For example, CreateViaActivator, CreateViaHashSwitch, and CreateViaIfName. Each test method instantiates the appropriate class factory and measures the time it takes the factory to create a specified number of objects.

Each class factory implementation resides in its own file (e.g., class ActivatorFactory is in file ActivatorFactory.cs). This test program declares an abstract class factory with an abstract Create method that is used to instantiate objects of the required type. Each concrete class factory implements the Create method. The actual implementations are described in the following sections.

Using System.Activator.CreateInstance

My first implementation of a class factory used the CreateInstance method of System.Activator. The implementation is in file ActivatorFactory.cs, class ActivatorFactory.

The System.Activator.CreateInstance method has a number of overloads and my first attempt used the overload expecting an assembly name and class name. However, I figured I was wasting time by repeatedly converting the name to a type. My final implementation uses a hash table to map the name of the XSLT element to a class type and then passes the class type to CreateInstance. As shown in the following code snippet, the hash table is initialized in the ActivatorFactory constructor:

  public class ActivatorFactory : BaseClassFactory
  {
    // The following hash table maps an XSLT element name to a

    // System.Type.

    private Hashtable _ElementHash;

    public ActivatorFactory()
    {
      // Populate the hash table.

      _ElementHash = new Hashtable(50, (float)0.5);
      _ElementHash.Add(Globals.ElementNames[0], typeof(XslApplyImports));
      ...
    }
  ...

Note that the Hashtable is constructed with a load factor less than 1.0. As pointed out by Wesner Moise, this noticeably reduces the time it takes to locate a key. The implementation of the class factory's Create method (i.e., the method that instantiates an object of the correct class) is as follows:

    public override BaseElement Create(string name)
    {
      System.Type type = (System.Type) _ElementHash[name];
      return (BaseElement) System.Activator.CreateInstance(type);
    }

To me, using System.Activator is elegant. If more classes are added, the only change that need be made is to update the constant array of class names and add an entry to a hash table in the factory's constructor. However, I had been warned this approach might be slow. To see if the warning was valid, I implemented other class factories (see the following sections). It turns out that using System.Activator.CreateInstance can be several times slower than the alternatives presented in this article.

Using a Hashtable and Switch statement

The next implementation of a class factory once again uses a hash table. Instead of mapping an element name to a class type, it maps the name to an integer index. A switch statement then uses the index to instantiate the correct class. The implementation is in file HashSwitchFactory.cs, class HashSwitchFactory.

The following code snippet shows part of the hash table's initialization:

  public class HashSwitchFactory : BaseClassFactory
  {
    // The following hash table maps an XSLT element name to an

    // integer index. The Create method uses the index to identify

    // the class to be instantiated.

    private Hashtable _ElementHash;

    public HashSwitchFactory()
    {
      // Populate the hash table.

      _ElementHash = new Hashtable(50, (float)0.5);
      _ElementHash.Add(Globals.ElementNames[0], 0);
      _ElementHash.Add(Globals.ElementNames[1], 1);
      ...
    }
  ...

The following code snippet shows a portion of the switch statement within the Create method:

    public override BaseElement Create(string name)
    {
      switch ((int) _ElementHash[name])
      {
        case 0:
          return new XslApplyImports();
        case 1:
          return new XslApplyTemplates();
        ...
      }
    }

This approach to a class factory is several times faster than using System.Activator.CreateInstance. However, it requires more source code to implement (I don't like big case statements) and comes across to me as less maintainable.

Using an If Statement with Name comparison

When I looked in the Rotor source code to see how Microsoft instantiates an XSLT class based upon an element name, I discovered they use a large if..else statement. Each clause compares the element name to one of the names on record. If they match, the associated class is instantiated. For the sake of completeness, I added class IfNameFactory in file IfNameFactory.cs to time an implementation using the same approach. The implementation of the factory is as follows:

  public class IfNameFactory : BaseClassFactory
  {
    public override BaseElement Create(string name)
    {
      if (name == Globals.ElementNames[0])
      {
        return new XslApplyImports();
      }
      else if (name == Globals.ElementNames[1])
      {
        return new XslApplyTemplates();
      }
      else if (name == Globals.ElementNames[2])
      ...
    }
  }

As you can see, the code looks very similar to the case where a hash table and switch statement was used. However, the performance of using an if statement varies depending upon how many comparisons need to be made. The final version of the test program instantiates 24 different classes. With 24 classes, the if statement is slower than the switch statement. When the number of classes decreases (e.g., 10, 15), the if statement is faster than the switch statement. For this particular test program, the performance gain dropped off around 17 or 18 classes.

In this particular implementation, each class is instantiated the same number of times as every other class. If certain classes are instantiated more than other classes, the tests for those classes could be positioned earlier in the if statement resulting in faster performance.

You can specify the number of classes to instantiate via the cutoff constant in the Tester class.

Using a Switch statement with Strings

Shortly after the initial release of this article, Wesner Moise pointed out that a switch statement works with strings. The implementation, defined as class SwitchNameFactory in file SwitchNameFactory.cs, is as follows:

  public class SwitchNameFactory : BaseClassFactory
  {
    public override BaseElement Create(string name)
    {
      switch (name)
      {
        case "xsl:apply-imports":
          return new XslApplyImports();

        case "xsl:apply-templates":
          return new XslApplyTemplates();
        ...
      }
    }

Like the IfNameFactory, it does not need a hash table. All of the logic and maintenance is contained within the switch statement. When it comes to performance, it can be slower than the HashSwitchFactory but faster than the IfNameFactory as the number of classes increase.

Using an element factory

M. Veurman presented another implementation with the focus on being object-oriented. The implementation, contained in unit ElementFactory.cs, defines an interface representing a class factory that instantiates an XSLT element. It then defines a concrete class factory for each XSLT element. The interface and sample class are shown in the following code snippet:

  // Interface for the element factory.

  public interface IXslElementFactory
  {
    string ElementName
    {
      get;
    }

    BaseElement Create();
  }

  // Concrete classes for element factories.

  public class XslApplyImportsFactory : IXslElementFactory
  {
    public string ElementName
    {
      get { return "xsl:apply-imports"; }
    }

    public BaseElement Create()
    {
      return new XslApplyImports();
    }
  }
  ...

The class factory, named ElementFactory, registers each element factory in its constructor. The registration maps the element factory's element name to an instance of the element factory.

  public class ElementFactory : BaseClassFactory
  {
    Hashtable _ElementHash;

    public ElementFactory()
    {
      // Create the hash table.

      _ElementHash = new Hashtable(50, (float)0.5);

      // Register the element class factories.

      RegisterElementFactory(new XslApplyImportsFactory());
      RegisterElementFactory(new XslApplyTemplatesFactory());
      ...
    }
    ...
    private void RegisterElementFactory(IXslElementFactory factory)
    {
      _ElementHash.Add(factory.ElementName, factory);
    }

The ElementFactory.Create method retrieves the element factory from the hash table and calls the element factory's Create method.

    public override BaseElement Create(string name)
    {
      IXslElementFactory factory = (IXslElementFactory) _ElementHash[name];
      return factory.Create();
    }

This implementation is certainly object-oriented. Performance-wise, it fairs very well. When measured with this test program, it comes in at 2nd place when 18 or more classes are being produced by the class factory. However, being object-oriented has a cost in this situation. There are more lines of code required for each class supported by the class factory. This may be good or bad depending upon the person and project.

Performance measurements

The release version of the test program was executed on a computer with 2.4 GHz Pentium 4 CPU and 512 MB of RAM. Each test was executed 10 times, with each pass instantiating one million objects. The times reported are the average of the 10 executions. The factory columns are ordered from fastest to slowest for the instantiation of 24 classes.

Classes instantiated Hash-Switch Element Factory Switch-Name If-Name System.Activator
5 273 ms 279 ms 296 ms 98 ms 1303 ms
10 267 ms 275 ms 287 ms 171 ms 1298 ms
15 264 ms 275 ms 286 ms 237 ms 1303 ms
18 265 ms 276 ms 287 ms 281 ms 1300 ms
20 268 ms 282 ms 290 ms 312 ms 1300 ms
24 273 ms 289 ms 293 ms 362 ms 1309 ms

Conclusion

Each type of implementation for a class factory has different degrees of performance and flexibility. This test program had to create one million instances of an object in order to show noticeable differences in performance. Will many real life projects need to create that many objects in a short amount of time? Probably not. So for most situations, maintenance may be a higher priority than performance when deciding which implementation to use.

When it comes to maintenance, the implementations could be ranked as follows, from least maintenance required to most maintenance required. Note that the actual difference between the first two sets of factories is just a couple lines of code.

  • SwitchNameFactory, IfNameFactory
  • HashSwitchFactory, ActivatorFactory
  • ElementFactory

If being object-oriented is a priority, the ElementFactory is the most object-oriented of the bunch and has very good performance.

If a class factory is to be used in situations where small numbers of objects are created and the time spent creating those objects is not important, using the ActivatorFactory can improve the maintainability and elegance of the code.

If the class factory will be creating large numbers of objects and there are a relatively small number of classes that may be instantiated (e.g., 20 or less), the best performance is achieved using the IfNameFactory. Performance may be improved by testing for frequently instantiated classes near the beginning of the if statement.

If the class factory will be creating large numbers of objects and there are twenty or more classes that may be instantiated, the best performance could be achieved by using either the HashSwitch, ElementFactory, or SwitchNameFactory.

History

  • Jan 7th, 2003 - Initial release
  • Jan 17th, 2003 - Refactored the code to show actual class factory implementations. Added Element Factory and Switch on Name implementations.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here