[Opinion/editorial by Mahesh Tabib]
Reusability has always been an interesting topic. When determining how best to tackle the large amounts of variations in the application, several factors have to be considered. These factors include code maintainability, code reuse, development effort, etc. Reusable code requires an effective combination of two points.
1. Properly defined interface definitions.
2. Efficiently defined class structures and inheritance.
One benefit of establishing interface definitions is that every class that implements an interface must adhere to that interface’s functional requirements. The fact that multiple sets of variations can share a given class implementation demonstrates how implementation logic can be reused in the system.
The interfaces must be defined in such a way as to maximize its ability to be reused. Large interface definitions (too many functions) can render an implementation class virtually impossible to reuse. Whereas a single function interface can lead to a class proliferation problem (too many classes). The balance must be struck somewhere in between with medium sized interfaces (fewer functions). In addition, more code sharing occurs within each implementation class. Based on the class structure established at development time the implementation classes can be organized according to their interface group type and can allow inheritance to access common logic. By extending implementation classes from a categorized base class a developer can “promote” common logic to the parent class thus reducing the amount of redundant code. When promotion of common code to a parent class starts to become impossible it might be time to re-evaluate the size of the underlying interface.
The combination of inheritance along with composition reduces the amount of duplicate code present in the system. Interface modifications may be required in the ongoing effort to balance the amount of logic that can be promoted to base classes as the number of implementation classes increases. Make a code reusable and avoid redundant code.
I think code reusability usually is an issue in more or less big project. And from practical point of view code reusablity means
1. Ability/will for one developer find reusable component of another developer, and understand semantics of component --> good doc's
2. Granularity - component should ideally represent some, probably low level use case.
And the component itself could be just plain class (without interface or inheritance) or ear deployed to the remote server and accessible via some RPC.
How may implementations do you have for each of your interfaces? If the answer is 1 (one) then the interface is an unnecessary overhead that reduces code maintainability, not increases it. Interfaces have their place, but they are not a panacea.
You also mentioned inheritance as a way to share code. If the only purpose it to share code, and there is no IS A relationship between the classes, then inheritance is the wrong way. If multiple objects are sharing some behaviour then look at creating a new class to encapsulate that behaviour.
Of course it is not that easy. It takes lots of experience smelling code to understand what works best and why. (Read Refactoring by Martin Fowler)