How to ensure that implementations have a static factory method in older versions of .NET?

57 views Asked by At

Overall, I'm looking for a clean and safe solution to write an interface/abstract class that ensures a private constructor and public static factory method on its implementation(s), in a context where default interface implementation is not an option, and without narrowing the return type of the factory method to an interface or abstract type.

Minimal factory method example:

public class ClassWithFactoryMethod
{
    private ClassWithFactoryMethod() { /* ... */ }
    public static ClassWithFactoryMethodCreateInstance() => new ClassWithFactoryMethod();
}

What I've tried already (and why it didn't work):

If using an interface to ensure the existence of the CreateInstance method, the following limitations arise:

  • We can't ensure that the implementation's constructor is private or that CreateInstance is public and static, because interface methods cannot be static or have an explicit accessibility
  • Implementations of CreateInstance must return an instance of IMyClass, instead of their own class
  • Implementations of CreateInstance cannot be static anyway, so interfaces cannot ensure that the class can be constructed using a public static factory method.
public interface IClassWithFactoryMethod 
{
    IClassWithFactoryMethod CreateInstance();
}

public class ClassWithFactoryMethod: IClassWithFactoryMethod
{
    private ClassWithFactoryMethod() { /* ... */ }
    public IClassWithFactoryMethod CreateInstance() => new ClassWithFactoryMethod();
}

Using an abstract class gives us slightly more control over access rights, but presents similar problems with type narrowing

  • Even if the abstract class declares a protected constructor, the implementation's constructor might be more accessible
  • We can ensure that the CreateInstance method is public by declaring it as public on the abstract class (as implementations cannot change access rights), but overridable methods cannot be made static in either the base or the implementation.
  • We cannot predict the specific type of the implementation, so CreateInstance must return an instance of (an implementation of) the abstract class, similar to the problem faced with interfaces.
  • Again similar to an issue faced by interfaces, the override factory method cannot be static, and therefore the abstract class cannot ensure a public static factory method.
public abstract class ClassWithFactoryMethodBase
{
    protected ClassWithFactoryMethodBase() { }
    public abstract ClassWithFactoryMethodBase CreateInstance();
}

public class ClassWithFactoryMethod : ClassWithFactoryMethodBase
{
    private ClassWithFactoryMethod() { /* ... */ }
    public override ClassWithFactoryMethodBase CreateInstance() => new ClassWithFactoryMethod();
}

Neither of these solutions can ensure that the factory method is public or static, and even if they could, both of them narrow down the return type of the factory method to their own non-instantiable types.

0

There are 0 answers