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 RequestEvent
s and ReportEvent
s. 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