JAL Computing

C++COMProgramming .NET Mac Palm CPP/CLI Hobbies

 

Home
Up

Chapter 35 "A More Efficient Class Factory"

The use of a type based class factory (late binding) is not as efficient as a static class factory (early binding) as discussed at MSDN: Efficient Reflection. If efficient object creation is essential it is possible to combine both early and late bound object creation into one method. Classes that are known at compile time are handled in a base class factory, BaseFactory, that uses early binding. Classes that are discovered at runtime are handled in a derived class factory, RuntimeFactory, using late binding. Request are first handled by the RuntimeFactory. Any un-handled request can then be passed to the early bound BaseFactory.

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
// although a type based factory is simple and allows addition of types at
// runtime through discovery it is less efficient than early binding
// this project demonstrates the use of inheritance to combine the efficiency
// of early binding of classes known at compile time and late binding
// for classes discovered at runtime.
// This project demonstrates
// 1) the reuse of code in an existing class factory at compile time 
// 2) the ability to mix late binding at runtime with early binding at compile time
// into a single class factory using simple inheritance
namespace ClassFactory
{
    // declare our interfaces
    // this class would be in a separate dll
    public interface IDrawable
    {
        void Draw();
    }
    // define two concrete classes in this project
    public class Circle : IDrawable
    {
        public void Draw() { System.Console.WriteLine("Circle"); }
    }
    public class Square : IDrawable
    {
        public void Draw() { System.Console.WriteLine("Square"); }
    }
    public class Oval : IDrawable
    {
        public void Draw() { System.Console.WriteLine("Oval"); }
    }
    // if this class is to be discovered at runtime as a plugin
    // it would be in a separate dll at runtime
    public class Triangle : IDrawable
    {
        public void Draw() { System.Console.WriteLine("Triangle"); }
    }
    // BASE_FACTORY A class factory that returns concrete objects of type IDrawable
    // using early binding of known classes at compile time
    public class BaseFactory
    {
        public virtual IDrawable GetInstance(string key)
        {
            //if (key == null) { return null; }
            if (key == "Circle") 
            {
                return new Circle();
            }
            else if (key == "Square")
            {
                return new Square();
            }
            else
            {
                return null;
            }
        }
        /*[STAThread]
        public static void Main(string[] args)
        {
            BaseFactory factory= new BaseFactory();
            IDrawable c = factory.GetInstance("Circle");
            if (c != null) { c.Draw();}
            IDrawable s = factory.GetInstance("Square");
            if (s != null) { s.Draw(); }
            System.Console.ReadLine();
        }*/
    }
    // DERIVED_FACTORY demonstrates reuse of code by extending from BaseFactory and 
    // adding a new concrete class Triangle at _compile_ time
    // still using early binding
    public class DerivedFactory : BaseFactory
    {
        public override IDrawable GetInstance(string key)
        {
            // if (key == null) {return null;}
            if (key == "Oval") 
            {
                return new Oval();
            }
            else
            {
                return base.GetInstance(key);
            }
        }
        /*[STAThread]
        public static void Main(string[] args)
        {
            DerivedFactory factory = new DerivedFactory();
            IDrawable c = factory.GetInstance("Circle");
            if (c != null) { c.Draw(); }
            IDrawable s = factory.GetInstance("Square");
            if (s != null) { s.Draw(); }
            IDrawable t = factory.GetInstance("Oval");
            if (t != null) { t.Draw(); }
            System.Console.ReadLine();
        }*/
    }
    // RUNTIME_FACTORY demonstrates reuse of code in the base factory but demonstrates
    // adding new IDrawables at _runtime_
    // using late binding and reflection
    // more efficient than a type based factory alone since this solution
    // uses early binding for compile time classes and late binding for plugins
    // We basically handle plug in classes first and then pass any un-handled
    // request to the base factory
    public class RuntimeFactory : BaseFactory
    {
        private Hashtable hashTypes = new Hashtable(); // must contain only IDrawable
        public RuntimeFactory()
            : base()
        {
            // you would need to discover ITriangle classes at runtime
            // from a plugins folder and add them here
            // if (typeof(IDrawableShape).IsAssignableFrom(t)
            //		&& !t.IsAbstract)) // safer
            //	{
            //		...
            //	}
            Type t = typeof(Triangle);
            this.hashTypes.Add(t.Name, typeof(Triangle));  // triangle plugin
        }
        public override IDrawable GetInstance(string key)
        {
            if (key != null && this.hashTypes.ContainsKey(key)) // case sensitive!
            {
                Type t = (Type)hashTypes[key];
                // dynamically load this class
                return (IDrawable)Activator.CreateInstance(t);
            }
            else
            {
                return base.GetInstance(key);
            }
        }
        [STAThread]
        public static void Main(string[] args)
        {
            System.Console.WriteLine("Early Bound Classes:");
            DerivedFactory factoryD = new DerivedFactory();
            IDrawable c = factoryD.GetInstance("Circle");
            if (c != null) { c.Draw(); }
            IDrawable s = factoryD.GetInstance("Square");
            if (s != null) { s.Draw(); }
            IDrawable t = factoryD.GetInstance("Oval");
            if (t != null) { t.Draw(); }
            System.Console.WriteLine("Early and Late Bound Classes.");
            RuntimeFactory factoryR = new RuntimeFactory();
            IDrawable c1 = factoryR.GetInstance("Circle");
            if (c1 != null) { c1.Draw(); }
            IDrawable s1 = factoryR.GetInstance("Square");
            if (s1 != null) { s1.Draw(); }
            IDrawable t1 = factoryR.GetInstance("Triangle");
            if (t1 != null) { t1.Draw(); }
            System.Console.WriteLine("Test null input");
            IDrawable n = factoryR.GetInstance(null);
            if (n == null) { System.Console.WriteLine("No Class"); }
            System.Console.WriteLine("Test invalid input");
            IDrawable invalid = factoryR.GetInstance("oval");
            if (invalid == null) { System.Console.WriteLine("No Class"); }
            System.Console.ReadLine();
        }
    }
}

Early bound object creation is done using the keyword new as in:

            if (key == "Circle") 
            {
                return new Circle();
            }

Late bound object creation is done using reflection and Activator.CreateInstance as in:

            if (key != null && this.hashTypes.ContainsKey(key)) // case sensitive!
            {
                Type t = (Type)hashTypes[key];
                // dynamically load this class
                return (IDrawable)Activator.CreateInstance(t);
            }

 

- 30 -
Send mail to [email protected] with questions or comments about this web site. Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 © 
Last modified: 08/04/09
Hosted by www.Geocities.ws

1