I have a classic diamond inheritance problem where
A
/ \
B C
\ /
D
are all interfaces, and I have
AImpl(A)
| \
| \
BImpl(B) CImpl(C)
| \
| \
DImpl(B,C) \
| F(C)
|
E(B,C)
where class E
implements both interfaces B
and C
, but F
implements only interface C
.
Due to the lack of multiple inheritance, I currently have duplicated functionality in DImpl
and CImpl
.
I just fixed a bug in CImpl
, but forgot to do the same for DImpl
. Obviously remembering to always copy code from CImpl
to DImpl
and vice versa is not very sustainable as the code base keeps growing. Are there any established best practices for putting the shared code of both in a single place, despite the disallowance of multiple inheritance?
EDIT -- Solution with multiple inheritance would have been to have DImpl
inherit CImpl.cFunction()
instead of redefining DImpl.cFunction
as a copy of CImpl.cFunction
EDIT 2 -- Sample code:
public interface Animal {
public void eat();
}
public interface FlyingAnimal extends Animal {
public void fly();
}
public interface RunningAnimal extends Animal {
public void run();
}
public interface Monster extends FlyingAnimal, RunningAnimal {
public void roar();
}
public class AnimalImpl implements Animal {
@Override
public void eat() {
...
}
}
public class FlyingAnimalImpl extends AnimalImpl implements FlyingAnimal {
@Override
public void fly() {
...
}
}
public class RunningAnimalImpl extends AnimalImpl implements RunningAnimal {
@Override
public void run() {
...
}
}
public class MonsterImpl extends FlyingAnimalImpl implements Monster {
@Override
public void run() {
...
}
@Override
public void roar() {
...
}
}
public class ScaryMonster extends MonsterImpl implements Monster {
public void sneakAround() {
...
}
}
public class Human extends RunningAnimalImpl implements RunningAnimal {
public void scream() {
...
}
}
Now if I find a bug in RunningAnimalImpl.run()
and I fix it, I have to remember to copy the fix over to MonsterImpl.run()
.
This is a very poor design. You should not have a diamond structure in the first place just because it is possible in interfaces.
One of the basic principle of OOP is
Prefer composition over inheritance!
What I am saying is you don't need interface D at all. Wherever you need to use DImpl
simply provide reference to interface A
and then based on your runtime need pass BIml
or CImpl
instance to it. That way all you have to change is BImpl
code or CImpl
code for bug fix and it will be used anywhere you are using DImpl
instance today.
As per your comment the code would be something like -
public class ScaryMonster {
Animal animal;
public ScaryMonster(Animal animal) {
this.animal = animal;
}
public void fly() {
if(animal instanceof FlyingAnimal ) {
((FlyingAnimal )animal).fly();
}
else {
throw new Exception("This mosnter cannot fly");
}
}
public void run() {
if(animal instanceof RunningAnimal ) {
((RunningAnimal )animal).run();
}
else {
throw new Exception("This mosnter cannot run");
}
}
public void sneakAround() {
...
}
}
If you want your monster to both fly and run pass instance of MonsterImpl
to the constructor. Note now ScaryMonster
does not extend or implement anything.
This is what I am saying - Prefer composition over Inheritance!.