Benjamin me pidió que aclarara mi posición, así que edité mi publicación anterior y agregué más pensamientos.
Bob Martin es autor de un gran libro titulado Clean Code. En ese libro hay un capítulo (Capítulo 6.) llamado Objetos y estructuras de datos, en el que analiza las diferencias más importantes entre objetos y estructuras de datos y afirma que tenemos que elegir entre ellos, porque mezclarlos es una muy mala idea.
Esta confusión a veces conduce a desafortunadas estructuras híbridas que son mitad estructura de objeto y mitad de datos. Tienen funciones que hacen cosas importantes, y también tienen variables públicas o accesores públicos y mutadores que, para todos los efectos, hacen públicas las variables privadas, tentando a otras funciones externas a usar esas variables de la manera en que un programa procesal usaría un estructura de datos.4 Tales híbridos dificultan la adición de nuevas funciones, pero también dificultan la adición de nuevas estructuras de datos. Son lo peor de ambos mundos. Evita crearlos. Son indicativos de un diseño confuso cuyos autores no están seguros, o peor aún, ignoran, si necesitan protección contra funciones o tipos.
Creo que DOM es un ejemplo de estos híbridos de estructura de datos y objetos. Por ejemplo, por DOM escribimos códigos como este:
el.appendChild(node);
el.childNodes;
// bleeding internals
el.setAttribute(attr, val);
el.attributes;
// bleeding internals
el.style.color;
// at least this is okay
el = document.createElement(tag);
doc = document.implementation.createHTMLDocument();
// document is both a factory and a tree root
El DOM debería ser claramente una estructura de datos en lugar de un híbrido.
el.childNodes.add(node);
// or el.childNodes[el.childNodes.length] = node;
el.childNodes;
el.attributes.put(attr, val);
// or el.attributes[attr] = val;
el.attributes;
el.style.get("color");
// or el.style.color;
factory = new HtmlNodeFactory();
el = factory.createElement(document, tag);
doc = factory.createDocument();
El marco jQuery es un conjunto de procedimientos, que pueden seleccionar y modificar una colección de nodos DOM y hacer muchas otras cosas. Como Laurent señaló en su publicación, jQuery es algo así debajo del capó:
html(select("#body"), "<p>hello</p>");
Los desarrolladores de jQuery fusionaron todos estos procedimientos en una sola clase, que es responsable de todas las características enumeradas anteriormente. Por lo tanto, viola claramente el Principio de responsabilidad única y, por lo tanto, es un objeto de Dios. Lo único porque no rompe nada, porque es una sola clase independiente que funciona en una sola estructura de datos (la colección de nodos DOM). Si añadiéramos subclases de jQuery u otra estructura de datos, el proyecto colapsaría muy rápido. Por lo tanto, no creo que podamos hablar de oo con jQuery, es más bien procesal que oo a pesar del hecho de que define una clase.
Lo que Laurent afirma es un completo disparate:
Entonces, ¿qué significa todo ésto? Ese jQuery (como LINQ) no es el antipatrón del objeto de Dios. En cambio, se trata de un patrón muy respetado llamado Decorador.
El patrón Decorador consiste en agregar nuevas funcionalidades manteniendo la interfaz y no modificando las clases existentes. Por ejemplo:
Puede definir 2 clases que implementan la misma interfaz, pero con una implementación completamente diferente:
/**
* @interface
*/
var Something = function (){};
/**
* @argument {string} arg1 The first argument.
* @argument {string} arg2 The second argument.
*/
Something.prototype.doSomething = function (arg1, arg2){};
/**
* @class
* @implements {Something}
*/
var A = function (){
// ...
};
/**
* @argument {string} arg1 The first argument.
* @argument {string} arg2 The second argument.
*/
A.prototype.doSomething = function (arg1, arg2){
// doSomething implementation of A
};
/**
* @class
* @implements {Something}
*/
var B = function (){
// ...
};
/**
* @argument {string} arg1 The first argument.
* @argument {string} arg2 The second argument.
*/
B.prototype.doSomething = function (arg1, arg2){
// doSomething implementation of B
// it is completely different from the implementation of A
// that's why it cannot be a sub-class of A
};
Si tiene métodos que usan solo la interfaz común, puede definir uno o más Decoradores en lugar de copiar y pegar el mismo código entre A y B. Puede usar estos decoradores incluso en una estructura anidada.
/**
* @class
* @implements {Something}
* @argument {Something} something The decorated object.
*/
var SomethingDecorator = function (something){
this.something = something;
// ...
};
/**
* @argument {string} arg1 The first argument.
* @argument {string} arg2 The second argument.
*/
SomethingDecorator.prototype.doSomething = function (arg1, arg2){
return this.something.doSomething(arg1, arg2);
};
/**
* A new method which can be common by A and B.
*
* @argument {function} done The callback.
* @argument {string} arg1 The first argument.
* @argument {string} arg2 The second argument.
*/
SomethingDecorator.prototype.doSomethingDelayed = function (done, arg1, arg2){
var err, res;
setTimeout(function (){
try {
res = this.doSomething(o.arg1, o.arg2);
} catch (e) {
err = e;
}
callback(err, res);
}, 1000);
};
Por lo tanto, puede sustituir las instancias originales con las instancias del decorador en un código de nivel de abstracción superior.
function decorateWithManyFeatures(something){
var d1 = new SomethingDecorator(something);
var d2 = new AnotherSomethingDecorator(d1);
// ...
return dn;
}
var a = new A();
var b = new B();
var decoratedA = decorateWithManyFeatures(a);
var decoratedB = decorateWithManyFeatures(b);
decoratedA.doSomethingDelayed(...);
decoratedB.doSomethingDelayed(...);
La conclusión de que jQuery no es un Decorador de nada, porque no implementa la misma interfaz que Array, NodeList o cualquier otro objeto DOM. Implementa su propia interfaz. Los módulos tampoco se usan como decoradores, simplemente anulan el prototipo original. Por lo tanto, el patrón Decorator no se usa en toda la biblioteca jQuery. La clase jQuery es simplemente un gran adaptador que nos permite usar la misma API por muchos navegadores diferentes. Desde el punto de vista de la perspectiva, es un completo desastre, pero eso no importa, funciona bien y lo usamos.
$
función o unjQuery
objeto?