In layman's terms:
Interfaces are for "can do/can be treated as" type of relationships.
Abstract ( as well as concrete ) classes are for "is a" kind of relationship.
Look at these examples:
class Bird extends Animal implements Flight;
class Plane extends Vehicle implements Flight, AccountableAsset;
class Mosquito extends Animal implements Flight;
class Horse extends Animal;
class RaceHorse extends Horse implements AccountableAsset;
class Pegasus extends Horse implements Flight;
Bird
, Mosquito
and Horse
are Animals
. They are related. They inherit common methods from Animal like eat(), metabolize() and reproduce()
. Maybe they override these methods, adding a little extra to them, but they take advantage of the default behavior implemented in Animal like metabolizeGlucose().
Plane
is not related to Bird
, Mosquito
or Horse
.
Flight
is implemented by dissimilar, unrelated classes, like Bird
and Plane
.
AccountableAsset
is also implemented by dissimilar, unrelated classes, like Plane
and RaceHorse
.
Horse
doesn't implement Flight.
As you can see classes (abstract or concrete) helps you build a hierarchies, letting you inhering code from the upper levels to the lower levels of the hierarchy. In theory the lower you are in the hierarchy, the more specialized your behavior is, but you don't have to worry about a lot of things that are already taken care of.
Interfaces, in the other hand, create no hierarchy, but they can help homogenize certain behaviors across hierarchies so you can abstract them from the hierarchy in certain contexts.
For example you can have a program sum the value of a group of AccountableAssets
regardless of their being RaceHorses
or Planes
.
Best Answer
Invite him to learn about YAGNI. The Rationale part of Wikipedia page may be particularly interesting here:
Other possible arguments:
“80% of the lifetime cost of a piece of software goes to maintenance”. Writing code just in time reduces the cost of the maintenance: one has to maintain less code, and can focus on the code actually needed.
Source code is written once, but read dozens of times. An additional argument, not used anywhere, would lead to time wasted understanding why is there an argument which is not needed. Given that this is an interface with several possible implementations makes things only more difficult.
Source code is expected to be self-documenting. The actual signature is misleading, since a reader would think that
end
affects either the result or the execution of the method.Persons writing concrete implementations of this interface may not understand that the last argument shouldn't be used, which would lead to different approaches:
I don't need
end
, so I'll simply ignore its value,I don't need
end
, so I'll throw an exception if it is notnull
,I don't need
end
, but will try to somehow use it,I'll write lots of code which might be used later when
end
will be needed.But be aware that your colleague may be right.
All previous points are based on the fact that refactoring is easy, so adding an argument later won't require much effort. But this is an interface, and as an interface, it may be used by several teams contributing to other parts of your product. This means that changing an interface could be particularly painful, in which case, YAGNI doesn't really apply here.
The answer by h.j.k. gives a good solution: adding a method to an already used interface is not particularly hard, but in some cases, it has a substantial cost too:
Some frameworks don't support overloads. For example and if I remember well (correct me if I'm wrong), .NET's WCF doesn't support overloads.
If the interface has many concrete implementations, adding a method to the interface would require going through all the implementations and adding the method there too.