jQueryは「神オブジェクト」アンチパターンの例ですか?


56

聞きたいのですが、jQueryをゆっくりと学んでいます。

私が見るものは、神オブジェクトのアンチパターンの正確な例です。基本的に、それが何であれ、すべてが関数に送られます。$

私は正しいですか、jQueryは本当にこのアンチパターンの例ですか?


13
おそらく間違った質問をしているのでしょう。正しい質問は、「$関数やjQueryオブジェクトを必要としない方法で、jQueryをJavascript言語でどのように満足のいくように実装できるか。」
Robert Harvey

1
私はいつもこの質問を本当にしたかったのです:)。
AnyOne

@RobertHarvey:悲しいことに、私はJavaScriptについてこれに答えるのに十分な知識がありません。
カレルビレック

はい(残り12文字...)
アンドレア

これは、より多くの是正ライブラリーのようなものだ
アンドリュー

回答:


54

その質問に答えるために、jQueryが操作するDOM要素に似たプロパティを持つ別の構造、つまり古き良きイテレータについて、修辞的な質問をします。質問は:

単純なイテレーターでは何回の操作が必要ですか?

質問は、特定の言語のIterator APIを調べることで簡単に回答できます。次の3つの方法が必要です。

  1. 現在の値を取得する
  2. イテレータを次の要素に移動します
  3. イテレータにさらに要素があるかどうかを確認します

必要なのはそれだけです。これらの3つの操作を実行できる場合、要素の任意のシーケンスをたどることができます。

しかし、それは通常、要素のシーケンスでやりたいことだけではありませんか?通常、達成すべき目標ははるかに高いレベルです。すべての要素で何かをしたい場合、何らかの条件、または他のいくつかの方法のいずれかに従ってそれらをフィルタリングしたい場合があります。その他の例については、.NETのLINQライブラリのIEnumerableインターフェイスを参照してください。

いくつあるかわかりますか?そして、それはIEnumerableインターフェイスに置くことができるすべてのメソッドのサブセットに過ぎません。通常、それらを組み合わせてさらに高い目標を達成するからです。

しかし、ここにひねりがあります。これらのメソッドは、IEnumerableインターフェイスにはありません。これらは、入力としてIEnumerableを実際に受け取り、それで何かを行う単純なユーティリティメソッドです。そのため、C#言語では、IEnumerableインターフェイスに無数のメソッドがあるように感じますが、IEnumerableは神のオブジェクトではありません。


それでは、jQueryに戻りましょう。今度はDOM要素を使用して、もう一度質問してみましょう。

DOM要素で何回の操作が必要ですか?

繰り返しますが、答えは非常に簡単です。必要なメソッドはすべて、属性と子要素を読み取り/変更するメソッドです。それについてです。他のすべては、これらの基本的な操作の組み合わせにすぎません。

しかし、DOM要素でどの程度高いレベルのものをやりたいですか?まあ、イテレーターと同じ:膨大な数の異なるもの。それがjQueryの出番です。jQueryは、本質的に2つのことを提供します。

  1. DOM要素で呼び出すことができるユーティリティメソッドの非常に素晴らしいコレクション。
  2. 構文糖衣を使用すると、標準のDOM APIを使用するよりもはるかに優れたエクスペリエンスになります。

砂糖のフォームを取り出すと、jQueryはDOM要素を選択/変更する一連の関数として簡単に記述できたことがわかります。例えば:

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

...として書かれている可能性があります:

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

意味的にはまったく同じことです。ただし、最初の形式には、ステートメントの左から右の順序が操作の実行順序に従うという大きな利点があります。中間の2番目の開始。多くの操作を組み合わせると、コードを非常に読みにくくなります。

それで、それはすべてどういう意味ですか?そのjQuery(LINQなど)は、Godオブジェクトのアンチパターンではありません。代わりに、Decoratorと呼ばれる非常に尊敬されるパターンのケースです。


しかし、再び、$これらすべての異なることを行うためのオーバーライドはどうですか?まあ、それは本当に構文糖です。呼び出し$とその派生物はすべて、$.getJson()まったく同じものを共有しているため、jQueryに属しているとすぐに感じることができます。$ただ1つのタスクを実行します。jQueryを使用するための簡単に認識できる開始点があります。また、jQueryオブジェクトで呼び出すことができるこれらのメソッドはすべて、godオブジェクトの症状ではありません。それらは、引数として渡されたDOM要素に対してそれぞれ唯一のことを実行する、単に異なるユーティリティ関数です。.dot表記は、コードの記述を容易にするため、ここにのみあります。


6
数百のメソッドが追加された装飾オブジェクトは、神オブジェクトの優れた例です。適切に設計されたオブジェクトを合理的な一連のメソッドで装飾するという事実は、必要な証拠です。
ロスパターソン

