Stephan van Hulst wrote:For classes that represent service types and not value types, I really like them to implement a service interface, even if the class in question is the obvious standard implementation of that interface. I do this for two reasons:
First, even if the class is a good standard implementation, I'm not sure if I might not want to change the implementation in the future anyway. Swapping out the implementing class for another makes the transition between the two implementations less of a hassle in regards to source control operations. Just keep the old class until you're sure you no longer need it. If your new implementation doesn't work out, there's no need to revert any commits: Just swap the old class back in.
Secondly, I prefer mocking service interfaces over mocking service implementations. Mocking interfaces is more "portable". By that I mean you can do it with a wider variety of mocking frameworks, and even with those that don't support heavy use of reflection. That in turn speeds up unit tests, which can really add up for a large code base where a complete test run can take many tens of minutes.
However, I'm definitely not a fan of the -Impl naming scheme. I prefer an implementation of the FooService interface to be named something like StandardFooService or DefaultFooService.
With your first point, I'm going with YAGNI on that one. It's not hard to add an interface in after the fact, IMO. And if I want to communicate that it's a service of some kind, I'd put Service at the end of the name (which is much more meaningful), or it'll at least fall under a 'service' package. The justification for
not adding it "just in case" in my mind comes from the sheer amount of code bloat and technical debt that software inevitably ends up piled under. Minimizing all that junk is a top priority for me personally, way above "in the future this thing might happen". Simplicity goes a LONG way in keeping code from becoming unmaintainable over 1-2 decades, as I'm sure you're well aware.
With the second, I'd say my complaint is
somewhat not applicable to that case. Test implementations are an obvious use case where you might be tempted to do this (and of course that means you do have two implementations, even if only one in production). I say it's only
somewhat non-applicable because even in that case, I'd come up with a better name such as the "Default" prefix you suggested. Admittedly my argument is slightly weaker in that case.
Yep, I fully agree with your naming convention.