Patrones de Diseño y JavaScript: Iterator

Iterator es un patrón de diseño de comportamiento (behavior), cuya finalidad es exponer una forma estándar de iterar un objeto. Para ello, tiene como requeriemiento que el objeto a iterar implemente una interfaz dada.

No hay que confundir la iteración provista por este pattern con la iteración de una colección (array u objetos en JavaScript), ya que el Iterator puede (también) generar dicha iteración sobre objetos que no sean colecciones o inclusive que permitan iterar sobre elementos dinamicamente generados.

Un claro ejemplo de esto, puede ser una utilidad de rango numerico, en la cual se itera sobre elementos generados dinámicamente en cada iteración gracias a que extiende Iterator e implementa la interfaz requerida (el método _next en este caso):

var range = new Range(1, 10);

range.each(function(number) {
	console.log(number);
});

Entonces, la implementación real de la iteración (el método each) se encuentra en el constructor Iterator y la implementación específica (el metodo _next en este ejemplo) en el sujeto. En este caso, la implementación del constructor Iterator solo consta del método en cuestión (y una conveniente exception que nos va a permitir detener la iteración), lo cual en burdo UML respetaría la siguiente estructura:

Representacion de jerarquia de Iterable y subject

Relacion de iterable y subject

En código:

/**
 * Constructor que provee de la funcionalidad
 * de iteracion
 *
 * @constructor
 */
function Iterable() {}

/**
 * Excepcion utilizada para detener la iteracion
 */
Iterable.StopException = function() {};

/**
 * Itera los elementos provistos por el sujeto
 *
 * @param {Function} iterator
 * @param {Object} context
 * @return {Object} el sujeto
 */
Iterable.prototype.each = function(iterator, context) {
	if(typeof this._next != 'function')
		throw new Error('Unimplemented method: _next');

	try {
		iterator.call(context, this._next());

		return this.each(iterator, context);
	} catch(e) {
		if(e instanceof Iterable.StopException)
			return this

		throw e;
	}
	return this;
}

Una vez que tenemos el constructor Iterator, implementar nuestro constructor Range es tarea sencilla:

/**
 * Genera un rango numerico iterable
 *
 * @param {Number} from
 * @param {Number} to
 * @constructor
 */
function Range (from, to) {
	this.actual = this.from = from;
	this.to = to;
}

// Extendemos el constructor iterador
Range.prototype = new Iterable();

/**
 * Implementacion del metodo requerido por Iterator
 *
 * @return {Number}
 */
Range.prototype._next = function() {
	// Limitamos la ejecucion a los numeros que
	// pertenezcan al rango
	if(this.actual > this.to)
		throw new Iterable.StopException;

	// Devolvemos el indice actual
	return this.actual++;
}

A modo de final feliz, este es un rango iterado con la implementación dada:

var range = new Range(40, 45);

// Genera un output
// @
// A
// B
// C
// D
// E
range.each(function(number) {
    console.log(unescape('%' + number));
});

Leer más:

 


Shortlink: http://goo.gl/PM5nH




Discussion has just started! Join in...



Pings to this post

  1. […] « Patrones de Diseño y JavaScript: Iterator […]


Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Comment

You may use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>