CSharpNoob
CSharpNoob

Reputation: 1301

Factory Initializer + Singleton

I wrote a non-static Generic class instantiator for my Abstract Factory design, and use Singleton approach to make sure that only 1 instance of the instantiator will be initialized for every client request.

public sealed class FactoryInstantiator<T> where T: class
{
    private static readonly FactoryInstantiator<T> _instance = new Instantiator<T>();
    public static FactoryInstantiator<T> Instance
    {
        get
        {
            _client = HttpContext.Current.Session["ClientCode"].ToString();
            return _instance;
        }
    }

    private static string _client;
    private string _className;
    private string _fullyQualifiedClassName;
    private string _assemblyName;

    private FactoryInstantiator() { }

    public T CreateInstance()
    {
        string fullClassName = typeof(T).ToString();
        string[] splitClassName = fullClassName.Split('.');
        _className = splitClassName[2];
        _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
        _fullyQualifiedClassName = _assemblyName + "." + _className;

        return (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
    }
}

I abstracted the the whole namespace for each Client

namespace InventorySuite.Factory.BusinessLogic
{
    // abstract factory
    public abstract class InvoiceFactory
    {
        public abstract void Set() { }
    }
}

namespace InventorySuite.Client1.BusinessLogic
{
    // concrete invoice class for Client1
    public class Invoice : InvoiceFactory
    {
        public override void Set() { }
    }
}

namespace InventorySuite.Client2.BusinessLogic
{
    // concrete invoice class for Client2
    public class Invoice : InvoiceFactory
    {
        public override void Set() { }
    }
}


protected void Page_Load(object sender, EventArgs e)
{       
    InvoiceFactory clientInvoice;

    Session.Add("ClientCode", "Client1");
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();

    Session["ClientCode"] = "Client2";
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();

}

It works well and already tested it, but my question is about its efficiency/performance hit, since I use reflection here, and for the Singleton approach if it has multi-threading issues (afaik, the singleton instance will be shared in all clients). I will also appreciate any other approach on this. thanks

Upvotes: 0

Views: 554

Answers (3)

CSharpNoob
CSharpNoob

Reputation: 1301

Loading the assemblies in Session State to solve multi-threading issue.

   public T CreateInstance()
    {
        string fullClassName = typeof(T).ToString();
        string[] splitClassName = fullClassName.Split('.');
        _className = splitClassName[2];
        _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
        _fullyQualifiedClassName = _assemblyName + "." + _className;


        T obj;
        var assemblies = HttpContext.Current.Session["ASSEMBLIES"] as Dictionary<string, T>;

        if (assemblies == null)
        {
            assemblies = new Dictionary<string, T>();
            assemblies.Add(_fullyQualifiedClassName, null);
            HttpContext.Current.Session.Add("ASSEMBLIES", assemblies);
        }

        obj = assemblies[_fullyQualifiedClassName] as T;

        if (obj == null)
        {
            obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
            assemblies[_fullyQualifiedClassName] = obj;
        }



        return obj;
    }

Upvotes: 0

CSharpNoob
CSharpNoob

Reputation: 1301

using Richard and Daniel suggestions, I was able to reduce the performance hit of reflection using Caching. I therefore conclude that Reflection really has huge performance issues.

  public T CreateInstance()
    {
        string fullClassName = typeof(T).ToString();
        string[] splitClassName = fullClassName.Split('.');
        _className = splitClassName[2];
        _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
        _fullyQualifiedClassName = _assemblyName + "." + _className;

        // use caching
        T obj;
        if (HttpContext.Current.Cache[_fullyQualifiedClassName] == null)
        {
            obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
            HttpContext.Current.Cache.Insert(_fullyQualifiedClassName, obj, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
        }
        else
        {
            obj = (T)HttpContext.Current.Cache[_fullyQualifiedClassName];
        }

        return obj;
    }


protected void Page_Load(object sender, EventArgs e)
{
    InvoiceFactory inv;

    Stopwatch globalTimer = Stopwatch.StartNew();

    //normal instantiation
    globalTimer = Stopwatch.StartNew();
    for (int x = 0; x <= 10000; x++)
        inv = new InventorySuit.Client1.BusinessLogic.Invoice;
    globalTimer.Stop();
    Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
    //result 0ms


    // using singleton factory w/o caching
    globalTimer = Stopwatch.StartNew();
    for (int x = 0; x <= 10000; x++)
        inv = new FactoryInstantiator<InvoiceFactory>().CreateInstance();
    globalTimer.Stop();
    Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
    //result 129ms

    // using singleton factory w/ caching
    for (int x = 0; x <= 10000; x++)
        inv = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    globalTimer.Stop();
    Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
    //result 21ms



}

Upvotes: 0

Daniel Pe&#241;alba
Daniel Pe&#241;alba

Reputation: 31847

You will not have any multi-threading issue since you're creating a new instance every time.

About the performance. You can measure the time creating 100 instances:

long ini = Environment.TickCount;
for (int i = 0; i < 100; i++)
{
    Session["ClientCode"] = "Client2";
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();
}
long timeCreate100Instances = Environment.TickCount - ini;

Using reflection, the performance hit resides in loading the assembly. I think that in your case, the class you're loading is in the same dll, you you also will not experiment any performance issue.

In other case, you can cache the Assembly obejcts in a Hastable/Dictionary in your CreateInstance() method.

Upvotes: 1

Related Questions