Horizontal reuse in JavaScript and C#
In his article Horizontal Reuse: An Alternative to Inheritance Toby Inkster compares how to implement multiple inheritance or mixins in Java, Perl, PHP and Ruby. It’s a very interesting comparison of programming languages features and well worth the read.
Toby writes:
In class-based object-oriented programming, when there are classes that appear to share some functionality, this is often a time when people will refactor them into two subclasses of a common base class, avoiding repetition.
Toby gives an example of a Tractor class and a Horse class which can both pull_plough to help on the farm.
However, Horse already inherits from Animal, and Tractor already inherits from Vehicle. […] So ruling out multiple inheritance, what other possibilities do we have?
In this blog-post, I would like to extend (mixin?) Toby’s examples with two of my favorite languages: JavaScript and C#.
JavaScript
JavaScript doesn’t implement classical object-oriented classes, but instead uses prototypes. JavaScript doesn’t have a built-in mixin concept available, but the flexibility of the languages makes it easy for us to create one from scratch:
function mixin(destination, source) {
for (var fn in source) {
if (source.hasOwnProperty(fn)) {
destination[fn] = source[fn];
}
}
return destination;
}
var HitchableMixin = {
pull\_plough: function() {
console.log("Hitching plough");
this.go();
}
};
var Vehicle = function() {
};
Vehicle.prototype.go = function() {
console.log("Brrrrm!");
};
var Tractor = function() {
};
Tractor.prototype = new Vehicle();
mixin(Tractor.prototype, HitchableMixin);
var tractor = new Tractor();
tractor.pull\_plough();
These mixins acts just as Ruby’s mixins, but like much of JavaScript, it falls on us to create the mechanism for reuse ourselves. There are many libraries out there that aim to do just this, but it’s useful to understand the underlying code.
Just like with Ruby, if we added the Hitchable mixin to Horse and forgot to implement go, JavaScript would only warn us when we tried to call horse.pull_plough().
C#
C# offers extension methods which aim to do the same thing as defender methods in Java, but rather than having to add the methods to the interface, C# allows us to put the extension methods in an independent static class.
public interface IGo {
void Go();
}
public static class Hitchable {
public static void PullPlough(this IGo go) {
Console.WriteLine("Hitching plough");
go.Go();
}
}
public class Vehicle : IGo {
public void Go() {
Console.WriteLine("Brrrrm!");
}
}
public class Tractor : Vehicle {
}
Just like Toby’s Perl example, all the important qualities of horizontal reuse are covered, but very differently from the Perl example: We have a name for the common behavior, the Hitchable class specifies what’s needed to make something pull a plough (the IGo interface) and we get an error message from the compiler if we try to PullPlough on something that doesn’t implement IGo.
The difference is that the compiler has no idea that we may want to make Horse Hitchable before we try to call MyHorse.PullPlough().
The C# approach has one big advantage: Anyone can create the Hitchable static class. Neither Vehicle, IGo or Horse needs to know about it’s existence.
The flip-side of this is that the only type that we can assign both Horses and Tractors to is the IGo interface.
Conclusion
Different languages offer different mechanisms for horizontal reuse. JavaScript reminds a lot of Ruby, but requires more manual work for the developer. C# has a totally different approach from other languages that offers some unique strengths, but also has limitations compared to the Perl and PHP approaches.
Make sure that you check out Toby Inkster’s article for four other languages each with their unique approach: Java, Ruby, PHP and Perl.
As developers in one programming language, it is useful to know what solutions are offered in other programming languages for the same problems that we face ourselves. For programming language designers, we live in a golden age with lots of different ideas that I hope may cross-pollinate between modern programming languages.