The top answer of "because that's how uncle Bob chose to phrase it" is correct, but I'd like to expand on why we would come up with the I
prefix in the first place, before uncle Bob railed against it.
I would like to offer the interpretation here that the origin of the I
prefix for interfaces is not because we want consumers to know it's an interface, but rather that it was introduced as a way to avoid needing to come up with two synonyms in the case where there is currently only one implementation of said interface and developers are struggling to describe the interface differently than the implementation.
Let's start from a point of agreement: even if only one implementation currently exists, we should already use an interface as an abstraction on top, so that we can later introduce different implementations of the same interface. If you disagree with this premise, you're not going to agree with anything going forward.
So, we have a class called FooCalculator
. What should we name its interface? Now that there's only one possible implementation, it's hard not to describe the interface as one that "calculates Foos". But we can't reuse FooCalculator
here (and I'm specifically ignoring namespacing as a solution here, because at the end of the day, while clever, it would still negatively impact readability).
We could come up with a synonym... well, a calculation is also called a computation, to we could use FooComputer
as the interface name.
I put good money on it that it won't take long for you to reference the interface when you needed to reference the class and vice versa, because the names are interchangeable and which is which is purely an artifact of which we wrote first and which name we came up with first. That's not a good naming system.
Instead of looking for synonyms, we could instead reuse the name, referring to the interface as "the interface of the Foo
calculator", which is actually a very apt way to put it. Well, that's exactly what IFooCalculator
tells you: I
-Foo
-Calculator
.
Note that you could also invert this and use the "real" name for the interface. This is why you see some languages sometimes favor FooCalculator
for the interface and FooCalculatorImpl
for the implementation. It's the same solution, but the other way round.
This would be different if we already had multiple implementations that we wanted to build from the get-go. For example, if we're thinking of implementations of different ways to calculate Foo
, e.g. one by a very expensive process, the other by a much easier approximation, a third by asking an external API, ...; then it would be natural for FooCalculator
to the be interface and PreciseFooCalculator
, ApproximateFooCalculator
and OnlineFooCalculator
to be the implementation names. No conflict here.
But this is not how most codebases work. We don't start with multiple implementations, we start with one and we prepare for future extension. Annoyingly, we are already forced to come up with a name even though we only have one implementation, and it's really difficult to figure out the link by only observing one thing, plus it might even be the case that we don't even know how we might want to create variant, making it impossible to come up with a correct name before we even know what it is that we're trying to capture and express. Maybe the variation isn't that there's multiple ways of calculating Foo
, but that we want to be able to calculate Bar
the same way. That's a different scenario that would lead to a different shared interface name.