Search code examples
c#design-patterns

Pros and cons for factory method with Activator.CreateInstance and static abstract interface?


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?

With 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}") { }
}

With static abstract interface

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);
}

Solution

  • 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.