Friday, January 2, 2009

Aspect Oriented Programming with Roles/Traits and Joose

Joose is different from other JavaScript frameworks because it focuses on only providing tools for making the programming with JavaScript for robust and productive. There are no facilities for DOM access or AJAX. Joose is thus complementary to using a more traditional Ajax library and also works nicely in non-browser environments like Rhino.

Many JavaScript libraries with the notable Exception of jQuery provide some helper methods to make things like class or prototype inheritance a more pleasant experience. Joose's capabilities, however, go way beyond these basic features. My personal favorite feature in Joose is the combination of Roles (sometimes also called Traits) and method modifiers.

Here is the definition from the Traits research page:
Traits are a simple composition mechanism for structuring object-oriented programs. A Trait is essentially a parameterized set of methods; it serves as a behavioral building block for classes and is the primitive unit of code reuse. With Traits, classes are still organized in a single inheritance hierarchy, but they can make use of Traits to specify the incremental difference in behavior with respect to their superclasses.
In Joose creating a role (aka trait) is as easy as writing
Role("Eq", {
requires: "equalTo",

methods: {
notEqualTo: function (other) {
return !this.equalTo(other)
}
}
})
This role can be applied to things that implement the method equalTo and gives them the method notEqualTo. Things here means classes and (because this is JavaScript!) objects.
Applying to a class looks like this:
Class("TestClass", {
does: Eq
}
Applying to an object is a one liner:
Eq.meta.apply(myObject)
So much for simple traits. Where things get really interesting is when the concept of roles is combined with the concept of method modifiers (I call them M&Ms). M&Ms allow augmenting methods with extra functionality by doing stuff before or after a method is executed (The full list of modifiers is before, after, around, override and augment).
In this example Test derives from SuperClass and modifies SuperClass's add method with a before-modifier:
Class("Test") {
isa: SuperClass,
before: {
add: function () { this.result += "3" }
}
}
Now it is also possible to define method modifiers in roles. When the role is applied the method modifiers are applied to the methods of the target class or object.
From here on I'll use real world examples from the Joose example app blok. Things placed on blok's viewport are instances of the class Shape. Shapes can be resized, dragged, styled, edited, etc. All of these "behaviors" are implemented as roles that are applied to shapes.
A shape keeps a jQuery object in it's instance variable called $ that represents the DOM node of the actual shape-html on the viewport. Whenever the DOM node receives or looses focus the respective methods "focus" and "blur" are called on the shape object. The method "place" is called to place a shape on the view port. One of the roles that can be applied to shapes is the Focusable role. This role gives a shape the ability to actually receive a visual recognizable focus (aka an outline). Here is the implementation of the Focusable role:
Module("block.ui.role", function () {
Role("Focusable", {
after: {
place: function () {
var me = this;
this.$.mousedown(function focusClick (e) {
e.preventDefault()
document.manager.switchFocus(me, e.shiftKey)
return false;
})
},

focus: function () {
this.$.append('<div class="focusDiv"></div>')
},

blur: function () {
this.$.find(".focusDiv").remove()
}
}
})
})
This particular role augments the methods "place", "focus", and "blur". When the shape that uses this role is placed on the view port a mousedown event is added to the jQuery object. This event is than delegated to the global document.manager singleton-object that manages focus on the page (This object later calls the focus and blur methods on the shape objects. This is different from the browser's understanding of focus, because shapes may keep focus while a control elsewhere is used). When the shape receives focus an extra div is added to the jQuery object (this is not done using only css classes for stupid technical reasons). When the focus is lost, the div is removed again.

The examples should demonstrate that roles are great ways to assemble "reusable units of behavior" (as the orginal authors put it). Using method modifiers further reduces conflict potential between multiple roles because an unlimited number of M&Ms can be applied to the same method. Thus other roles like Resizable, Draggable, Connectable or Editable can be safely added to the same shape.

I particularly like the pattern outlined above because it feels really JavaScript-y. The use of roles makes the pattern superior to simple aspect oriented techniques using only method modifiers because multiple related modifiers can be applied in a single stepped.

I hope this post was interesting, now go play with Joose :)

PS: Thanks to Moose for comming up with all these concepts so that I could copy them for JavaScript.

Earlier post on the same subject.
Post a Comment