Using the factory pattern, and reflection (as explained in this blog post), you would get:
static void Main(string[] args)
{
ReportFactory<Report> factory = new ReportFactory<Report>();
Report r1 = factory.CreateObject("LandscapeReport");
Report r2 = factory.CreateObject("PortraitReport");
Console.WriteLine(r1.WhoAmI());
Console.WriteLine(r2.WhoAmI());
}
Which would output "Landscape" and "Portrait", respectivley.
Of course, for the plumbing, you need an interface that all your reports are based off of (which I assume you already have).
For this example:
public interface Report
{
string WhoAmI();
}
And the two implemenations:
public class PortraitReport : Report
{
public string WhoAmI()
{
return "Portrait";
}
}
public class LandscapeReport : Report
{
public string WhoAmI()
{
return "Landscape";
}
}
The secret is in the ReportFactory, which uses Reflection to see what other classes are based on Report, and automatically register them for use, which I think is pretty cool:
public class ReportFactory<Report>
{
private Dictionary<string, Type> reportMap = new Dictionary<string, Type>();
public ReportFactory()
{
Type[] reportTypes = Assembly.GetAssembly(typeof(Report)).GetTypes();
foreach (Type reportType in reportTypes)
{
if (!typeof(Report).IsAssignableFrom(reportType) || reportType == typeof(Report))
{
// reportType is not derived from Report
continue;
}
reportMap.Add(reportType.Name, reportType);
}
}
public Report CreateObject(string ReportName, params object[] args)
{
return (Report)Activator.CreateInstance(reportMap[ReportName], args);
}
}
So now all you have to do is just add any new implementations of Report in your assembly, and they will be available to the factory with no extra coding or changing other code files.