Apakah jQuery contoh "objek dewa" antipattern?

56

Saya ingin bertanya - Saya perlahan belajar jQuery.

Apa yang saya lihat adalah contoh tepat dari anti-pola Objek Allah . Pada dasarnya, semuanya berjalan ke $fungsi, apa pun itu.

Apakah saya benar dan apakah jQuery benar-benar contoh dari pola-anti ini?

Karel Bílek
sumber
13
Anda mungkin mengajukan pertanyaan yang salah. Pertanyaan yang tepat adalah, "Bagaimana bisa jQuery diimplementasikan secara memuaskan dalam bahasa Javascript dengan cara yang tidak memerlukan $fungsi atau jQueryobjek.
Robert Harvey
1
Saya selalu ingin menanyakan pertanyaan ini benar-benar :).
Siapa saja
@RobertHarvey: sayangnya, saya tidak cukup tahu tentang JavaScript untuk menjawab ini.
Karel Bílek
Ya (12 karakter lagi ...)
Andrea
Ini lebih seperti perpustakaan korektif
Andrew

Jawaban:

54

Untuk menjawab pertanyaan itu, saya akan mengajukan pertanyaan retoris tentang struktur lain yang memiliki properti serupa dengan elemen DOM yang dimanipulasi jQuery, yaitu iterator tua yang baik. Pertanyaannya adalah:

Berapa banyak operasi yang Anda butuhkan pada iterator sederhana?

Pertanyaannya dapat dijawab dengan mudah dengan melihat API Iterator dalam bahasa tertentu. Anda memerlukan 3 metode:

  1. Dapatkan nilai saat ini
  2. Pindahkan iterator ke elemen berikutnya
  3. Periksa apakah Iterator memiliki lebih banyak elemen

Itu yang kamu butuhkan. Jika Anda dapat melakukan 3 operasi tersebut, Anda dapat melewati urutan elemen apa pun.

Tapi bukan hanya itu yang biasanya ingin Anda lakukan dengan urutan elemen, bukan? Anda biasanya memiliki tujuan tingkat yang jauh lebih tinggi untuk dicapai. Anda mungkin ingin melakukan sesuatu dengan setiap elemen, Anda mungkin ingin memfilternya berdasarkan kondisi tertentu, atau salah satu dari beberapa metode lain. Lihat antarmuka IEnumerable di pustaka LINQ di .NET untuk contoh lebih lanjut.

Apakah Anda melihat ada berapa? Dan itu hanya sebagian dari semua metode yang dapat mereka gunakan pada antarmuka IEnumerable, karena Anda biasanya menggabungkannya untuk mencapai tujuan yang lebih tinggi.

Tapi di sini ada twist. Metode-metode itu tidak ada pada antarmuka IEnumerable. Mereka adalah metode utilitas sederhana yang benar-benar mengambil IEnumerable sebagai input dan melakukan sesuatu dengannya. Jadi sementara dalam bahasa C # rasanya seperti ada metode bajillion pada antarmuka IEnumerable, IEnumerable bukan objek dewa.


Sekarang kembali ke jQuery. Mari kita ajukan pertanyaan itu lagi, kali ini dengan elemen DOM.

Berapa banyak operasi yang Anda butuhkan pada elemen DOM?

Sekali lagi jawabannya cukup mudah. Semua metode yang Anda butuhkan adalah metode untuk membaca / memodifikasi atribut dan elemen turunan. Itu saja. Yang lainnya hanyalah kombinasi dari operasi dasar itu.

Tetapi seberapa banyak hal tingkat yang lebih tinggi yang ingin Anda lakukan dengan elemen DOM? Yah, sama seperti Iterator: satu bajillion hal yang berbeda. Dan di situlah jQuery masuk. JQuery, pada dasarnya menyediakan dua hal:

  1. Kumpulan metode utilitas yang sangat bagus yang mungkin ingin Anda panggil pada elemen DOM, dan;
  2. Gula sintaksis sehingga menggunakannya adalah pengalaman yang jauh lebih baik daripada menggunakan API DOM standar.

Jika Anda mengeluarkan formulir bergula, Anda menyadari bahwa jQuery dapat dengan mudah ditulis sebagai sekelompok fungsi yang memilih / memodifikasi elemen DOM. Sebagai contoh:

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

... bisa ditulis sebagai:

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

Semantik itu hal yang persis sama. Namun bentuk pertama memiliki keuntungan besar bahwa urutan kiri-ke-kanan dari pernyataan mengikuti urutan operasi akan dieksekusi. Mulai kedua di tengah, yang membuat kode sangat sulit dibaca jika Anda menggabungkan banyak operasi bersama.

Jadi, apa artinya semua itu? JQuery itu (seperti LINQ) bukan anti-pola objek Allah. Ini bukan kasus pola yang sangat dihormati yang disebut Dekorator .


