Чи є jQuery приклад антивірусного "об'єкта бога"?


56

Я хочу запитати - я повільно вивчаю jQuery.

Те , що я бачу , це точний приклад з Бога об'єкта анти-патерну . В основному, все переходить до $функції, якою б вона не була.

Я правий і чи справді jQuery є прикладом цього антидіаграму?


13
Ви, мабуть, задаєте неправильне запитання. Правильне питання: "Як можна jQuery задовільно реалізувати в мові Javascript таким чином, що не вимагає $функції чи jQueryоб'єкта.
Роберт Харві

1
Я завжди хотів по-справжньому задати це питання :).
AnyOne

@RobertHarvey: На жаль, я не знаю достатньо JavaScript, щоб відповісти на це.
Карел Білек

Так (ще 12 символів, які потрібно пройти ...)
Андреа

Це більше схоже на коригувальну бібліотеку
Андрій

Відповіді:


54

Щоб відповісти на це запитання, я збираюся задати вам риторичне запитання про іншу структуру, яка має властивість елементів DOM, якими маніпулює jQuery, - це добрий старий ітератор. Питання:

Скільки операцій вам потрібно на простому ітераторі?

На це питання можна легко відповісти, переглянувши будь-який API Iterator на даній мові. Вам потрібні 3 методи:

  1. Отримайте поточне значення
  2. Перемістіть ітератор до наступного елемента
  3. Перевірте, чи є в Ітераторі більше елементів

Це все, що вам потрібно. Якщо ви можете виконати ці 3 операції, ви можете пройти будь-яку послідовність елементів.

Але це не тільки те, що ти зазвичай хочеш робити з послідовністю елементів, чи не так? Зазвичай у вас є набагато вища мета досягнення. Можливо, ви хочете зробити щось із кожним елементом, ви можете відфільтрувати їх відповідно до якоїсь умови або одним із кількох інших методів. Для отримання додаткових прикладів див. Інтерфейс IEnumerable у бібліотеці LINQ у .NET.

Бачиш, скільки їх? І це лише підмножина всіх методів, які вони могли б поставити на інтерфейс IEnumerable, тому що ви зазвичай комбінуєте їх для досягнення ще більш високих цілей.

Але ось поворот. Ці методи відсутні в інтерфейсі IEnumerable. Вони є простими корисними методами, які насправді приймають IEnumerable як вхід і щось роблять з ним. Тож, хоча в мові C # здається, що на інтерфейсі IEnumerable є багатомільйонні методи, IEnumerable не є об'єктом бога.


Тепер повернемося до jQuery. Давайте задамо це питання ще раз, цього разу з елементом DOM.

Скільки операцій потрібно над елементом DOM?

Знову відповідь досить проста. Всі необхідні вам методи - це методи читання / зміни атрибутів та дочірніх елементів. Ось про це. Все інше - це лише поєднання цих основних операцій.

Але скільки вищого рівня ви хотіли б зробити з елементами DOM? Ну, те саме, що Ітератор: мільярд різних речей. І ось де входить jQuery. JQuery, по суті, передбачає дві речі:

  1. Дуже приємна колекція методів утиліт, до яких ви можете зателефонувати за елементом DOM, і;
  2. Синтаксичний цукор, так що його використання набагато кращий досвід, ніж використання стандартного API DOM.

Якщо ви виймаєте цукрову форму, ви розумієте, що jQuery може бути легко записаний як купа функцій, які вибирають / змінюють елементи DOM. Наприклад:

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

... можна було написати так:

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

Семантично це саме те саме. Однак перша форма має велику перевагу в тому, що вказівки зліва направо на виписки слідують тому, як будуть виконуватися операції. Другий початок посередині, що робить дуже важким для читання код, якщо ви поєднуєте багато операцій разом.

Так що це все означає? Цей jQuery (як LINQ) не є об'єктом божого об'єкта. Натомість це випадок дуже шанованого шаблону під назвою « Декоратор» .


Але знову ж таки, а що за переоцінка $робити всі ті різні речі? Ну, це справді синтаксичний цукор. Всі дзвінки $та подібні до $.getJson()них похідні - це абсолютно різні речі, які просто поділяють подібні імена, щоб ви могли негайно відчути, що вони належать до jQuery. $виконує одне і лише одне завдання: нехай у вас легко впізнається відправна точка для використання jQuery. І всі ті методи, якими можна зателефонувати на об’єкт jQuery, не є симптомом об'єкта бога. Це просто різні утилітні функції, які виконують одну і єдину річ на елементі DOM, передану як аргумент. Позначення .dot є лише тут, оскільки це полегшує написання коду.


6
Оформлений об’єкт, який містить сотні додаткових методів, - чудовий приклад об’єкта Бога. Той факт, що він прикрашає добре спроектований об’єкт з розумним набором методів, є необхідним доказом.
Росс Паттерсон

