Say I have a singleton-ish, factory-ish, reflection-ish class that receives some input, and spits back a new instance of a concrete implementation of some interface. What kind of design is this? Is there a better way to do what I want?
Here’s some code to illustrate the point:
using System;
using System.Collections.Generic;
// static factory class
public static class ArticleFactory
{
// given an SKU, store the Type object for an IArticle object
private static Dictionary<string, Type> articleRegistry = new Dictionary<string, Type>();
// allow public registration of SKU-to-Type object relationships
public static bool Register(string sku, Type typeInfo)
{
if(!articleRegistry.ContainsKey(sku))
{
articleRegistry.Add(sku, typeInfo);
return true;
}
return false;
}
// given a SKU, give me an instance of the related IArticle object
public static IArticle NewArticle(string sku)
{
if(articleRegistry.ContainsKey(sku))
{
// use reflection to invoke the default constructor
return articleRegistry[sku].GetConstructor(Types.EmptyTypes).Invoke(null) as IArticle;
}
return null;
}
}
// example concrete-implementation of an IArticle
public class Jeans : IArticle
{
public decimal GetPrice() { return SomeDecimal(); }
}
// WHERE DO I CALL THIS LINE?
ArticleFactory.Register("0929-291", typeof(Jeans));
// Later on, if another group needs to write the class for Snowboards,
// how can they self-register their class, without changing any "Main()"
// or "Page_Init()" function?
Looks like you’ve already identified the pattern. It’s the Factory Method Pattern. Or rather, a somewhat half-baked implementation of one. A slightly better approach would be to first make it an interface:
Then implement the factory without any Reflection at all:
A few important differences:
Registration isn’t public, nor should it be. Registration usually either resides in a configuration file somewhere or is private.
Does not require the
IArticleconcrete types to have a default parameterless constructor. This can easily register articles with parameterized constructors (as long as it knows what parameters to use).Throws an exception on duplicate registrations. I don’t like the idea of simply returning
false; if you try to register the same factory method twice, that ought to be considered a bug.It’s not static. You can replace this factory with a different factory. You can unit-test it.
Of course, an even better approach would just be to use any of the myriad of existing .NET Dependency Injection/Inversion of Control Frameworks, such as Ninject or AutoFac.