Tetapi sekali lagi, bagaimana dengan menimpa $untuk melakukan semua hal yang berbeda itu? Ya, itu benar-benar gula sintaksis. Semua panggilan $dan turunannya suka $.getJson()adalah hal-hal yang benar-benar berbeda yang kebetulan berbagi nama yang sama sehingga Anda dapat segera merasa bahwa mereka milik jQuery. $melakukan satu dan hanya satu tugas: biarkan Anda memiliki titik awal yang mudah dikenali untuk menggunakan jQuery. Dan semua metode yang dapat Anda panggil pada objek jQuery bukan merupakan gejala objek dewa. Mereka hanya fungsi utilitas yang berbeda yang masing-masing melakukan satu-satunya hal pada elemen DOM yang dilewatkan sebagai argumen. Notasi .dot hanya ada di sini karena membuat penulisan kode lebih mudah.

Laurent Bourgault-Roy
sumber
6
Objek yang dihiasi yang memiliki ratusan metode tambahan adalah contoh yang bagus dari Obyek Dewa. Fakta bahwa ia menghiasi objek yang dirancang dengan baik dengan serangkaian metode yang masuk akal adalah bukti yang Anda butuhkan.
Ross Patterson
2
@RossPatterson Apakah Anda tidak setuju? Jika ya, saya mendorong Anda untuk mengirim jawaban Anda sendiri. Saya pikir Laurent bagus, tapi saya masih ragu-ragu.
Nicole
@ RossPatterson Saya menganggap komentar Anda berarti bahwa ini adalah kasus di mana itu adalah Obyek Tuhan tetapi itu adalah hal yang baik. Apakah saya salah?
Laurent Bourgault-Roy
3
@ LaurentBourgault-Roy Saya tidak setuju dengan kesimpulan Anda - Saya percaya jQuery memang contoh Obyek Tuhan. Tetapi Anda melakukan pekerjaan yang sangat bagus sehingga saya tidak sanggup untuk membatalkan jawaban Anda. Terima kasih atas penjelasan yang bagus tentang posisi Anda.
Ross Patterson
1
Saya sepenuhnya tidak setuju dengan konsul. Ada banyak fungsi utilitas pada jQuery yang tidak terkait dengan manipulasi DOM.
Boris Yankov
19

Tidak - $fungsi ini sebenarnya hanya kelebihan beban untuk tiga tugas . Yang lainnya adalah fungsi anak yang hanya menggunakannya sebagai namespace .

Michael Borgwardt
sumber
17
Tapi ia mengembalikan " jQuery " objek yang berisi banyak jQuery API - dan, saya pikir, akan menjadi "Allah" objek OP mengacu pada.
Nicole
2
@NickC, meskipun itu sebuah objek, dalam hal ini saya pikir itu memang digunakan sebagai namespace, sama seperti Math. Karena tidak ada built-in namespace di JS, mereka hanya menggunakan objek untuk itu. Meskipun saya tidak yakin apa alternatifnya? Taruh semua fungsi dan properti di namespace global?
laurent
5

Fungsi jQuery utama (misalnya $("div a")) pada dasarnya adalah metode pabrik yang mengembalikan instance dari tipe jQuery yang mewakili kumpulan elemen DOM.

Ini contoh dari jenis jQuery memiliki sejumlah besar metode manipulasi DOM tersedia yang beroperasi pada elemen DOM diwakili oleh instance. Meskipun ini bisa dianggap sebagai kelas yang tumbuh terlalu besar, itu tidak benar-benar cocok dengan pola Obyek Dewa.

Akhirnya, seperti yang disebutkan Michael Borgwardt, ada juga sejumlah besar fungsi utilitas yang menggunakan $ sebagai namespace dan hanya secara tangensial terkait dengan objek koleksi jOM DOM.

grahampark
sumber
1
Mengapa itu tidak cocok dengan pola Obyek Allah?
Benjamin Hodgson
4

$ bukan objek, ini namespace.

Apakah Anda memanggil java.lang objek dewa karena banyak kelas yang dikandungnya? Ini sintaks yang benar-benar valid untuk dipanggil java.lang.String.format(...), sangat mirip dalam bentuk memanggil apa pun di jQuery.

Suatu objek, untuk menjadi objek dewa, harus menjadi objek yang tepat di tempat pertama - berisi data dan kecerdasan untuk bertindak berdasarkan data. jQuery hanya berisi metode.

Cara lain untuk melihatnya: ukuran yang baik untuk berapa banyak objek dewa objek adalah kohesi - kohesi yang lebih rendah berarti lebih banyak objek dewa. Kohesi mengatakan karena banyak data yang digunakan oleh berapa banyak metode. Karena tidak ada data di jQuery, Anda melakukan matematika - semua metode menggunakan semua data, jadi jQuery sangat kohesif, sehingga tidak menjadi objek dewa.

pengguna625488
sumber
3