2
@RossPatterson Ви не згодні? Якщо ви є, я б радив опублікувати власну відповідь. Я думаю, що Лоран добре, але я все ще не визначився.
Ніколь

@RossPatterson Я вважаю , ваш коментар означає , що це той випадок , коли він є Богом Object , але це хороша річ. Я помиляюся?
Лоран Буро-Рой

3
@ LaurentBourgault-Roy Я не згоден з вашим висновком - я вважаю, що jQuery справді є прикладом об'єкта Бога. Але ви зробили таку прекрасну роботу, що я не можу перенести, щоб спростувати вашу відповідь. Дякую за чудове пояснення вашої позиції.
Росс Паттерсон

1
Я повністю не погоджуюся з умовою. У jQuery існує багато функціональних функцій, які не пов'язані з DOM-маніпулюванням.
Борис Янков

19

Ні - $функція насправді перевантажена лише трьома завданнями . Все інше - це дочірні функції, які просто використовують його як простір імен .


17
Але вона повертає « Jquery » об'єкт , який містить велику частину API JQuery - і, я думаю, був би «Бог» об'єкт ФП має в увазі.
Ніколь

2
@NickC, хоча це об'єкт, я думаю, що він справді використовується як простір імен, так само, як Math. Оскільки в JS немає простору імен вбудованого простору, вони просто використовують для цього об’єкт. Хоча я не впевнений, якою була б альтернатива? Покласти всі функції та властивості у глобальний простір імен?
Лоран

5

Основна функція jQuery (наприклад $("div a")) - це, по суті, заводський метод, який повертає екземпляр типу jQuery, який представляє собою набір елементів DOM.

Ці екземпляри типу jQuery мають велику кількість методів маніпуляції з DOM, які працюють на елементах DOM, представлених екземпляром. Хоча це можна вважати класом, який зростав занадто великим, він насправді не відповідає шаблону God Object.

Нарешті, як згадує Майкл Боргвардт, існує також велика кількість утилітних функцій, які використовують $ як простір імен і лише дотично пов'язані з об'єктами jQuery колекції DOM.


1
Чому він не відповідає шаблону об'єкта Бога?
Бенджамін Ходжсон

4

$ не є об'єктом, це простір імен.

Ви б назвали java.lang об'єктом бога через багато класів, які він містить? Це абсолютно дійсний синтаксис для виклику java.lang.String.format(...), за формою дуже схожий на дзвінок у що-небудь на jQuery.

Для того, щоб бути об'єктом бога, об'єкт повинен бути в першу чергу належним об'єктом - містить як дані, так і інтелект для дії на дані. jQuery містить лише методи.

Ще один спосіб поглянути на це: хороший показник того, наскільки об'єктом є бог, є згуртованість - нижча згуртованість означає більше об'єкта бога. Згуртованість каже, що велика частина даних використовується за допомогою багатьох методів. Оскільки в jQuery немає даних, ви займаєтесь математикою - усі методи використовують усі дані, тому jQuery є високо згуртованим, таким чином, не є об'єктом бога.


3

Бенджамін попросив мене уточнити свою позицію, тому я відредагував попереднє повідомлення і додав подальші думки.

Боб Мартін - автор чудової книги під назвою «Чистий код». У цій книзі є розділ (Глава 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-вузлів та робити багато інших речей. Як зазначив Лоран у своєму дописі, jQuery - це щось подібне під кришкою:

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

Розробники jQuery об'єднали всі ці процедури в єдиний клас, який відповідає за всі перелічені вище функції. Таким чином, він явно порушує Принцип єдиної відповідальності, тому він є об'єктом бога. Єдине, тому що це нічого не порушує, тому що це єдиний окремий клас, який працює на одній структурі даних (колекція вузлів DOM). Якщо ми додамо підкласи jQuery або іншу структуру даних, проект би розпався дуже швидко. Тому я не думаю, що ми можемо говорити про oo від jQuery, це швидше процедурно, ніж 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
};

Якщо у вас є методи, які використовують лише загальний інтерфейс, ви можете визначити один або кілька декораторів замість того, щоб копіювати один і той же код між 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, це повний безлад, але це не дуже важливо, він працює добре, і ми його використовуємо.


Ви, здається, стверджуєте, що використання методів інфікування - це ключ, що відрізняється між кодом OO та процедурним кодом. Я не думаю, що це правда. Чи можете ви уточнити свою позицію?
Бенджамін Ходжсон

@BenjaminHodgson Що ви маєте на увазі під «методом інфікування»?
inf3rno

@BenjaminHodgson я уточнив. Я сподіваюся, що це зараз зрозуміло.
inf3rno
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.