Getting the right level of Interface granularity
I'm doing some API design work at present, involving the specification of a number of interfaces as abstractions that will later be implemented by various concrete classes.
As it happens, I am using Java, but I think the question is relevant to any language that supports a similar interface concept.
I've noticed that there is often an option between:
- Making a la开发者_开发知识库rge interface with a full range of methods
- Making multiple interfaces each containing a subset of the full range of methods (a single concrete class would probably have to implement several or all of these interfaces)
What are the pros / cons of each approach?
The pro of splitting out the interface is that you can divide the methods into groups of responsibilities that make sense to be together. The con is the your interface is now split into a bunch of smaller ones that one class might be implementing.
I would recommend splitting the interface out where it helps readability and no further. If you have one class that is implementing 10 interfaces, either those interfaces need to be combined into one, or possibly the class is taking to much responsibility and it really needs to be two or more classes.
In terms of anti-patterns I would say that too much interfaces may lead to the so called Yo-yo-problem:
http://en.wikipedia.org/wiki/Yo-yo_problem
And putting everything in a single interface may create the God object:
http://en.wikipedia.org/wiki/God_object
You should find your place somewhere in between :)
Good luck!
One issue not mentioned yet: interfaces which put items into a collection should be separate from those which take items out; a combined interface should inherit from both. Segregating interfaces in this way allows for covariance and contravariance. For example, a ReadableBunch(Of ToyotaPrius) may be safely passed to a routine expecting a ReadableBunch(Of Car) [since an object that gives out instances of ToyotaPrius will in so doing give out instances of Car] , and a WritableQueue(Of Car) may be safely passed to a routine expecting a WriteableQueue(Of HondaCivic) [since an object that can accept a Car will by definition accept a HondaCivic].
I don't know if this type of covariance and contravariance mean anything in Java, but since the question was tagged language-agnostic, the issue should be considered by anyone coding for platforms that support covariance and contravariance (e.g. .net)
Making multiple interfaces each containing a subset of the full range of methods
That approach would tend to work better with the design principle of "prefer composition to inheritence", because separate classes could each implement one (or a few) of the interfaces.
I don't have a nice answer to your question. API design is a bit of an art. If you are in the middle of a large design effort, I recommend you get yourself a copy of Practical API Design by Jaroslav Tulach of NetBeans fame.
I think he would recommend against too many methods, some of which might just be helper methods. You should expose the minimum needed to work with the API. The less verbose, the better.
I wouldn't advocate for an interface with too much methods, as I wouldn't for a class as well. The programmer that have to use such interfaces or the derived classes would have trouble understanding how they relate; furthermore trying to remember what they are and when to use is like juggling too many balls.
To me, in general, more than 1000 lines in a module is too much; more than 100 lines in a method too; more than 15 methods or so in a class or interface, too. There can be exceptions, of course, but I try to avoid them.
I'd define which interfaces you have before thinking which methods go into them. Consider what are the 'atomic behaviors' for each abstract entity in your system and make each entity an interface, composing with inheritance if needed. Then decide the methods - after that, probably there wouldn't be many in each interface.
The number of methods in an interface should be driven only by its known (or expected) usage. If callers typically use three different (but overlapping) members of an interface's six members, then six it is!
A large number of methods often indicates poor cohesion and good object design should place a natural limit on that number anyway. But you shouldn't ever split an interface just to reduce the number of methods it contains.
精彩评论