In API design, strive for clarity over convenience. Convenience often leads to confusion, confusion is chaos, chaos is not convenient.
Clarity of an API must be the overall goal.
Instead of
interface A extends B{
...
}
to make it convenient to use A's as B's, better add a method
interface A {
B asB();
...
}
Interesting, but I don't buy it, at least not in the general case. I guess you want to do this because in the first example, it's not obvious to users of the API that an instance of A can also be used as a B. The second case makes this more obvious, the asB() method gives a clear hint.
ReplyDeleteBut simply choosing better names for A and B might solve the problem in a convenient and clear way. Think of Iterator and CloseableIterator. There is little doubt that the second one can be used like an Iterator, and please don't give me an asIterator() method on CloseableIterator.
Also, in a well-designed API, the methods that A inherits from B should be a clear hint that an instance is also a B. Even if my CloseableIterator is called WeirdThingy, the presence of hasNext() and next() will make me wonder if I can use this weird thingy in places where an Iterator goes, because hasNext() and next() evoke “iterator-ness” in Java programmers.
So maybe you are just covering up other design problems by using the asB() trick, like counter-intuitive types and confusing class hierarchies.
On the other hand, your dictum helps to keep type hierarchies flat, which I like.
Quoting Phil Karlton: “There are only two hard problems in Computer Science: cache invalidation and naming things.”
Right. Sometimes it *is* a great idea, like in your example.
ReplyDeleteI had an example in mind, where I have a set of model entities which haven an ID. Years ago I thougt: Wow, let simply IModelEntity implement ID. The trouble comes when you consider how to implement hashCode, equals and compareTo. You can have only ONE implementation. So suddenly comparing ModelEntities is by IDs, never by content. What a horrible design implication!
Maybe my rant applies mostly to data objects.
Makes sense. A ModelEntity is an ID? Or does it have one? Intuitively, the latter sounds better, so “ModelEntity extends ID” seems wrong.
ReplyDelete