When writing a factory method, I can use either Activator.CreateInstance
or an interface with static abstract method as follows.
Are there pros and cons I have to consider here?
Activator.CreateInstance
using System;
var s = Factory<Student>("Anton");
s?.Print();
var t = Factory<Teacher>("Howard");
t?.Print();
Person? Factory<T>(string name)
where T : Person
{
return (T?)Activator.CreateInstance(typeof(T), name);
}
class Person
{
public string Name { get; set; }
public Person(string name) => Name = name;
public void Print() => Console.WriteLine(Name);
}
class Student : Person
{
public Student(string name) : base($"Student: {name}") { }
}
class Teacher : Person
{
public Teacher(string name) : base($"Teacher: {name}") { }
}
using System;
var s = Factory<Student>("Anton");
s.Print();
var t = Factory<Teacher>("Howard");
t.Print();
Person Factory<T>(string name)
where T : Person, IPerson<T>
{
return T.Constructor(name);
}
interface IPerson<T>
{
static abstract T Constructor(string name);
}
class Person : IPerson<Person>
{
public string Name { get; set; }
public Person(string name) => Name = name;
public void Print() => Console.WriteLine(Name);
public static Person Constructor(string name) => new(name);
}
class Student : Person, IPerson<Student>
{
public Student(string name) : base($"Student: {name}") { }
static Student IPerson<Student>.Constructor(string name) => new(name);
}
class Teacher : Person, IPerson<Teacher>
{
public Teacher(string name) : base($"Teacher: {name}") { }
static Teacher IPerson<Teacher>.Constructor(string name) => new(name);
}
Both appraoches are IMHO bad design, as they completey omit what generics are about: type-safety at compile-time. So what is the benefit in writing
var s = Factory<Student>("Anton");
or
var s = IPerson<Student>.Create("Anton");
over
var s = new Student("Anton");
?
In all three cases you need to know the type at compile-time, so there's no huge difference between these two. The only benefit on your static interface-approach is, that you get compiler-support when changing the signature of your Constructor
-method. But after all, an interface shouldn't make any assumptions on how to create instances. Interfaces define functionality.
A factory on the other hand only makes sense, if you do not know the types at compile-time, or if you want to hide them from your client, e.g.:
var s = MyFactory.Create(ReadTypeNameFromDatabase(), "Anton");
This way the factory decides which type to instantiate.
class MyFactory
{
public static IPerson Create(string typeName, string name)
{
return (IPerson) Activator.CreateInstance(typeName, name);
}
}
A client on the other hand just gets an abstraction, i.e. an interface-pointer (IPerson
for example).
As an asside: it might be a good idea to make Person
an abstract
class, so you cannot just create instances of it via new Person("Anton")
. However it depends on your data-model if that makes sense.