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
CreateInstanceis public and static, because interface methods cannot be static or have an explicit accessibility - Implementations of
CreateInstancemust return an instance ofIMyClass, instead of their own class - Implementations of
CreateInstancecannot 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
CreateInstancemethod 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
CreateInstancemust 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.