2
@RossPattersonあなたは同意しませんか?もしそうなら、あなた自身の答えを投稿することをお勧めします。ローランのものは良いと思いますが、まだ未定です。
ニコール

@RossPatterson私はあなたのコメントは、それがこの場合という意味では想定している神のオブジェクトが、それは良いことです。私は間違っていますか?
ローランブルゴーロイ

3
@ LaurentBourgault-Royあなたの結論に同意しません-jQueryは確かにGod Objectの例だと思います。しかし、あなたは非常に素晴らしい仕事をしてくれたので、あなたの答えを支持することはできません。あなたの立場について素晴らしい説明をありがとう。
ロスパターソン

1
私はその陰謀に完全に反対します。jQueryには、DOM操作に関連しないユーティリティ関数が多数あります。
ボリスヤンコフ

19

いいえ- $関数は実際には3つのタスクに対してのみオーバーロードされます。それ以外はすべて、名前空間として使用するだけの子関数です。


17
ただし、jQuery APIの多くを含む「jQueryオブジェクトを返します。OPが参照している「God」オブジェクトになると思います。
ニコール

2
@NickCは、それがオブジェクトであるにもかかわらず、その場合、まさにのように、名前空間として実際に使用されていると思いますMath。JSには組み込みの名前空間がないため、そのためにオブジェクトを使用するだけです。私は代替案が何であるかわかりませんが?すべての関数とプロパティをグローバル名前空間に入れますか?
ローレント

5

メインのjQuery関数($("div a"))は、本質的には、DOM要素のコレクションを表すjQuery型のインスタンスを返すファクトリメソッドです。

これらのjQueryタイプのインスタンスには、インスタンスで表されるDOM要素を操作する多数のDOM操作メソッドがあります。これは大きすぎるクラスと考えられるかもしれませんが、実際にはゴッドオブジェクトパターンに適合していません。

最後に、Michael Borgwardtが言及しているように、$を名前空間として使用し、DOMコレクションjQueryオブジェクトに正接的にのみ関連するユーティリティ関数も多数あります。


1
なぜGod Objectパターンに適合しないのですか?
ベンジャミンホジソン

4

$ オブジェクトではなく、名前空間です。

java.langには多くのクラスがあるため、godオブジェクトと呼びますか?を呼び出すのは絶対に有効な構文でありjava.lang.String.format(...)、jQueryで何かを呼び出すのと非常によく似ています。

オブジェクトは、神のオブジェクトであるために、そもそも適切なオブジェクトでなければなりません-データとデータに作用する知性の両方を含んでいます。jQueryにはメソッドのみが含まれています。

別の見方をすると、オブジェクトがどれだけの神のオブジェクトであるかを示す適切な尺度は、結束です。結束が低いと、神のオブジェクトが多くなります。Cohesionは、多くのデータがいくつのメソッドで使用されているかを示しています。jQueryにはデータがないため、数学を実行します。すべてのメソッドがすべてのデータを使用するため、jQueryは非常にまとまりがあり、神のオブジェクトではありません。


3

ベンジャミンは自分の立場を明確にするように頼んだので、以前の投稿を編集してさらに考えを追加しました。

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サブクラスまたは別のデータ構造を追加すると、プロジェクトは非常に速く崩壊します。したがって、クラスを定義しているという事実にもかかわらず、ooよりも手続き型であるjQueryによってooについて話すことはできないと思います。

ローランが主張することは完全なナンセンスです:

それで、それはすべてどういう意味ですか?そのjQuery(LINQなど)は、Godオブジェクトのアンチパターンではありません。代わりに、デコレータと呼ばれる非常に尊敬されるパターンの場合です。

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の間で同じコードをコピーアンドペーストする代わりに、1つ以上のデコレーターを定義できます。これらのデコレーターはネスト構造でも使用できます。

/**
 * @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オブジェクトと同じインターフェースを実装しないため、jQueryは何のデコレーターでもないという結論。独自のインターフェイスを実装します。モジュールはデコレーターとしても使用されず、単に元のプロトタイプをオーバーライドします。したがって、デコレータパターンはjQueryライブラリ全体では使用されません。jQueryクラスは単純に巨大なアダプターであり、多くの異なるブラウザーで同じAPIを使用できます。オブジェクト指向の観点からは、それは完全な混乱ですが、それは実際には問題ではなく、うまく機能し、それを使用します。


あなたは、中置法の使用がオブジェクト指向コードと手続き型コードの間で異なる重要な鍵であると主張しているようです。私はこれが真実だとは思わない。あなたの立場を明確にしていただけますか?
ベンジャミンホジソン

@BenjaminHodgson「中置法」とはどういう意味ですか?
inf3rno

@BenjaminHodgson私は明確にした。私はそれが今明確であることを望みます。
inf3rno
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.