jQuery è un esempio di "dio oggetto" antipattern?

55

Voglio chiedere - sto imparando lentamente jQuery.

Quello che vedo è un esempio esatto di un Oggetto di Dio anti- modello . Fondamentalmente, tutto va alla funzione $ , qualunque essa sia.

Ho ragione ed è veramente jQuery un esempio di questo anti-pattern?

    
posta Karel Bílek 16.12.2012 - 00:07
fonte

5 risposte

53

Per rispondere a questa domanda, ti farò una domanda retorica su un'altra struttura che ha proprietà simili agli elementi DOM manipolati da jQuery, cioè il buon vecchio iteratore. La domanda è:

Quante operazioni hai bisogno di su un semplice iteratore?

La domanda può essere risolta facilmente osservando qualsiasi API Iterator in una determinata lingua. Hai bisogno di 3 metodi:

  1. Ottieni il valore corrente
  2. Sposta l'iteratore all'elemento successivo
  3. Controlla se l'Iterator ha più elementi

Questo è tutto ciò di cui hai bisogno. Se è possibile eseguire queste 3 operazioni, è possibile passare a qualsiasi sequenza di elementi.

Ma non è solo quello che di solito vuoi fare con una sequenza di elementi, vero? Di solito hai un obiettivo di livello molto più alto da raggiungere. Potresti voler fare qualcosa con ogni elemento, potresti volerlo filtrare in base ad alcune condizioni, o uno dei tanti altri metodi. Vedere l' Interfaccia IEnumerable nella libreria LINQ in .NET per ulteriori esempi.

Vedi quanti ce ne sono? E questo è solo un sottoinsieme di tutti i metodi che potrebbero aver messo sull'interfaccia IEnumerable, perché di solito li unisci per raggiungere obiettivi ancora più alti.

Ma ecco il colpo. Questi metodi non si trovano nell'interfaccia IEnumerable. Sono semplici metodi di utilità che in realtà prendono un IEnumerable come input e fanno qualcosa con esso. Quindi, mentre nel linguaggio C # sembra che ci siano metodi bajillion sull'interfaccia IEnumerable, IEnumerable non è un oggetto dio.

Ora torna a jQuery. Facciamo ancora una volta questa domanda, questa volta con un elemento DOM.

Di quante operazioni hai bisogno su un elemento DOM?

Ancora una volta la risposta è piuttosto semplice. Tutti i metodi necessari sono metodi per leggere / modificare gli attributi e gli elementi figli. Questo è tutto. Tutto il resto è solo una combinazione di queste operazioni di base.

Ma quanto roba di livello più alto vorresti fare con un elemento DOM? Bene, come un Iterator: un bajillion di cose diverse. Ed ecco dove entra jQuery. JQuery, in sostanza, fornisce due cose:

  1. Un bel gruppo di metodi di utilità che potresti voler chiamare su un elemento DOM, e;
  2. Zucchero sintattico in modo che utilizzarlo sia un'esperienza molto migliore rispetto all'utilizzo dell'API DOM standard.

Se togli il modulo zuccherato, ti rendi conto che jQuery potrebbe facilmente essere stato scritto come un insieme di funzioni che selezionano / modificano elementi DOM. Ad esempio:

$("#body").html("<p>hello</p>");

... potrebbe essere stato scritto come:

html($("#body"), "<p>hello</p>");

Semanticamente è la stessa identica cosa. Tuttavia, la prima forma ha il grande vantaggio che l'ordine da sinistra a destra delle istruzioni segue l'ordine in cui verranno eseguite le operazioni. Il secondo avvio nel mezzo, che rende molto difficile leggere il codice se si combinano molte operazioni insieme.

Quindi cosa significa tutto questo? Quel jQuery (come LINQ) non è l'anti-modello dell'oggetto di Dio. È invece un caso di un modello molto rispettato chiamato Decorator .

Ma poi di nuovo, per quanto riguarda l'override di $ per fare tutte quelle cose diverse? Bene, questo è solo zucchero sintattico davvero. Tutte le chiamate a $ e le sue derivate come $.getJson() sono cose completamente diverse che capita di condividere nomi simili in modo che tu possa immediatamente sentire di appartenere a jQuery. $ esegue una sola e unica attività: ti consente di avere un punto di partenza facilmente riconoscibile per utilizzare jQuery. E tutti quei metodi che puoi chiamare su un oggetto jQuery non sono un sintomo di un oggetto dio. Sono semplicemente diverse funzioni di utilità che eseguono ciascuna una sola cosa su un elemento DOM passato come argomento. La notazione .dot è qui solo perché rende più semplice la scrittura del codice.

    
risposta data 16.12.2012 - 07:30
fonte
19

No: la funzione $ è in realtà solo sovraccaricata per tre attività . Tutto il resto sono funzioni secondarie che lo usano come uno spazio dei nomi .

    
risposta data 16.12.2012 - 00:26
fonte
5

La principale funzione jQuery (ad esempio $("div a") ) è essenzialmente un metodo factory che restituisce un'istanza del tipo jQuery che rappresenta una raccolta di elementi DOM.

Queste istanze del tipo jQuery hanno un gran numero di metodi di manipolazione DOM disponibili che operano sugli elementi DOM rappresentati dall'istanza. Anche se questo potrebbe essere considerato una classe che è diventata troppo grande, non si adatta veramente al modello di Oggetto di Dio.

Infine, come menziona Michael Borgwardt, ci sono anche un gran numero di funzioni di utilità che usano $ come spazio dei nomi e sono solo collegate tangenzialmente agli oggetti jQuery della raccolta DOM.

    
risposta data 16.12.2012 - 03:02
fonte
4

$ non è un oggetto, è un namespace.

Chiameresti java.lang un oggetto dio a causa delle molte classi che contiene? È assolutamente valida la sintassi per chiamare java.lang.String.format(...) , molto simile nella forma per chiamare qualsiasi cosa su jQuery.

Un oggetto, per essere un oggetto di Dio, deve essere in primo luogo un oggetto appropriato - contenente sia i dati che l'intelligenza per agire sui dati. jQuery contiene solo i metodi.

Un altro modo per guardarlo: una buona misura per quanto un oggetto di dio è un oggetto è la coesione - una coesione più bassa significa più di un oggetto divino. La coesione dice che gran parte dei dati è utilizzata da quanti dei metodi. Dato che non ci sono dati in jQuery, fai i calcoli matematici: tutti i metodi usano tutti i dati, quindi jQuery è altamente coesivo, quindi non è un oggetto divino.

    
risposta data 04.05.2015 - 11:13
fonte
3

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.

    
risposta data 17.02.2015 - 17:44
fonte

Leggi altre domande sui tag