57

I want to ask – I am slowly learning jQuery.

What I see is an exact example of a God Object anti-pattern. Basically, everything goes to the $ function, whatever it is.

Am I right and is jQuery really an example of this anti-pattern?

5
  • 13
    You're probably asking the wrong question. The right question is, "How could jQuery be satisfactorily implemented in the Javascript language in a way that doesn't require the $ function or a jQuery object. Commented Dec 16, 2012 at 6:49
  • 1
    I always wanted to ask this question really :).
    – AnyOne
    Commented Dec 16, 2012 at 21:32
  • @RobertHarvey: sadly, I don't know enough about JavaScript to answer this. Commented Dec 17, 2012 at 0:00
  • Yes (12 more characters to go...)
    – Andrea
    Commented Feb 18, 2015 at 10:00
  • It's more like a corrective library
    – Andrew
    Commented Sep 11, 2018 at 21:44

5 Answers 5

54

To answer that question, I'm going to ask you a rhetorical question about another structure that have similar property to the DOM elements that jQuery manipulates, that is the good old iterator. The question is:

How many operation do you need on a simple iterator?

The question can be answered easily by looking at any Iterator API in a given language. You need 3 methods:

  1. Get the current value
  2. Move the iterator to the next element
  3. Check if the Iterator has more elements

That's all you need. If you can perform those 3 operations, you can go trough any sequence of elements.

But that is not only what you usually want to do with a sequence of elements, is it? You usually have a much higher level goal to achieve. You may want to do something with every element, you may want to filter them according to some condition, or one of several other methods. See the IEnumerable interface in the LINQ library in .NET for more examples.

Do you see how many there are? And that is just a subset of all the methods they could have put on the IEnumerable interface, because you usually combine them to achieve even higher goals.

But here is the twist. Those methods are not on the IEnumerable interface. They are simple utility methods that actually take a IEnumerable as input and do something with it. So while in the C# language it feels like there are a bajillion methods on the IEnumerable interface, IEnumerable is not a god object.


Now back to jQuery. Lets ask that question again, this time with a DOM element.

How many operation do you need on a DOM element?

Again the answer is pretty straightforward. All the methods you need are methods to read/modify the attributes and the child elements. That's about it. Everything thing else is only a combination of those basic operations.

But how much higher level stuff would you want to do with a DOM elements? Well, same as an Iterator: a bajillion different things. And that's where jQuery comes in. jQuery, in essence provide two things:

  1. A very nice collections of utilities methods that you may want to call on a DOM element, and;
  2. Syntactic sugar so that using it is a much better experience than using the standard DOM API.

If you take out the sugared form, you realise that jQuery could easily have been written as a bunch of functions that select/modify DOM elements. For example:

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

...could have been written as:

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

Semantically it's the exact same thing. However the first form has the big advantage that the order left-to-right of the statements follow the order the operations will be executed. The second start in the middle, which makes for very hard to read code if you combine lots of operations together.

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.


But then again, what about the override of $ to do all those different things? Well, that is just syntactic sugar really. All the calls to $ and its derivatives like $.getJson() are completely different things that just happen to share similar names so that you can immediately feel that they belong to jQuery. $ performs one and only one task: let you have an easily recognizable starting point to use jQuery. And all those methods that you can call on a jQuery object are not a symptom of a god object. They are simply different utility functions that each perform one and only thing on a DOM element passed as an argument. The .dot notation is only here because it make writing code easier.

5
  • 6
    A decorated object that has hundreds of added methods is a great example of a God Object. The fact that it decorates a well-designed object with a reasonable set of methods is the proof you need. Commented Dec 16, 2012 at 15:25
  • 2
    @RossPatterson Are you disagreeing? If you are, I'd encourage you to post your own answer. I think Laurent's is good, but I'm still undecided.
    – Nicole
    Commented Dec 16, 2012 at 22:39
  • @RossPatterson I assume your comment mean that this a case where it is a God Object but it is a good thing. Am I mistaken? Commented Dec 17, 2012 at 5:11
  • 3
    @LaurentBourgault-Roy I disagree with your conclusion - I believe jQuery is indeed a God Object example. But you did such a fine job that I can't bear to downvote your answer. Thanks for a great explanation of your position. Commented Dec 17, 2012 at 11:47
  • 1
    I disagree completely with the conculsion. There are many utility functions on jQuery that are not related to DOM manipulation. Commented Dec 18, 2012 at 23:17
