As is the nature of using interfaces, this decouples my users from being concerned with how I variously decide to refactor the implementations.
Let's take a step back and make sure we understand what the value of an interface is. It's not really what you've stated. Take the following example:
interface IFoo
{
void baz();
}
Now later you want to refactor this and realize the method shouldn't be named baz
but bar
instead. Can you do that refactoring without the users of the interfaces being concerned about it? No. How about adding or changing parameter types? No. You can add new methods to the interface without breaking things but that doesn't mean the user is not or should not be concerned. Nothing about this changes by using an interface or not defining one.
What about refactoring the implementation? You can (carefully) change the implementation of a public method on a concrete class without users knowing or having concerns about it. You can also change method's implementation to be incompatible with previous versions. Putting an interface in front of that doesn't really change anything with those two scenarios.
The only point of an interface is to define a contract which is independent of a specific concrete class. In other words, the client should not need to care which implementation it is dealing with. They are expected to be interchangeable with regard to the methods defined on the interface. It shouldn't even matter to the client whether the contract is defined on an interface or on a concrete class. All an interface does is decouple a set of public method signatures from a concrete class definition. It is a formal way to allow for substitution of implementations.
I think the C# Hungarian wart standard of prefixing interfaces with 'I' is misguided (and misguiding.) Interfaces don't do anything. There's no reason developers need to be concerned with whether the type they are working with is an interface. A lot of people might think this is just a preference but it creates a real issue. This standard means that you can't start with a class and then later decided it should be an interface without breaking everything that was using it. Therefore, C# APIs are so packed with interfaces that have a single implementation that I believe it has lead to a lot of developers who 'grow up' in C# not understanding the point of them.
In a nutshell, interfaces only support adding additional concrete implementations of a contract and especially allowing users to do so. And, naming standard aside, they can be used to do so without having to introduce them prematurely as long as you don't allow clients to bind to anything other than public methods. If you follow the 'I' wart standard, you must create an interface for every class you might ever want to later provide more implementations of without breaking user code.
interface
is public but an implementation is internal. Then you provide a mechanism to register the implementation with DI (services.AddMyThing();
). The slight hiccup is that when unit testing, you have to use [InternalsVisibleTo] (or the csproj equivilent), to allow the unit test project to see the implementation class.