When people start writing some new code they often feel so enthusiastic that they try to make it reusable right from the beginning. Or some do it out of some pretended sophistication. The end result is usually layers of unnecessary abstractions that are actually not used anywhere else. In the end such code takes more effort to write and more effort to maintain afterwards.
The reality is that you cannot make something reusable if it is used in a single place/scenario. To make it reusable you do need at least two real use cases, so you can abstract the common traits. The more use cases you have, the better you can refine the abstraction.
You may think you know what new use cases will come, but in reality these either don't come at all or are quite different from what you have expected.
So don't do this, unless you can tell the future.
I thought premature generalization would be a good term for such situations. Then after searching the net it turned out this term is well established already - Premature Generalization Is Evil. It is even listed as #1 in Seven deadly sins of programming.
Friday, October 26, 2012
Thursday, August 30, 2012
Remove class inheritance from Java
Introduction
We often hear the advice that we should prefer composition over inheritance. Then naturally come the questions: Are there valid cases at all to use inheritance? Is it possible that inheritance, one of the fundamental OOP principles, is actually an anti-pattern and unnecessary language feature?By inheritance here I mean only class inheritance and not interface extension.
Goodbye Class Inheritance
The most common reason for class inheritance found in practice is code reuse. When people realize they need some common code in two or more classes, they pull it up in a common base class (usually an abstract one). But what would you do if you need to reuse code from several other classes? There is no multiple inheritance in Java. The right way to reuse some common code is via composition, i.e. extract the common code in a separate class and use it via a reference.The next reason for inheritance is method overriding, i.e. template method pattern. When people realize they need some variability in behavior, they define a method for it (often abstract) and implement it in different ways in different subclasses. This is also wrong for several reasons. You cannot change the variable method implementation dynamically without changing the whole object. Also if you have several axes of variability, i.e. several abstract methods, how many subclasses do you need to cover all possible combinations? Again the right way to do that is via composition. Each independently variable part is extracted in a separate interface. Different implementations of these interfaces are injected from outisde, i.e. strategy pattern.
Can you suggest cases where it is still best to use class inheritance? Post a comment.
Generally, inheritance violates other OOP principles like encapsulation as it creates tight coupling between involved classes.
The conclusion is that the availability of this feature (implementation inheritance) leads people into wrong designs. So the Java language can be both improved and simplified if classes are not allowed to extend other classes. Still an interface can extend other interfaces and a class can implement a number of interfaces.
Removing this feature can simplify the language significantly. Additional language features can also be removed as they are no longer necessary.
- abstract keyword is no longer necessary and can be removed. Abstract coding can get you only abstract money. :)
- protected keyword is no longer necessary and can be removed
- final keyword on classes and methods is no longer necessary
- (Probably this list can be extended further. Drop your suggestions in the comments.)
"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away."
Welcome Auto-binding
Still when your class implements an interface you often do not write the code from scratch but want to reuse some existing implementation. As we saw already the proper way to do that is via composition. Something like this
class A implements I
{
private I anI;
...
public void foo() { anI.foo(); }
public void foo() { anI.foo(); }
public void moo() { anI.moo(); }
...
}Unfortunately we have no way to tell that anI provides the implementation of interface I, so we have to implement all the methods of I in A just by delegating to anI. Boring boilerplate! This is where the second proposal for the language improvement kicks in. You can call it auto-wiring or auto-binding but it does just that, specify that an interface is implemented by a specific class member. Something like this:
class A implements I via anI
{
private I anI;
...
...
}
The compiler can automatically generate the necessary delegation methods or an optimized implementation could even skip this overhead.
If a class needs to customize the interface implementation it can still implement some of the interface methods, thus overriding these methods from the external implementation.
Instead of a field also a method could be specified in case the interface implementation should be calculated dynamically. Something like this:
class A implements I via getI
{
private I getI() { ... }
}
}
I find these two proposals for Java language improvement nicely complement each other and naturally lead developers in the right direction.
Until these improvements are implemented in Java (version 15 probably :) you can try to avoid class inheritance in your code.
Subscribe to:
Posts (Atom)