Factory Aspect, Added to a Decorator: APE.getById Sunday, 11 May 2008

This article describes the generic concepts for creating and mixing design patterns. The basic principle for all design patterns is: Encapsulate the parts that vary.

The problem is finding a way to create a generic Factory that can be reused on various constructor functions for element Decorators. This article explains the problem and the process for finding the solution.

Decorator Factory Aspect

A Decorator Factory Aspect is a Factory method, added as an Aspect to a constructor of a Decorator.

Before I explain how to add a Factory to a constructor function for an element decorator, I should first define Decorator (also called a wrapper), Factory and Aspect.

Decorator Pattern
makes it possible to extend (decorate) the functionality of a class by adding a new decorator class that wraps the original class. (Wikipedia link)
Factory Pattern
The Factory pattern is a creational design pattern that encapsulates the processes of creating objects (Wikipedia link)
Aspect
introduces separation of concerns, specifically cross-cutting concerns, as an advance in modularization (Wikipedia link)

Decorator Examples

Decorator is very common in JavaScript. For example: YAHOO.util.Element decorates an element, jQuery decorates an array of elements.

Factory Example

The Factory gets or creates a decorated element. The id of the wrapper is the same as the id of the element. This is the part I want to make reusable:

/**
 * @constructor
 * @param {String} id - the id of the element and widget. 
 */
function ElementWrapper(id, x) {
  this.id = id;
  this.x = x;
}

// Factory. 
// TODO: How can I make this generic/reusable?
ElementWrapper.instances = {};
ElementWrapper.getById = function(id, x) {
  if(this.instances.hasOwnProperty(id)) return this.instances[id];
  return this.instances[id] = new this(id, x);
};

ElementWrapper.prototype = { 
  show : function() { 
    document.getElementById(this.id).style.visibility = "visible";
  }
};
Benefits

Solves the problem of creating only one decorator per element id.

By calling getElementById, the decorator can avoid some of the problems with changing node references with innerHTML (though state changes must still be managed manually).

Problem: DRY

Don't Repeat Yourself

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

It is cumbersome and error-prone to write out a Factory each time. Since this is an idiom I use a lot, it makes sense to make it reusable.

I want to have a generic getById method that can be reused and will return an instance of the constructor that it is called on. I want to be able to pass extra arguments to that constructor (varargs).

Encapsulate the Parts That Vary

What varies?

The id parameter variable of getById does not change; it will always be present in any generic Factory. The parts of the Factory that vary are: The additional zero or more arguments (varargs, this case, x), and the context, or thisArg.

Resolving the context arg is easy.

If I can solve passing varargs to a constructor in a generic context, it will be possible to create a generic Factory Aspect.

Function newApply

A way to call new with variable arguments would solve this problem. A new + apply() would provide the varargs functionality of apply, but passed to [[Construct]], not [[Call]].

This has been solved in APE core. The source code for APE.newApply:


/** 
 * @param {Function} fun constructor to be invoked.
 * @param {Array} args arguments to pass to the constructor.
 * Instantiates a constructor and uses apply().
 */
newApply : function(fun, args) {
    if(arguments.length === 0) return;
    var f = arguments.callee, i;

    f.prototype = fun.prototype; // Add prototype.
    f.prototype.constructor = fun;

    i = new f;
    fun.apply(i, args);  // Apply the original constructor.
    return i;
}

What's it Good For?

Now I can create the generic getById function I wanted. This function can be added as an aspect to any constructor function. Factory Aspect APE.getById is a part of APE core.

getById : function(id) {
    if(!this.hasOwnProperty("instances")) this.instances = {};
    return this.instances[id] || (this.instances[id] = APE.newApply(this, arguments));       
},

Using the Generic getById

This getById method can be used with ElementWrapper (above) or any other constructor that acts as a Decorator to an element and accepts the element's id as its first argument.

Slider = function(id, dir) { /* ... */ };

// Factory. 
Slider.getById = APE.getById;

Then I can use:

Slider.getById( "weight", 1 );

Subsequent calls to:

Slider.getById( "weight" );

— will return the same Slider instance.

More Examples

I have used this approach for many parts of APE, including Calendar, Draggable, and Slider. It is most useful for building widgets.