Benjamin meminta saya untuk mengklarifikasi posisi saya, jadi saya mengedit posting saya sebelumnya dan menambahkan pemikiran lebih lanjut.

Bob Martin adalah penulis buku hebat berjudul Clean Code. Dalam buku itu ada bab (Bab 6.) yang disebut Objects and struktur Data, yang ia membahas perbedaan paling penting antara objek dan struktur data dan klaim bahwa kita harus memilih di antara mereka, karena mencampurkannya adalah ide yang sangat buruk.

Kebingungan ini kadang-kadang menyebabkan struktur hibrida yang kurang menguntungkan yang setengah objek dan setengah struktur data. Mereka memiliki fungsi yang melakukan hal-hal signifikan, dan mereka juga memiliki variabel publik atau pengakses publik dan mutator yang, untuk semua maksud dan tujuan, membuat variabel pribadi menjadi publik, menggoda fungsi eksternal lainnya untuk menggunakan variabel-variabel itu seperti cara program prosedural menggunakan suatu struktur data.4 Hibrida seperti itu membuat sulit untuk menambahkan fungsi baru tetapi juga membuatnya sulit untuk menambah struktur data baru. Mereka adalah yang terburuk dari kedua dunia. Hindari membuatnya. Mereka adalah indikasi dari desain kacau yang penulis tidak yakin - atau lebih buruk, tidak tahu - apakah mereka membutuhkan perlindungan dari fungsi atau jenis.

Saya pikir DOM adalah contoh dari hibrida objek dan struktur data ini. Sebagai contoh oleh DOM kami menulis kode seperti ini:

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 harus jelas merupakan struktur data, bukan hibrid.

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

Kerangka kerja jQuery adalah sekelompok prosedur, yang dapat memilih dan memodifikasi kumpulan node DOM dan melakukan banyak hal lainnya. Seperti yang ditunjukkan Laurent dalam posnya, jQuery adalah sesuatu seperti ini di bawah tenda:

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

Pengembang jQuery menggabungkan semua prosedur ini ke dalam satu kelas, yang bertanggung jawab untuk semua fitur yang tercantum di atas. Jadi jelas melanggar Prinsip Tanggung Jawab Tunggal dan jadi itu adalah objek dewa. Satu-satunya hal karena tidak merusak apa pun, karena itu adalah kelas mandiri tunggal yang bekerja pada struktur data tunggal (kumpulan node DOM). Jika kita akan menambahkan subclass jQuery atau struktur data lain proyek akan runtuh sangat cepat. Jadi saya tidak berpikir kita bisa berbicara tentang oo dengan jQuery itu lebih prosedural daripada oo meskipun fakta bahwa mendefinisikan kelas.

Apa yang diklaim Laurent adalah omong kosong:

Jadi apa artinya semua itu? JQuery itu (seperti LINQ) bukan objek anti-pola Dewa. Ini bukan kasus pola yang sangat dihormati yang disebut Dekorator.

Pola Dekorator adalah tentang menambahkan fungsionalitas baru dengan menjaga antarmuka dan tidak mengubah kelas yang ada. Sebagai contoh:

Anda dapat mendefinisikan 2 kelas yang mengimplementasikan antarmuka yang sama, tetapi dengan implementasi yang sama sekali berbeda:

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

Jika Anda memiliki metode yang hanya menggunakan antarmuka umum, maka Anda dapat menentukan satu atau lebih Dekorator alih-alih menyalin kode yang sama antara A dan B. Anda dapat menggunakan dekorator ini bahkan dalam struktur bersarang.

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

Jadi Anda dapat mengganti instance asli dengan instance dekorator dalam kode level abstraksi yang lebih tinggi.

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

Kesimpulannya bahwa jQuery bukan Dekorator apa pun, karena ia tidak mengimplementasikan antarmuka yang sama dengan Array, NodeList atau objek DOM lainnya. Ini mengimplementasikan antarmuka itu sendiri. Modul tidak digunakan sebagai Dekorator juga, mereka hanya menimpa prototipe asli. Jadi pola dekorator tidak digunakan di seluruh jQuery lib. Kelas jQuery hanyalah sebuah adaptor besar yang memungkinkan kami menggunakan API yang sama oleh banyak browser yang berbeda. Dari perspektif oo memang berantakan, tetapi itu tidak terlalu penting, ia bekerja dengan baik, dan kami menggunakannya.

inf3rno
sumber
Anda tampaknya berpendapat bahwa penggunaan metode infiks adalah kunci yang berbeda antara kode OO dan kode prosedural. Saya pikir ini tidak benar. Bisakah Anda memperjelas posisi Anda?
Benjamin Hodgson
@BenjaminHodgson Apa maksud Anda dengan "metode infix"?
inf3rno
@BenjaminHodgson saya klarifikasi. Saya harap itu jelas sekarang.
inf3rno