My favorites | Sign in
Project Logo
                
Search
for
Updated Jun 22, 2009 by jhurstus
Labels: Featured, Phase-Design
TraitSynopsis  

Before we jump into the synopsis a couple of notes: the full API documentation for JSTraits can be found in both the release zip and online; also the following documentation borrows heavily from the documentation for the Perl 5 implementation of traits.

Synopsis

// define a new trait that draws colored circles
var TColoredCircle = new Trait({
  uses: [ // TColoredCircle uses two other traits ...
    TColor,
    TCircle.aliases({'drawOutline': 'draw'}) // alias the 'draw' method provided by TCircle to 'drawOutline'
  ],
  requires: 'fillWithColor', // this method will need to be implemented for TColoredCircle to work
  methods: { // TColoredCircle provides the following method (as wells as those provided by TColor and TCircle)
    draw: function() {
      // draw a colored circle
      this.drawOutline();
      this.fillWithColor(this.getColor());
    }
  }
});

// define a new class that draws happy faces
var HappyFace = Class.define({
  superclass: Doodle, // this class inherits from the superclass Doodle
  uses: [ // this class adds the methods from the TFace and TColoredCircle traits
    TFace,
    TColoredCircle.aliases({drawColoredCircle: 'draw'})
  ],
  members: {
    // constructor
    init: function(color) {
      this.isDrawn = false;
      if (color)
        this.setColor(color);
    },
    // draw a happy face
    draw: function() {
      // call Doodle's draw method to set up the canvas
      this._super();
      // draw a colored circle (using the TColoredCircle trait's method)
      this.drawColoredCircle();
      // draw the face (via TFace)
      this.drawEyes();
      this.drawMouth():
      // record that the happy face has been drawn
      this.isDrawn = true;
    },
    // color of the happy face (default is yellow)
    color: 'yellow',
    getColor: function() { return this.color },
    setColor: function(color) { this.color = color }
  }
});

// draw a blue happy face
var hf = new HappyFace('blue');
hf.draw();
log(hf.isDrawn); // => true
log(hf.does(TFace)); // => true
log(HappyFace.does(TColoredCircle)); // => true
log(HappyFace.superclass === Doodle); // => true

Description

Trait Definition

A Trait can be defined as an object containing:

Here is an example of the syntax for a very basic trait:

var TComparable = Trait.define({
  uses: [TEquality], // requires equalTo, provides notEqualTo
  requires: ['lessThan'],
  methods: {
    lessThanOrEqualTo: function(other) {
      return this.lessThan(other) || this.equalTo(other);
    },
    greaterThan: function(other) {
      return !this.lessThenOrEqualTo(other);
    },
    greaterThanOrEqualTo: function(other) {
      return !this.lessThan(other);
    },
    compare: function(other) {
      if (this.lessThan(other)) return -1;
      if (this.equalTo(other)) return 0;
      return 1;
    }
  }
});

The above example requires the user of the trait to implement the lessThan and equalTo methods. It then provides a convenient set of methods for doing arbitrary comparisons among objects in a class.

Trait usage

When a class uses a trait:

Here is a simple example of the usage of the above trait in a class.

var GardenGnome = Class.define({
  superclass: LawnOrnament,
  uses: TComparable,
  members: {
    init: function(girth) {
      // use the natural unit of measurement for garden gnomes, girth
      this.girth = girth;
    },
    equalTo: function(other) {
      return this.girth === other.girth;
    },
    lessThan: function(other) {
      return this.girth < other.girth;
    }
  }
});

The above example class uses the TComparable trait to add common comparison methods to the class. It implements the required methods of TComparable, equalTo and lessThan.

Trait operations

When defining a new trait or class, changes can be made to the structure of any composed traits through the following methods.

Method Conflicts

When composing traits, a number of exported methods may come in conflict if they have the same name. All such conflicts must be resolved immediately in the class or trait definition where they occur. Conflicts can be resolved by:

Requirement Satisfaction

One trait may satisfy the requirements of another trait when they are combined into a composite trait. This results in the removal of the requirement from the requirements array in the composite trait. Requirements do not need to be fulfilled in traits, but they must be fulfilled if used in a class definition.

Class

JSTraits provides a simple pseudo-classical single inheritance mechanism for the sake of completeness. This functionality is based heavily on work by John Resig and Dean Edwards (as noted in the source code and license). The aim of this project is not to provide a complete object oriented programming system for JavaScript, but instead to implement traits in JavaScript. My hope is that the traits implementation can be extracted and used with any OOP implementation already utilized by JSTraits users (YUI, Tibco GI, Ext, etc.).

For more information on how to use the pseudo-classical inheritance mechanism in JSTraits see the API documentation.

Next Steps


Sign in to add a comment
Hosted by Google Code