Click to See Complete Forum and Search --> : Building a better factory


DeepT
February 14th, 2008, 12:14 PM
I have been using relfection to help me make object factories for a while. Currently I am doing something like this:

Base Class:
public abstract class AWPESD_BASE
{
public class AWPESD_Builder
{
public delegate AWPESD_BASE Builder();
public Builder Build;

public AWPESD_Builder(Builder Bld)
{
Build = Bld;
}
}

static AWPESD_BASE()
{
Assembly Asm = Assembly.GetExecutingAssembly();
Type[] Types = Asm.GetTypes();
object[] Prams = new object[0];
foreach (Type T in Types)
{
if (true == T.IsSubclassOf(typeof(AWPESD_BASE)))
{
MethodInfo Method = T.GetMethod("RegisterWith_AWPESD_BASE");
if (null != Method)
{
Method.Invoke(null, Prams);
}
}
}
}

private static Dictionary<string, AWPESD_Builder> SubBuilders = new Dictionary<string, AWPESD_Builder>();

protected static void RegisterSubClass(string TypeName, AWPESD_Builder Bld)
{
SubBuilders.Add(TypeName, Bld);
}

public static AWPESD_BASE Create(string TypeName)
{
AWPESD_Builder Bld;
AWPESD_BASE Data = null;

if (true == SubBuilders.TryGetValue(TypeName, out Bld))
{
Data = Bld.Build();
}

return Data;
}

SubClass:
public class AWPESD_NumRange : AWPESD_BASE
{


public AWPESD_NumRange()
{
}



private static string XMLTag = "AWPESD_NumRange";

public static void RegisterWith_AWPESD_BASE()
{
RegisterSubClass(XMLTag, new AWPESD_Builder(Build));
}

public static AWPESD_BASE Build()
{
return new AWPESD_NumRange();
}



This works well and good, but I am thinking there is likely an even cleaner way to do this.

One thing this lacks is the benefit of "static abstract" which only makes sense in the context of reflection. The "static" part is needed because I do not know how to create instances of a class without directly using it's type or a delegate. In the above example, the builder method is static so it can exist without an instance of the class, and thus the register method can create a delegate which is then added to the sub-type collection.

The abstract part, which does not exist in this example, would ensure that all sub-classes MUST implement the Build() method. The build method, can't be anything but static since the actual instance doesn't exist, and as we all know, if something is static, it can't be abstract.

If I could figure out how to create an instance of an object via "something" I got through reflection, then I could drop the Builder part.

Ideally Factory 2.0 would NOT require the sub-classes to do anything special (like have static methods) and would be able to create a list of sub-classes by the sub-class name. I could then just pass in the sub-class name and have it somehow create an instance of the sub-class.

Any ideas on how to do this? My current method works fine, it is just a bit clunky.

TheCPUWizard
February 14th, 2008, 12:22 PM
Looks like a pretty good start (and like many implementations I have seen).

Why do you even need the Build Method at all. You could either:

1) use Activator.CreateInstance
2) use Reflection to Get Constructors.

As a side note, I would like to suggest a bit of refactoring, that can simplify your code, and dramatically improve performance.

Reflection is costly, even some things that you would think ae "simple" take a significan amount of time (run a performance on Type.IsGeneric :eek: ).

What I do, is to structure things so that reflection only occurs ONCE during the application. I then build custom classes which hold the necessary results. For example (over simplified) a Dicionary<Type, ConstructoInfo> would allow you to quickly have a factory which given a Type provides an instance. Even better a Dictionary<String, ConstructorInfo> would allow the factory to be 100% extensible and callers to actually be totally un aware of the type that was being built!

DeepT
February 14th, 2008, 01:29 PM
All that reflection is done only once when the application starts. The static constructor of the base class does the reflection and the results are stored in a dictionary. After that, the class is just looked up and the "Builder" is called.

Now instead, if the builder could be a reference to a constructor, that would be even better because I would not need to create static builder methods in the sub-classes.

Ill look into those methods you mentioned.

DeepT
February 14th, 2008, 02:15 PM
Ok, just messed with it and here is my new factory pattern:
static AWPAFS()
{
Assembly Asm = Assembly.GetExecutingAssembly();
Type[] Types = Asm.GetTypes();
foreach (Type T in Types)
{
if (true == T.IsSubclassOf(typeof(AWPAFS)))
{
ConstructorInfo Constructor;
Constructor = T.GetConstructor(Type.EmptyTypes);
SubBuilders.Add(T.Name, Constructor);

}
}
}

private static Dictionary<string, ConstructorInfo> SubBuilders = new Dictionary<string, ConstructorInfo>();


public static AWPAFS Create(string TypeName)
{
ConstructorInfo Constructor;
AWPAFS Data = null;

if (true == SubBuilders.TryGetValue(TypeName, out Constructor))
{
Data = (AWPAFS) Constructor.Invoke(new object[0]);
}

return Data;
}

Thanks, that does work out the way I want (although I have not done a lot of deep testing yet). There is one more thing which would help this setup out a lot from the opposite point of view, saving data instead of loading it.

Is there an easy way to get the type name of an instance? For example, I have a "AWPAFS" reference, which is really some sub-class. When I am saving this data into an XML document, Ill want the exact type so I can pass it into the factory when I load it. Here is how I do that now:
xmlOut.WriteStartElement("Settings");
xmlOut.WriteAttributeString("Type", Settings.XMLType);

Where the settings base class has:
public string XMLType
{
get
{
return GetXMLType();
}
}

protected abstract string GetXMLType();

Which requires all the sub-classes to define the GetXMLType() method which would just return a cut and paste of the class name. It would be nice if I could eliminate that too.

If all that can be done, then I guess the next and final small step would be some incredibly ultra-generic XML loader / saver where a saver might be able to do something like:

xmlOut.WriteStartElement(MyClass.Member.MemberName);
xmlOut.WriteAttributeString("Type", MyClass.Member.MemberType);
MyClass.Member.Save();

and the loader might be something like:
while (true == xmlIterEle.MoveNext())
{
switch (xmlIterEle.Current.Name)
{
.....

case MyClass.Member.MemberName:
MyClass.Member = Factory.Make(xmlIterEle.Current.GetAttribute("Type", string.Empty));
break;

......

}
}

I do not know if it is possible to get that generic though.

TheCPUWizard
February 14th, 2008, 02:24 PM
You really missed the obvious here.....

Object.GetType()

:wave: :wave: :wave:

Mutant_Fruit
February 14th, 2008, 02:26 PM
Is there an easy way to get the type name of an instance?

myObject.GetType().Name :)

edit: Doh, beaten to it ;)

DeepT
February 14th, 2008, 02:33 PM
Dammit. Before I posted that question I tried that with "object".

I put in object. and the context help didn't show a GetType() method so I thought it was not part of the class. Apparently it is a member of all my classes. Thanks.

TheCPUWizard
February 14th, 2008, 03:08 PM
object o = new string();
Type t = o.GetType();


Worked fine for me, including IntelliSense....

DeepT
February 14th, 2008, 03:19 PM
No, I mean literal "object." not an instance.

TheCPUWizard
February 14th, 2008, 03:42 PM
Well, if you want to get the type of an instance.... ;)

Also dont forget about Type.GetType() http://msdn2.microsoft.com/en-us/library/system.type.gettype(VS.71).aspx