Benjamin mi ha chiesto di chiarire la mia posizione, quindi ho modificato il mio precedente post e aggiunto ulteriori riflessioni.
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 differenze più importanti tra oggetti e strutture di dati e afferma che dobbiamo scegliere tra loro, perché mescolarle è una pessima idea.
Questa confusione a volte porta a sfortunate strutture ibride che sono metà oggetto e metà struttura dati. Hanno funzioni che fanno cose significative e hanno anche variabili pubbliche o accessori pubblici e mutatori che, a tutti gli effetti, rendono pubbliche le variabili private, tentando altre funzioni esterne di usare quelle variabili come un programma procedurale userebbe struttura dei dati.4 Tali ibridi rendono difficile l'aggiunta di nuove funzioni ma anche l'aggiunta di nuove strutture di dati. Sono i peggiori di entrambi i mondi. Evita di crearli. Sono indicativi di un disegno confuso i cui autori non sono sicuri di - o peggio, ignoranti - se hanno bisogno di protezione da funzioni o tipi.
Penso che DOM sia un esempio di questi ibridi di oggetti e strutture di dati. Ad esempio da 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 di dati anziché 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 raccolta 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 un'unica classe, che è responsabile di tutte le funzionalità sopra elencate. Quindi viola chiaramente il Principio della singola responsabilità e quindi è un oggetto divino. L'unica cosa perché non rompe nulla, perché è una singola classe autonoma che funziona su una singola struttura di dati (la raccolta di nodi DOM). Se aggiungessimo sottoclassi jQuery o un'altra struttura di dati, il progetto collasserebbe molto velocemente. Quindi non penso che possiamo parlare di oo con jQuery è piuttosto procedurale che oo nonostante il fatto che definisca una classe.
Ciò che Laurent afferma è una totale assurdità:
Quindi cosa significa tutto questo? Quel jQuery (come LINQ) non è l'anti-pattern dell'oggetto God. È invece un caso di un modello molto rispettato chiamato Decoratore.
Il modello Decorator riguarda l'aggiunta di nuove funzionalità mantenendo l'interfaccia e non modificando le classi esistenti. Per esempio:
È possibile 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 incollare lo stesso codice tra A e B. Puoi usare questi decoratori anche in una struttura nidificata.
/**
* @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 del decoratore con un codice di livello di astrazione più alto.
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 vengono utilizzati anche come decoratori, ma semplicemente sostituiscono il prototipo originale. Quindi il modello Decorator non è usato nell'intera libreria jQuery. La classe jQuery è semplicemente un enorme adattatore che ci consente di utilizzare la stessa API da molti browser diversi. Dal punto di vista oo è un casino completo, ma non importa, funziona bene e lo usiamo.
$
funzione o unjQuery
oggetto.