[caption id="attachment_391" align="alignright" width="300" caption="Blender"][/caption]
A week ago I started learning Scala. One of the features I found pretty interesting are mixins and traits. That was just the point of time, when I read this little tweet of GeekyL:
"i am still not sure if mixins are super cool or dark magic."
Of course I was instantly reminded of the time when the dinosaurs dominated the world and I was learning the first little bits of OO and C++. I thought inheritance and polymorphism was great and the solution for every possible programming problem there is. It turned out that was not the case. Actually inheritance can result in pretty ugly code.
So GeekyL's tweet got me thinking: Do mixins have the same problem? What exactly are the problems with inheritance anyway? Time for a new blog post.
What are the problems of inheritance?
Inheritance for code reuse: It is tempting to have a class inherit from a superclass just because the superclass has some useful feature. Like this.
import java.beans.PropertyChangeSupport;
public class PropertySupport {
protected PropertyChangeSupport propSup;
}
public class UserEntity extends PropertySupport {
private String firstName;
public void setFirstName(String aFirstName) {
String oldValue = firstName;
firstName = aFirstName;
propSup.firePropertyChange("firstName", oldValue, firstName);
}
}
The problem with such code is that inheritance suggests a is-a-relationship, which in this case is just wrong. A UserEntity is not a PropertySupport, it just uses one.
Multiple inheritance: At least in java a class can only inherit from one superclass. But many classes are many things. For example, a Cat is a Carnivor, it is a FourLeggedAnimal and a FurryAnimal. Without multiple inheritance there is just no way how to model this with class inheritance. With multple inheritance you can extend all these classes at the same time, but now you inherit the top class Animal three times, which at least is ugly.
So the proper way to do this kind of stuff in java is to use delegation and interfaces:
public class Cat implements Furry, FourLegged {
FurryDelegate fd;
FourLeggedDelegate fld;
@Override
public void pet() {
fd.pet();
}
@Override
public void runFast() {
fld.runFast();
}
}
Which works in the sense that you can pretty much express the kind of things you need to do, and which you might be tempted to solve with class inheritance. Of course the draw back is that you'll have to create all theses trivial delegate methods.
In Scala you have the option to use Traits. Like so
class Animal
class Cat extends Animal with Furry with FourLegs{
}
trait Furry{
def pet() {
println("purrrrr")
}
}
trait FourLegs{
def runFast() {
println("I'm gone")
}
}
Obviously this contains much less code duplication then the java version. Does it have drawbacks? You bet.
Mixins are firmly tied to the traits they use. Imagine a more complex trait, which itself uses many other clases and resources. Once you mix in such a trait you have a strong dependency. Therefor I suggest the following pattern for getting the reduction of code duplication from traits and the flexibility of delegates: Use Traits, which don't have a real implementation, but use a delegate for doing all the real stuff, this way you can switch the implementation with touching just a single place:
class Superclass
class Mixin extends Superclass with Trait {
// ... more stuff goes here
}
trait Trait {
val delegate = new TraitImpl // this should be some kind of DI or lookup in a real system
def aMethod() {
delegate.aMethod()
}
}
class TraitImpl extends Trait {
override def aMethod(){
println("you just called me")
}
}
Summary: Traits can be used to solve some of the problems of inheritance, but they might introduce strong coupling, which you can easily be avoided.
Talks
Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: