O jQuery é um exemplo de antipadrão de "objeto divino"?


56

Eu quero perguntar - estou aprendendo lentamente o jQuery.

O que vejo é um exemplo exato de um antipadrão de Objeto de Deus . Basicamente, tudo vai para a $função, seja ela qual for.

Estou certo e o jQuery é realmente um exemplo desse anti-padrão?


13
Você provavelmente está fazendo a pergunta errada. A pergunta certa é: "Como poderia jQuery ser satisfatoriamente implementado na linguagem Javascript de uma forma que não requer a $função ou um jQueryobjeto.
Robert Harvey

11
Eu sempre quis fazer essa pergunta realmente :).
AnyOne

@RobertHarvey: infelizmente, não sei o suficiente sobre JavaScript para responder a isso.
Karel Bílek

Sim (faltam mais 12 caracteres ...)
Andrea

É mais como uma biblioteca corretiva
Andrew

Respostas:


54

Para responder a essa pergunta, vou fazer uma pergunta retórica sobre outra estrutura que possui propriedade semelhante aos elementos DOM que o jQuery manipula, que é o bom e antigo iterador. A questão é:

Quantas operações você precisa em um iterador simples?

A pergunta pode ser respondida facilmente, analisando qualquer API Iterator em um determinado idioma. Você precisa de 3 métodos:

  1. Obter o valor atual
  2. Mova o iterador para o próximo elemento
  3. Verifique se o Iterator possui mais elementos

É tudo o que você precisa. Se você puder executar essas 3 operações, poderá percorrer qualquer sequência de elementos.

Mas isso não é apenas o que você geralmente quer fazer com uma sequência de elementos, é? Você geralmente tem uma meta de nível muito mais alto a alcançar. Você pode querer fazer algo com todos os elementos, filtrá-los de acordo com alguma condição ou com um de vários outros métodos. Consulte a interface IEnumerable na biblioteca LINQ no .NET para obter mais exemplos.

Você vê quantos existem? E isso é apenas um subconjunto de todos os métodos que eles poderiam ter colocado na interface IEnumerable, porque você geralmente os combina para atingir objetivos ainda mais altos.

Mas aqui está a reviravolta. Esses métodos não estão na interface IEnumerable. São métodos simples de utilidade que, na verdade, usam um IEnumerable como entrada e fazem algo com ele. Portanto, enquanto na linguagem C # parece haver vários métodos na interface IEnumerable, IEnumerable não é um objeto divino.


Agora, de volta ao jQuery. Vamos fazer essa pergunta novamente, desta vez com um elemento DOM.

Quantas operações você precisa em um elemento DOM?

Novamente, a resposta é bem direta. Todos os métodos que você precisa são métodos para ler / modificar os atributos e os elementos filhos. É sobre isso. Tudo o resto é apenas uma combinação dessas operações básicas.

Mas quanto de nível superior você gostaria de fazer com os elementos DOM? Bem, o mesmo que um iterador: um bilhão de coisas diferentes. E é aí que entra o jQuery. O jQuery, em essência, fornece duas coisas:

  1. Uma coleção muito boa de métodos de utilitários que você pode querer chamar em um elemento DOM;
  2. Açúcar sintático para que usá-lo seja uma experiência muito melhor do que usar a API DOM padrão.

Se você escolher o formato açucarado, perceberá que o jQuery poderia facilmente ter sido escrito como várias funções que selecionam / modificam elementos DOM. Por exemplo:

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

... poderia ter sido escrito como:

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

Semântica é exatamente a mesma coisa. No entanto, o primeiro formulário tem a grande vantagem de que a ordem da esquerda para a direita das instruções segue a ordem em que as operações serão executadas. A segunda começa no meio, o que dificulta a leitura do código se você combinar muitas operações.

Então o que tudo isso significa? Esse jQuery (como LINQ) não é o antipadrão de objetos de Deus. Em vez disso, é um caso de um padrão muito respeitado chamado Decorator .


Mas, novamente, o que dizer da substituição de $fazer todas essas coisas diferentes? Bem, isso é apenas açúcar sintático, na verdade. Todas as chamadas $e seus derivados $.getJson()são coisas completamente diferentes que compartilham nomes semelhantes, para que você possa sentir imediatamente que eles pertencem ao jQuery. $realiza uma e apenas uma tarefa: permite que você tenha um ponto de partida facilmente reconhecível para usar o jQuery. E todos esses métodos que você pode chamar em um objeto jQuery não são um sintoma de um objeto deus. Eles são simplesmente funções utilitárias diferentes, cada uma executando uma única coisa em um elemento DOM passado como argumento. A notação .dot está aqui apenas porque facilita a escrita do código.


6
Um objeto decorado que possui centenas de métodos adicionados é um ótimo exemplo de um objeto Deus. O fato de decorar um objeto bem projetado com um conjunto razoável de métodos é a prova de que você precisa.
Ross Patterson

2
@RossPatterson Você está discordando? Se você é, eu encorajo você a postar sua própria resposta. Acho que o Laurent's é bom, mas ainda estou indeciso.
22612 Nicole

@ RossPatterson Presumo que seu comentário signifique que este é um caso em que é um objeto de Deus, mas é uma coisa boa. Estou enganado?
Laurent Bourgault-Roy

3
@ LaurentBourgault-Roy Eu discordo de sua conclusão - acredito que o jQuery é realmente um exemplo de Deus. Mas você fez um trabalho tão bom que não posso suportar rebater sua resposta. Obrigado por uma ótima explicação de sua posição.
Ross Patterson

11
Eu discordo completamente da conculsão. Existem muitas funções utilitárias no jQuery que não estão relacionadas à manipulação do DOM.
Boris Yankov

19

Não - a $função é sobrecarregada apenas por três tarefas . Tudo o resto são funções filho que apenas o utilizam como um espaço para nome .


17
Mas ele retorna um objeto " jQuery " que contém grande parte da API do jQuery - e, eu acho, seria o objeto "Deus" ao qual o OP está se referindo.
22412 Nicole

2
@ NickC, mesmo sendo um objeto, nesse caso, acho que é realmente usado como um espaço para nome, assim como Math. Como não há espaço para nome incorporado no JS, eles apenas usam um objeto para isso. Embora eu não tenha certeza de qual seria a alternativa? Coloque todas as funções e propriedades no espaço para nome global?
laurent

5

A principal função jQuery (por exemplo $("div a")) é essencialmente um método de fábrica que retorna uma instância do tipo jQuery que representa uma coleção de elementos DOM.

Essas instâncias do tipo jQuery têm um grande número de métodos de manipulação de DOM disponíveis, que operam nos elementos DOM representados pela instância. Embora isso possa ser considerado uma classe que é muito grande, ele realmente não se encaixa no padrão de Objeto de Deus.

Por fim, como menciona Michael Borgwardt, também há um grande número de funções utilitárias que usam $ como um espaço para nome e são apenas tangencialmente relacionadas aos objetos jQuery da coleção DOM.


11
Por que não se encaixa no padrão de Objeto de Deus?
Benjamin Hodgson

4

$ não é um objeto, é um espaço para nome.

Você chamaria java.lang de um objeto god por causa das muitas classes que ele contém? É uma sintaxe absolutamente válida para chamar java.lang.String.format(...), muito semelhante em forma a chamar qualquer coisa no jQuery.

Um objeto, para ser um objeto divino, deve ser um objeto apropriado em primeiro lugar - contendo dados e inteligência para agir sobre os dados. O jQuery contém apenas os métodos.

Outra maneira de ver: uma boa medida de quanto de um objeto deus é um objeto é a coesão - coesão mais baixa significa mais um objeto de deus. A coesão diz que grande parte dos dados é usada por quantos dos métodos. Como não há dados no jQuery, você faz as contas - todos os métodos usam todos os dados; portanto, o jQuery é altamente coeso, não sendo, portanto, um objeto divino.


3

Benjamin me pediu para esclarecer minha posição, por isso editei meu post anterior e acrescentei mais pensamentos.

Bob Martin é o autor de um grande livro intitulado como Código Limpo. Nesse livro, há um capítulo (Capítulo 6.) chamado Objetos e estruturas de dados, que ele discute as diferenças mais importantes entre objetos e estruturas de dados e afirmações que precisamos escolher entre eles, porque misturá-los é uma péssima idéia.

Essa confusão às vezes leva a estruturas híbridas infelizes que são meio objeto e meia estrutura de dados. Eles têm funções que realizam coisas significativas e também têm variáveis ​​públicas ou acessadores e mutadores públicos que, para todos os efeitos, tornam públicas as variáveis ​​privadas, tentando outras funções externas a usar essas variáveis ​​da maneira que um programa procedural usaria. estrutura de dados.4 Esses híbridos dificultam a adição de novas funções, mas também dificultam a adição de novas estruturas de dados. Eles são os piores dos dois mundos. Evite criá-los. Eles são indicativos de um design confuso cujos autores não têm certeza - ou pior, ignoram - se precisam de proteção contra funções ou tipos.

Eu acho que o DOM é um exemplo desses híbridos de objetos e estrutura de dados. Por exemplo, por DOM, escrevemos códigos como este:

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

O DOM deve ser claramente uma estrutura de dados em vez de um híbrido.

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

A estrutura do jQuery é uma série de procedimentos, que podem selecionar e modificar uma coleção de nós DOM e fazer muitas outras coisas. Como Laurent apontou em seu post, o jQuery é algo assim:

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

Os desenvolvedores do jQuery mesclaram todos esses procedimentos em uma única classe, responsável por todos os recursos listados acima. Por isso, viola claramente o Princípio da Responsabilidade Única e, portanto, é um objeto divino. A única coisa que não quebra nada, porque é uma única classe independente que funciona em uma única estrutura de dados (a coleção de nós DOM). Se adicionarmos subclasses jQuery ou outra estrutura de dados, o projeto entrará em colapso muito rápido. Portanto, acho que não podemos falar sobre oo pelo jQuery, é mais processual do que ooo, apesar de definir uma classe.

O que Laurent afirma é um absurdo completo:

Então o que tudo isso significa? Esse jQuery (como LINQ) não é o antipadrão de objetos de Deus. Em vez disso, é um caso de um padrão muito respeitado chamado Decorator.

O padrão Decorator visa adicionar novas funcionalidades mantendo a interface e não modificando as classes existentes. Por exemplo:

Você pode definir 2 classes que implementam a mesma interface, mas com uma implementação completamente diferente:

/**
 * @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 você tiver métodos que usam apenas a interface comum, poderá definir um ou mais decoradores em vez de copiar e colar o mesmo código entre A e B. Você pode usar esses decoradores mesmo em uma estrutura aninhada.

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

Portanto, você pode substituir as instâncias originais pelas instâncias do decorador no código de nível de abstração mais alto.

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

A conclusão de que o jQuery não é um decorador de nada, porque não implementa a mesma interface que Array, NodeList ou qualquer outro objeto DOM. Ele implementa sua própria interface. Os módulos também não são utilizados como decoradores, eles simplesmente substituem o protótipo original. Portanto, o padrão Decorator não é usado em toda a lib do jQuery. A classe jQuery é simplesmente um grande adaptador que nos permite usar a mesma API por muitos navegadores diferentes. De uma perspectiva completa, é uma bagunça completa, mas isso realmente não importa, funciona bem e nós a usamos.


Você parece estar argumentando que o uso de métodos infix é a chave diferente entre o código OO e o código processual. Eu não acho que isso seja verdade. Você poderia esclarecer sua posição?
Benjamin Hodgson

@BenjaminHodgson O que você quer dizer com "método infix"?
Inf3rno

@BenjaminHodgson eu esclareci. Espero que esteja claro agora.
Inf3rno
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.