Reusable Concept

Another closely related technique is Decorator that accepts an element instead of an element's id. This is covered by APE.getByNode.

Source Code

APE.js

Reflection

In most patterns, encapsulating the parts that vary entails creating an class. However, in JavaScript, this particular pattern was simple to implement by using just two functions (APE.getById and APE.newApply) and leveraging the dynamic nature of JavaScript.

Forward to ES4

ES4 has had some proposals for something called a splat operator.

fun(...argsOrArray); 
new fun(...argsOrArray);

This proposal will allow passing varargs to a constructor or a function call.

It is unclear how the splat operator, if accepted into the language, will work with functions which expect typed arguments, in strict mode.

Links

Orthogonality and the DRY Principle, A Conversation with Andy Hunt and Dave Thomas, Part II by Bill Venners March 10, 2003

Posted by default at 10:35 PM in JavaScript

Event Notification System Friday, 4 January 2008

An Event Notification System is an object that manages notification of events to multiple callbacks. The Event Notification System uses an Event Registry to store the callbacks as bound methods. When the event fires, the callbacks are invoked.

Event Registry

An Event Registry is a store of bound methods. An Event Registry is used by an Event Notification System. The Event Notification System is tightly coupled with the Event Registry. Sometimes it is referred to as the Registry. In reality, the Registry is just a data structure and the Event Notification System is a behavioral object.

Almost Every JavaScript library has an Event Registry, or at least some way of dealing with event notification.

For example:

// YUI:
YAHOO.util.Event.addListener( link, "click", linkClickHandler, thisArg );

// Prototype: (not a registry, but the old 'addEvent' function renamed).
Event.observe( link, "click", linkClickHandler ); 

// Dojo:
dojo.connect( link, "onclick", window, "linkClickHandler" );

They're all different in how they work.

The Event Registry is useful for a few reasons.

  • It allows multiple callbacks to be assigned to a function call.
  • Provides a usable alternative to attachEvent. Internet Explorer 7 and below has attachEvent/detachEvent. The callback function for attachEvent executes in global context (this is window), not the object it was attached to.

A good Event Registry solves these problems. A good Event Registry also allows for context resolution with an optional thisArg. A good Event Registry also allows custom events to be registered using the same interface.

A poorly designed Event Registry concerns itself with things related to native events (DOMContentLoaded, keyPress, et c). A poorly designed Event Registry does not pass an event object to the callback (perhaps trying to use eval to pass varargs).

Error Handling in an Event Notification System

Callback Errors Should not Break the Registry

A good Event Registry does not allow any callback to break the registry.

One common problem in most Event Notification Systems (such as Dojo, Mochikit, YUI, and jQuery) is that they allow the callback to break the System. If a callback fails, it prevents subsequent callbacks from firing. A callback should not be given the ability to break the Registry.

Here's how to break a Registry that doesn't consider errors:

var passed = false;
addCallback( link, "click", function(){ setTimeout(checkTitle, 500); } );
addCallback( link, "click", function(){ throw Error('bad'); } );
addCallback( link, "click", function(){ passed = true; } );

function checkTitle(){ 
    if(!passed) 
        alert("registry broken: second callback did not fire.");
    else 
        alert('passed');
}

Callbacks sometimes throw Errors. It is important for the Event Registry to consider this and take the responsibility to handle these errors properly. If an error occurs in a callback, it should not break the Registry.

It should be guaranteed that all callbacks fire, even when earlier callbacks throw errors. This is a natural expectation; it's exactly how DOM Events work:

DOM Events Test

(function(){
var s = document.getElementById('r-test');

var el = document.getElementById("registry-dom-event-button");
if(!window.hasDocumentListeners) {
    el.addEventListener( "click", setUpCheck, false );
    el.addEventListener("click", throwError, false );
    // setTitle must fire.
    el.addEventListener( "click", setTitle, false ); 

    window.hasDocumentListeners = true;
}
function setUpCheck(){ setTimeout(checkTitle, 500); }
function throwError(){ document.title = ""; throw Error('bad'); }
function setTitle(){ document.title += 'ok'; }
function checkTitle(ev) {
    if(document.title != "ok") {
        alert("DOM Events broken: setTitle did not fire. " + document.title );
    }
    else {
        alert("passed");
    }
}
})();

