Elizabeth Keogh has been writing a couple of articles on naming of interfaces. In the process, she’s highlighted a more important issue: granularity of types.
IMHO opinion, base types (those that don’t inherit from other types) should be fairly fine grained. You can always aggregate types together to get coarse-grained types, but you can’t go back the other way.
(Types are represented in Java by both classes and interfaces. However, most of these comments apply to interfaces more so than classes)
What do I mean by fine-grained? Simple: not many methods, all of which are logically consistent. Clients to a type (either a caller or an implementer) should care about the vast majority of the methods.
This gives a heuristic for when you should extract a new type – when you have a client that only cares about a subset of the entire type. Elizabeth gave a good example: a Gui type and an Engine type need to talk to each other via RequestEvents and ReportEvents. Elizabeth wants to create an EventQueueHandler. With the coarse grained interfaces she has now, her EventQueueHandler class needs to implement both the Gui and Engine types – ouch! All it cares about is one method on each, and it needs the rest of the baggage!
The right way to solve it is to extract two new types: RequestEventListener and ReportEventListener. The EventQueueHandler implements both. The Gui implements the ReportEventListener, and the Engine implements the RequestEventListener. (Alternatively, the Event concept could be made more generic, at the cost of some typesafety)
To repeat:
Extract new types when a client only cares about a subset of the existing type
