I would regard the approach as being in many circumstances the best possible in a framework which does not allow interfaces to include public static helper methods. I would consider
IFoo.Create() preferable to
Foo.Create() if such a thing were allowed, but unfortunately (at least in .NET languages) it isn't.
Despite the unfortunate naming of the factory method, I think such an approach is optimal in cases where:
- There exists a single class which will be a reasonably good fit for the needs of code which won't need to use any features beyond those implemented by the interface.
- Some clients may need things which can be used as implementations of the interface but which support other abilities as well, and the optimal internal design of an implementation which supports the added features may be totally different from the optimal design for an implementation which doesn't.
- Methods which can be used with basic implementations of the interface should also be usable with the fancier implementations.
If a method takes a parameter of the "ordinary implementation class" type, such a method will not be useful with any of the fancier implementations. Thus, unless there is some reason to have a method reject the fancier implementations, methods should generally use the interface type. If a variable will never be passed to any methods except as an interface type, then the variable itself should probably be declared as that same interface type.
Although in situations involving a parameterless constructor/factory there may not be much advantage to using
IWidget foo = Widget.Create() over
IWidget foo = new Widget(), a factory method which accepts parameters can vary the type of the created object based upon the parameter values it is given. This can be especially useful in cases involving immutable types. For example, a method
ImmutableSequence<T>.Create(IEnumerable<T> source); could copy
source to an array and encapsulate that in an immutable wrapper in cases where no obviously-better alternative behavior suggests itself, but in cases where it recognizes the source type it might be able to do something better. For example, if
source instance implements an
AsImmutableSnapshot() method, the
Create method could chain to that and avoid having to create an extra layer of nesting.