Benjamin hat mich gebeten, meine Position zu klären, also habe ich meinen vorherigen Beitrag bearbeitet und weitere Gedanken hinzugefügt.
Bob Martin ist der Autor eines großartigen Buches mit dem Titel Clean Code. In diesem Buch gibt es ein Kapitel (Kapitel 6) mit dem Titel Objekte und Datenstrukturen, in dem er die wichtigsten Unterschiede zwischen Objekten und Datenstrukturen und die Behauptungen, dass wir zwischen ihnen wählen müssen, beschreibt, da das Mischen von Objekten eine sehr schlechte Idee ist.
Diese Verwirrung führt manchmal zu unglücklichen Hybridstrukturen, die halb Objekt- und halb Datenstruktur sind. Sie haben Funktionen, die wichtige Dinge tun, und sie haben entweder öffentliche Variablen oder öffentliche Zugriffsmethoden und Mutatoren, die die privaten Variablen in jeder Hinsicht öffentlich machen und andere externe Funktionen dazu verleiten, diese Variablen so zu verwenden, wie ein prozedurales Programm a verwenden würde Datenstruktur.4 Solche Hybride erschweren das Hinzufügen neuer Funktionen, erschweren aber auch das Hinzufügen neuer Datenstrukturen. Sie sind die schlimmsten beider Welten. Vermeiden Sie es, sie zu erstellen. Sie weisen auf ein durcheinandergebrachtes Design hin, dessen Autoren sich nicht sicher sind oder noch schlimmer, ob sie Schutz vor Funktionen oder Typen benötigen.
Ich denke, DOM ist ein Beispiel für diese Objekt- und Datenstruktur-Hybride. Zum Beispiel schreiben wir mit DOM Codes wie diesen:
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 sollte eindeutig eine Datenstruktur anstelle eines Hybrids sein.
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();
Das jQuery-Framework besteht aus einer Reihe von Prozeduren, mit denen Sie eine Sammlung von DOM-Knoten auswählen und ändern und viele andere Aufgaben ausführen können. Wie Laurent in seinem Beitrag betonte, ist jQuery so etwas unter der Haube:
html(select("#body"), "<p>hello</p>");
Die Entwickler von jQuery haben alle diese Prozeduren in einer einzigen Klasse zusammengefasst, die für alle oben aufgeführten Funktionen verantwortlich ist. Es verstößt also eindeutig gegen das Prinzip der Einzelverantwortung und ist somit ein göttlicher Gegenstand. Das einzige, weil es nichts kaputt macht, weil es eine einzelne Standalone-Klasse ist, die mit einer einzelnen Datenstruktur (der Auflistung von DOM-Knoten) arbeitet. Wenn wir jQuery-Unterklassen oder eine andere Datenstruktur hinzufügen würden, würde das Projekt sehr schnell zusammenbrechen. Ich glaube also nicht, dass wir mit jQuery über oo sprechen können, es ist eher prozedural als oo, obwohl es eine Klasse definiert.
Was Laurent behauptet, ist ein völliger Unsinn:
Was bedeutet das alles? Diese jQuery (wie LINQ) ist nicht das Gott-Objekt-Anti-Pattern. Stattdessen handelt es sich um ein sehr angesehenes Muster, das als Dekorateur bezeichnet wird.
Beim Decorator-Muster geht es darum, neue Funktionen hinzuzufügen, indem die Benutzeroberfläche beibehalten und vorhandene Klassen nicht geändert werden. Zum Beispiel:
Sie können 2 Klassen definieren, die die gleiche Schnittstelle implementieren, jedoch mit einer völlig anderen Implementierung:
/**
* @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
};
Wenn Sie über Methoden verfügen, die nur die gemeinsame Schnittstelle verwenden, können Sie einen oder mehrere Decoratoren definieren, anstatt denselben Code zwischen A und B einzufügen. Sie können diese Decoratoren auch in einer verschachtelten Struktur verwenden.
/**
* @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);
};
Sie können also die ursprünglichen Instanzen durch die Decorator-Instanzen in Code höherer Abstraktionsebene ersetzen.
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(...);
Die Schlussfolgerung, dass jQuery kein Decorator von irgendetwas ist, weil es nicht die gleiche Schnittstelle wie Array, NodeList oder ein anderes DOM-Objekt implementiert. Es implementiert eine eigene Schnittstelle. Die Module werden auch nicht als Dekorateure verwendet, sondern überschreiben einfach den ursprünglichen Prototyp. Das Decorator-Muster wird also nicht in der gesamten jQuery-Bibliothek verwendet. Die jQuery-Klasse ist einfach ein riesiger Adapter, mit dem wir dieselbe API von vielen verschiedenen Browsern verwenden können. Aus oo Perspektive ist es ein komplettes Durcheinander, aber das ist nicht wirklich wichtig, es funktioniert gut und wir benutzen es.
$
Funktion noch einjQuery
Objekt erfordert