Mixins for Atlas
I've beeen toying with that idea for a while and I thought I would try to get some feedback on it. Mixins are a way to dynamically add members to an existing object. I've built a small function, Type.Mixin, that does that by copying members from the mixin object's prototype to the object that you want to extend. The mixin object is a type (that is, a function) that can't be instantiated (you can ensure that by throwing from the constructor). The members that will extend the target object are defined on the prototype of the mixin.
Here's an example of a mixin object that is specialized at extending strings:
Bleroy.Mixin.StringHtmlExtensions = function() {
throw new Error("Can't instantiate a mixin.");
}
Bleroy.Mixin.StringHtmlExtensions.prototype = {
// Don't do this at home, this is a very naive
// implementation just for demonstration purposes.
htmlEncode: function() {
return this
.replace(/&/g, "&")
.replace(/\</g, "<")
.replace(/\>/g, ">")
.replace(/\"/g, """);
},
htmlDecode: function() {
return this
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.replace(/&/g, "&");
}
}
Bleroy.Mixin.StringHtmlExtensions.targetType = String;
You can extend a string with this mixin this way:
var val = new String($("userInput").value);
Type.Mixin(val, Bleroy.Mixin.StringHtmlExtensions);
$("encoded").innerHTML = val.htmlEncode();
Here, we get the value of an input field, apply the mixin and we're ready to use the mixin's methods on the string and set a label's text with the results. Note here that you need to construct the string instance using the String constructor. A string literal won't do because in JavaScript you can't extend objects of the built-in types that were not constructed from the corresponding type's constructor. You need to "box" the literal in order to be able to extend it.
Now, if you want to extend all string instances instead of just one, just pass the String prototype as the object to extend:
Type.Mixin(String.prototype, Bleroy.Mixin.StringHtmlExtensions);
$("decoded").innerHTML = $("userInput").value.htmlDecode();
This is actually analogous to C# 3.0 extender methods. And this time, no need to box the string: once the type itself has been extended, all instances, existing and future, will get the extensions. This looks kind of cool as a way to package a collection of methods but it would still be more direct to just directly extend the string prototype.
Where mixins really shine is when they're not restricted to a single type like above. Here's another mixin that can be applied on any object:
Bleroy.Mixin.Duck = function() {
throw new Error("Can't instantiate a mixin.");
}
Bleroy.Mixin.Duck.prototype = {
quack: function() {
return this.quackSound;
},
walk: function() {
// implement duck walk here.
}
}
Bleroy.Mixin.Duck.type = Bleroy.Mixin.IDuck;
This one will not only add the methods to the object you apply it to, it will also make it a valid implementation of the IDuck interface. Here's an example of how you can use it to transform a plain JavaScript object (like something you may have got as a JSON expression from a web service for example) into a legitimate implementation of the IDuck interface:
var myDuck = {quackSound: "qwak!"};
Type.Mixin(myDuck, Bleroy.Mixin.Duck);
// using our new duck
if (Bleroy.Mixin.IDuck.isImplementedBy(myDuck)) {
alert(myDuck.quack());
}
Like above, you can also apply the same treatment to a type instead of just one object:
Bleroy.Mixin.RubberDucky = function() {
this.quackSound = "skweek!";
}
Bleroy.Mixin.RubberDucky.registerClass('Bleroy.Mixin.RubberDucky');
Type.Mixin(Bleroy.Mixin.RubberDucky.prototype, Bleroy.Mixin.Duck);
One might argue that this is just a fancy way of doing multiple inheritance and we all know what a mess that is. Well, it is. But it's also a nice way to package a canonical implementation for an interface which is actually coupled with the interface type. I really like that it's still interface-based programming.
Now the thing about all this is that it's not exactly necessary. For example, to associate an interface and its implementation to a plain object, you can just write a class that encapsulates the implementation and that takes a plain object as a constructor parameter.
I'd really love to read what you guys think about that. Would you use something like this? Why would you use it over more traditional inheritance techniques?
The full source code can be downloaded here (you need to extract that into a web site with the latest CTP of Atlas installed):
http://weblogs.asp.net/bleroy/attachment/463965.ashx
UPDATE: The prototype library has something very similar to this, Object.extend (thanks to Zach for pointing that out). You can check it out as well as more useful examples of mixins here: http://www.sitepoint.com/article/painless-javascript-prototype