19

No - the $ function is actually only overloaded for three tasks. Everything else are child functions that just use it as a namespace.

2
  • 17
    But it returns a "jQuery" object which contains much of the jQuery API -- and, I think, would be the "God" object the OP is referring to.
    – Nicole
    Commented Dec 16, 2012 at 0:02
  • 2
    @NickC, even though it's an object, in that case I think it's indeed used as a namespace, just like Math. Since there's no build-in namespace in JS, they just use an object for that. Although I'm not sure what the alternative would be? Put all the functions and properties in the global namespace?
    – laurent
    Commented Jan 12, 2013 at 5:49
5

The main jQuery function (e.g. $("div a")) is essentially a factory method that returns an instance of the jQuery type that represents a collection of DOM elements.

These instances of the jQuery type have a large number of DOM manipulation methods available which operate on the DOM elements represented by the instance. While this could conceivably be considered a class that's grown too large, it doesn't really fit the God Object pattern.

Finally, as Michael Borgwardt mentions, there are also a large number of utility functions that use $ as a namespace and are only tangentially related to the DOM collection jQuery objects.

1
  • 1
    Why doesn't it fit the God Object pattern? Commented Feb 17, 2015 at 20:46
4

$ is not an object, it's a namespace.

Would you call java.lang a god object because of the many classes it contains? It's absolutely valid syntax to call java.lang.String.format(...), very similar in form to calling anything on jQuery.

An object, in order to be a god object, has to be a proper object in the first place - containing both data and the intelligence to act upon the data. jQuery only contains the methods.

Another way to look at it: a good measure for how much of a god object an object is is cohesion - lower cohesion means more of a god object. Cohesion says ow much of the data is used by how many of the methods. Since there's no data in jQuery, you do the math - all methods use all the data, so jQuery is highly cohesive, thus not being a god object.

3

Benjamin asked me to clarify my position, so I edited my previous post and added further thoughts.

Bob Martin is the author of a great book titled as Clean Code. In that book there is a chapter (Chapter 6.) called Objects and Data structures, which he discusses the most important differences between objects and data structures and claims that we have to choose between them, because mixing them is a very bad idea.

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.

I think DOM is an example of these object and data structure hybrids. For example by DOM we write codes like this:

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 should be clearly a data structure instead of a hybrid.

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();

The jQuery framework is a bunch of procedures, which can select and modify a collection of DOM nodes and do many other things. As Laurent pointed out in his post, jQuery is something like this under the hood:

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

The developers of jQuery merged all of these procedures into a single class, which is responsible for all of the features listed above. So it clearly violates the Single Responsibility Principle and so it is a god object. The only thing because it does not break anything, because it is a single standalone class which works on a single data structure (the collection of DOM nodes). If we would add jQuery subclasses or another data structure the project would collapse very fast. So I don't think we can talk about oo by jQuery it's rather procedural than oo despite the fact that it defines a class.

What Laurent claims is a complete nonsense:

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.

The Decorator pattern is about adding new functionality by keeping the interface and not modifying existing classes. For example:

You can define 2 classes which implements the same interface, but with a completely different implementation:

/**
 * @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
};

If you have methods which use the common interface only, then you can define one or more Decorator instead of copy-pasting the same code between A and B. You can use these decorators even in a nested structure.

/**
 * @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);
};

So you can substitute the original instances with the decorator instances in higher abstraction level code.

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(...);

The conclusion that jQuery is not a Decorator of anything, because it does not implements the same interface as Array, NodeList or any other DOM object. It implements it's own interface. The modules are not used as Decorators as well, they simply override the original prototype. So the Decorator pattern is not used in the whole jQuery lib. The jQuery class is simply a huge adapter which let us use the same API by many different browsers. From oo perspective it is a complete mess, but that does not really matter, it works well, and we use it.

3
  • You seem to be arguing that the use of infix methods is the key different between OO code and procedural code. I don't think this is true. Could you clarify your position? Commented Feb 17, 2015 at 20:48
  • @BenjaminHodgson What do you mean under "infix method"?
    – inf3rno
    Commented Feb 18, 2015 at 5:32
  • @BenjaminHodgson I clarified. I hope it is clear now.
    – inf3rno
    Commented Feb 18, 2015 at 8:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.