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 callnew 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
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


AnimTree