Result and Analysis

There should be 1 error and an alert passed. This indicates that after the error happened, the setTitle callback successfully fired.

This example assumes:

  • Callbacks fire in the order in which they were registered.
  • The bad error in the first callback does not stop subsequent callbacks from firing.
  • The button supports the EventTarget interface (not in IE7).
  • The button supports onfocus (not in safari bug 16331).

Proper Callback Error-Handling

Throwing the error in a separate thread allows the callstack to continue without breaking. Any errors that are thrown are thrown in the correct order in the callstack. The Event Publisher's fire function would have something like this:

try {
// If an error occurs, continue the event fire,
// but still throw the error.
  callback.call( thisArg, ev );
}
catch( ex ) {
  setTimeout("throw ex;", 1); 
}

The one subtle issue is that setTimeout uses global scope, like the Function constructor, not like eval, which runs in the calling context's scope.

A closure must be used to preserve the ex variable.

try {
// If an error occurs, continue the event fire,
// but still throw the error.
  callback.call( thisArg, ev );
}
catch( ex ) {
  setTimeout(function(){ throw ex; }, 1); 
}

Event Registry Test

The remaining problem with the above code is that the error condition is untestable. Writing a test suite forced me to realize this and I changed the design.

try {
if(csi[0].call(csi[1], e) == false)
  preventDefault = true; // continue main callstack and return false afterwards.
}
catch(ex) {
  APE.deferError(ex);
}

Where APE.deferError is defined:

deferError : function(error) {		
  setTimeout(function deferError(){throw error;},1);
}

I have included the source code for my own Event Registry, along with this test, which shows how I managed to test APE.deferError.

Performance?

Wrapping each callback call in a try catch might seem to be bad for performance. I tried it with mousemove event on my drag code, dragging multiple drag objects at a time (example), and it seemed fast enough; I did not notice performance problems in any browser. There is most likely some performance overhead using this approach, but I did not find a need to write a benchmark.

src should never be a string. Although this may seem obvious, YUI actually allows src to be a string, where the string represents an element's ID. The document is polled regulary until the element with the id matching string is found and then the callback is attached to that element. If the element has been renamed, the document is still polled and silent failure occurs.

This can lead to silent failure or corrupted application state if the element is not found. It is not recommended.

Packaging and API Design

The Event Notification System is a low level component with no external dependencies.

Being a low level component, the Event Notification System should be maximally stable (no efferent couplings), and maximally abstract. In this case, the Event Notification system is maximally abstract because it can't be subclassed or used independently.

Stable Dependencies Principle

Depend in the direction of stability

Stable Abstractions Principle

A package should be as abstract as it is stable.

Reuse Equivalence Principle

The Granule of Reuse is the Granule of Release.

The Event Notification System is a low level component with no external dependencies. It is intentionally packaged as a single, tested unit. It amplifies the essential (event notification) and eliminates the irrelevant.

Creating special cases for handling DOM events (keyCode, et c), would reduce abstraction. These special cases are perfectly valid, but do not belong in the Registry. Special case needs can either be hard-coded into end-implementation code (using feature/capability detection) or, if the special-case logic is complex, programmed into an object that performs a task (such as an Adapter object).

An example of an Adapter object would be a Content Load Adapter or a KeyEvent Adapter (key events are highly inconsistent across platforms). Such objects would be slightly higher-level and, having at least one dependency, would be less stable (though this is not a bad thing).

Department Store JavaScript

[insert_popular_library_name_here] usually include more code than any one application could possibly use in an attempt to cover the needs of every application.

Libraries that add more functionality into one module than is usually needed, or create modules that are not cohesive do so in spite of commonly known software package design concepts. The one-stop library approach is appealing because it allows developers to "stop cobbling bits of javascript."

Performance (Again)

Load Time Performance problems can be acheived by creating custom javascript builds on the server. Hand-rolled "combination" files or utils files are fine for web sites with fewer pages. Sites that don't require 200k+ of additional javascript should not include such functionality.

Posted by default at 9:42 PM in Uncategorized

 

*AnimTree
*Tabs
*GlideMenus
*DragLib