Answers:
为了回答这个问题,我要问一个关于另一个结构的反问,这个结构具有与jQuery操纵的DOM元素相似的属性,那就是很好的旧迭代器。问题是:
您需要在一个简单的迭代器上执行多少操作?
通过查看给定语言的任何Iterator API,可以轻松回答该问题。您需要3种方法:
这就是您所需要的。如果可以执行这3个操作,则可以遍历任何元素序列。
但这不仅是您通常要对一系列元素进行的处理,对吗?通常,您有更高的目标要实现。您可能需要对每个元素进行某些操作,可能要根据某种条件或其他几种方法之一对它们进行过滤。有关更多示例,请参见.NET中LINQ库中的IEnumerable接口。
你看到有多少吗?那只是它们可以放在IEnumerable接口上的所有方法的一部分,因为您通常将它们组合在一起以实现更高的目标。
但这是转折。这些方法不在IEnumerable接口上。它们是简单的实用程序方法,实际上将IEnumerable作为输入并对其进行处理。因此,尽管在C#语言中,感觉IEnumerable接口上有成千上万的方法,但IEnumerable并不是上帝的对象。
现在回到jQuery。让我们再次问这个问题,这次是一个DOM元素。
您需要对DOM元素进行多少次操作?
同样,答案很简单。您需要的所有方法都是读取/修改属性和子元素的方法。就是这样 其他所有东西只是这些基本操作的组合。
但是,您想对DOM元素进行多少高级处理?好吧,和迭代器一样:成千上万种不同的事物。这就是jQuery的用处。jQuery本质上提供两件事:
如果您采用加糖形式,您将意识到jQuery可能很容易被编写为一堆用于选择/修改DOM元素的函数。例如:
$("#body").html("<p>hello</p>");
...本来可以写成:
html($("#body"), "<p>hello</p>");
从语义上讲,这是完全相同的事情。但是,第一种形式具有很大的优势,即语句从左到右的顺序遵循将要执行的操作的顺序。中间的第二个起点,如果将许多操作组合在一起,将很难阅读代码。
那是什么意思呢?那个jQuery(像LINQ)不是上帝对象的反模式。相反,这是一个非常受人尊敬的模式,称为Decorator的情况。
但是话又说回来,$
做所有这些不同的事情的优先级又如何呢?好吧,那实际上只是语法糖。所有对它们的调用$
及其派生类$.getJson()
都是完全不同的事物,只是碰巧共享相似的名称,因此您可以立即感觉到它们属于jQuery。$
仅执行一项任务:让您有一个容易识别的起点来使用jQuery。您可以在jQuery对象上调用的所有这些方法都不是God对象的症状。它们是完全不同的实用程序函数,每个函数在作为参数传递的DOM元素上仅执行一项操作。.dot表示法仅在此处,因为它使编写代码更加容易。
jQuery的主要功能(例如$("div a")
)本质上是一个工厂方法,该方法返回代表DOM元素集合的jQuery类型的实例。
这些jQuery类型的实例具有大量可用的DOM操作方法,它们可对由该实例表示的DOM元素进行操作。尽管可以认为这是一个太大的类,但它实际上并不适合God Object模式。
最后,正如Michael Borgwardt所提到的,还有大量的实用程序函数将$用作命名空间,并且仅与DOM集合jQuery对象切向相关。
$
不是对象,而是名称空间。
由于Java.lang包含许多类,您会称其为God对象吗?它是绝对有效的语法java.lang.String.format(...)
,其形式与在jQuery上调用任何形式非常相似。
为了成为上帝的对象,一个对象首先必须是一个适当的对象-既包含数据又包含对数据采取行动的智慧。jQuery仅包含方法。
另一种看待它的方法:内聚力是衡量一个神像有多少的一个好方法-较低的内聚力意味着更多神像。内聚性说,很多数据被多少种方法使用。由于jQuery中没有数据,因此您需要进行数学运算-所有方法都使用所有数据,因此jQuery具有高度的内聚性,因此不是上帝的对象。
本杰明要求我阐明我的立场,因此我编辑了我以前的文章并增加了进一步的想法。
鲍勃·马丁(Bob Martin)是一本名为《清洁代码》(Clean Code)的伟大著作的作者。在那本书中,有一章(第6章)称为对象和数据结构,他讨论了对象和数据结构之间最重要的区别,并声称我们必须在它们之间进行选择,因为混合它们是一个非常糟糕的主意。
这种混乱有时会导致不幸的是一半对象和一半数据结构的混合结构。它们具有执行重要功能的函数,也具有公共变量或公共访问器和更改器,它们出于所有意图和目的,将私有变量公开,从而诱使其他外部函数以程序程序将要使用的方式使用这些变量。数据结构。4这样的混合体很难添加新功能,但是也很难添加新数据结构。他们是两全其美的。避免创建它们。它们表明设计混乱,其作者不确定是否需要保护其功能或类型,或更不确定,更无知。
我认为DOM是这些对象和数据结构混合的示例。例如,通过DOM我们编写如下代码:
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显然应该是数据结构,而不是混合结构。
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();
jQuery框架是一堆程序,它们可以选择和修改DOM节点的集合并执行许多其他操作。就像Laurent在他的帖子中指出的那样,jQuery是这样的:
html(select("#body"), "<p>hello</p>");
jQuery的开发人员将所有这些过程合并到一个类中,该类负责上面列出的所有功能。因此,它显然违反了单一责任原则,因此它是上帝的对象。唯一的原因是它不会破坏任何内容,因为它是一个可在单个数据结构(DOM节点的集合)上工作的独立类。如果我们要添加jQuery子类或其他数据结构,则该项目将很快崩溃。因此,我认为我们无法通过jQuery来讨论oo,尽管它定义了一个类,但它实际上是过程而不是oo。
洛朗声称是完全胡说八道:
那是什么意思呢?那个jQuery(像LINQ)不是上帝对象的反模式。相反,这是一种非常受人尊敬的模式,称为装饰器。
Decorator模式是通过保留接口而不修改现有类来添加新功能。例如:
您可以定义2个类,这些类实现相同的接口,但实现方式完全不同:
/**
* @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
};
如果您有仅使用公共接口的方法,则可以定义一个或多个Decorator,而不是在A和B之间复制粘贴相同的代码。即使在嵌套结构中也可以使用这些装饰器。
/**
* @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);
};
因此,您可以在更高的抽象级别代码中用装饰器实例替换原始实例。
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(...);
jQuery不是任何东西的装饰器的结论,因为它没有实现与Array,NodeList或任何其他DOM对象相同的接口。它实现了自己的接口。这些模块也不用作装饰器,它们只是覆盖原始原型。因此,Decorator模式未在整个jQuery库中使用。jQuery类只是一个巨大的适配器,它使我们可以在许多不同的浏览器中使用相同的API。从oo的角度看,这是一团糟,但这并不重要,它运作良好,我们可以使用它。
$
功能或jQuery
对象。