Benjamin mi ha chiesto di chiarire la mia posizione, quindi ho modificato il mio post precedente e aggiunto ulteriori pensieri.
Bob Martin è l'autore di un grande libro intitolato Clean Code. In quel libro c'è un capitolo (Capitolo 6) chiamato Strutture di Oggetti e Dati, che discute le più importanti differenze tra oggetti e strutture dati e afferma che dobbiamo scegliere tra di loro, perché mescolarle è una pessima idea. / p>
This confusion sometimes leads to unfortunate hybrid structures that are half object and half data structure. They have functions that do significant things, and they also have either public variables or public accessors and mutators that, for all intents and purposes, make the private variables public, tempting other external functions to use those variables the way a procedural program would use a data structure.4 Such hybrids make it hard to add new functions but also make it hard to add new data structures. They are the worst of both worlds. Avoid creating them. They are indicative of a muddled design whose authors are unsure of—or worse, ignorant of—whether they need protection from functions or types.
Penso che DOM sia un esempio di questi ibridi di strutture di dati e di oggetti. Ad esempio dal DOM scriviamo codici come questo:
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
DOM dovrebbe essere chiaramente una struttura dati invece di un ibrido.
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();
Il framework jQuery è un insieme di procedure, che possono selezionare e modificare una collezione di nodi DOM e fare molte altre cose. Come ha sottolineato Laurent nel suo post, jQuery è qualcosa di simile sotto il cofano:
html(select("#body"), "<p>hello</p>");
Gli sviluppatori di jQuery hanno unito tutte queste procedure in una singola classe, responsabile di tutte le funzionalità sopra elencate. Quindi viola chiaramente il principio di singola responsabilità e quindi è un oggetto divino. L'unica cosa perché non infrange nulla, perché è una singola classe standalone che funziona su una singola struttura di dati (la collezione di nodi DOM). Se aggiungessimo sottoclassi jQuery o un'altra struttura dati, il progetto collasserebbe molto velocemente. Quindi non penso che possiamo parlare di oo di jQuery è piuttosto procedurale di oo nonostante il fatto che definisca una classe.
Ciò che Laurent afferma è una completa assurdità:
So what does it all mean? That jQuery (like LINQ) is not the God object anti-pattern. It's instead a case of a very respected pattern called the Decorator.
Il pattern Decorator riguarda l'aggiunta di nuove funzionalità mantenendo l'interfaccia e non modificando le classi esistenti. Ad esempio:
Puoi definire 2 classi che implementano la stessa interfaccia, ma con un'implementazione completamente diversa:
/**
* @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
};
Se hai metodi che usano solo l'interfaccia comune, puoi definire uno o più Decorator invece di copiare lo stesso codice tra A e B. Puoi usare questi decoratori anche in una struttura annidata.
/**
* @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);
};
Quindi puoi sostituire le istanze originali con le istanze di decoratore nel codice di livello di astrazione più elevato.
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 conclusione che jQuery non è un decoratore di nulla, perché non implementa la stessa interfaccia di Array, NodeList o qualsiasi altro oggetto DOM. Implementa la propria interfaccia. I moduli non sono usati come decoratori, semplicemente sostituiscono il prototipo originale. Quindi il pattern Decorator non è utilizzato nell'intera libreria jQuery. La classe jQuery è semplicemente un enorme adattatore che ci consente di utilizzare la stessa API da molti diversi browser. Da questo punto di vista è un disastro completo, ma non ha molta importanza, funziona bene e lo usiamo.