Sue sedang merancang perpustakaan JavaScript Magician.js
,. Kunci pasnya adalah fungsi yang menarik Rabbit
keluar dari argumen yang disahkan.
Dia tahu penggunanya mungkin ingin menarik kelinci keluar dari String
, a Number
, a Function
, bahkan mungkin a HTMLElement
. Dengan pemikiran itu, ia dapat merancang API-nya seperti:
Antarmuka yang ketat
Magician.pullRabbitOutOfString = function(str) //...
Magician.pullRabbitOutOfHTMLElement = function(htmlEl) //...
Setiap fungsi dalam contoh di atas akan tahu bagaimana menangani argumen dari jenis yang ditentukan dalam nama fungsi / nama parameter.
Atau, dia bisa mendesainnya seperti ini:
Antarmuka "ad hoc"
Magician.pullRabbit = function(anything) //...
pullRabbit
harus menjelaskan variasi dari berbagai tipe yang diharapkan dari anything
argumen tersebut, serta (tentu saja) tipe yang tidak terduga:
Magician.pullRabbit = function(anything) {
if (anything === undefined) {
return new Rabbit(); // out of thin air
} else if (isString(anything)) {
// more
} else if (isNumber(anything)) {
// more
}
// etc.
};
Yang pertama (ketat) tampaknya lebih eksplisit, mungkin lebih aman, dan mungkin lebih berkinerja - karena ada sedikit atau tidak ada biaya tambahan untuk pengecekan tipe atau konversi tipe. Tetapi yang terakhir (ad hoc) merasa lebih mudah melihatnya dari luar, karena "hanya bekerja" dengan argumen apa pun yang menurut konsumen nyaman untuk diteruskan.
Untuk jawaban atas pertanyaan ini , saya ingin melihat pro dan kontra khusus untuk pendekatan mana pun (atau pendekatan yang berbeda secara keseluruhan, jika tidak ada yang ideal), bahwa Sue harus tahu pendekatan mana yang harus diambil ketika merancang API perpustakaannya.
sumber
Jawaban:
Beberapa pro dan kontra
Pro untuk polimorfik:
Pro untuk ad-hoc:
Cat
instance? Apakah itu hanya berfungsi? jika tidak, apa perilakunya? Jika saya tidak membatasi jenis di sini, saya harus melakukannya dalam dokumentasi, atau dalam tes yang mungkin membuat kontrak lebih buruk.pullRabbitOutOfString
versi ini kemungkinan akan jauh lebih cepat di mesin seperti V8. Lihat video ini untuk informasi lebih lanjut. Sunting: Saya menulis perf, ternyata dalam prakteknya, ini tidak selalu terjadi .Beberapa solusi alternatif:
Menurut pendapat saya, desain semacam ini tidak terlalu 'Java-Scripty' untuk memulai. JavaScript adalah bahasa yang berbeda dengan idiom yang berbeda dari bahasa seperti C #, Java atau Python. Idiom-idiom ini berasal dari pengembang bertahun-tahun yang mencoba memahami bagian bahasa yang lemah dan kuat, yang saya lakukan adalah mencoba untuk tetap menggunakan idiom-idiom ini.
Ada dua solusi bagus yang dapat saya pikirkan:
Solusi 1: Mengangkat Objek
Salah satu solusi umum untuk masalah ini, adalah 'meninggikan' benda dengan kemampuan kelinci ditarik keluar.
Artinya, memiliki fungsi yang mengambil beberapa jenis objek, dan menambahkan menarik topi untuk itu. Sesuatu seperti:
Saya dapat membuat
makePullable
fungsi seperti itu untuk objek lain, saya bisa membuatmakePullableString
, dll. Saya mendefinisikan konversi pada setiap jenis. Namun, setelah saya mengangkat objek saya, saya tidak memiliki tipe untuk menggunakannya secara umum. Antarmuka dalam JavaScript ditentukan oleh mengetik bebek, jika memilikipullOfHat
metode saya dapat menariknya dengan metode Magician.Kemudian Magician bisa melakukan:
Mengangkat objek, menggunakan semacam pola mixin sepertinya lebih banyak hal JS yang harus dilakukan. (Catatan ini bermasalah dengan tipe nilai dalam bahasa yang berupa string, angka, null, tidak terdefinisi dan boolean, tetapi semuanya bisa dilakukan dalam kotak)
Berikut adalah contoh dari apa yang terlihat seperti kode
Solusi 2: Pola Strategi
Ketika mendiskusikan pertanyaan ini di ruang obrolan JS di StackOverflow, teman saya phenomnomnominal menyarankan penggunaan pola Strategi .
Ini akan memungkinkan Anda untuk menambahkan kemampuan untuk menarik kelinci keluar dari berbagai objek pada saat dijalankan, dan akan membuat kode yang sangat JavaScript. Seorang pesulap dapat belajar cara menarik benda-benda dari berbagai jenis dari topi, dan itu menarik mereka berdasarkan pengetahuan itu.
Berikut ini tampilannya di CoffeeScript:
Anda dapat melihat kode JS yang setara di sini .
Dengan cara ini, Anda mendapat manfaat dari kedua dunia, tindakan cara menarik tidak terlalu erat dengan objek, atau Penyihir dan saya pikir ini membuat solusi yang sangat bagus.
Penggunaan akan menjadi seperti:
sumber
Masalahnya adalah Anda mencoba menerapkan jenis polimorfisme yang tidak ada dalam JavaScript - JavaScript hampir secara universal paling baik diperlakukan sebagai bahasa yang diketik bebek, meskipun itu mendukung beberapa jenis fakultas.
Untuk membuat API terbaik, jawabannya adalah Anda harus mengimplementasikan keduanya. Ini sedikit lebih banyak mengetik, tetapi akan menghemat banyak pekerjaan dalam jangka panjang untuk pengguna API Anda.
pullRabbit
seharusnya hanya menjadi metode arbiter yang memeriksa jenis dan memanggil fungsi yang tepat terkait dengan jenis objek tersebut (misalnyapullRabbitOutOfHtmlElement
).Dengan cara itu, sementara prototipe pengguna dapat menggunakan
pullRabbit
, tetapi jika mereka melihat perlambatan mereka dapat mengimplementasikan pengecekan tipe di ujung mereka (kemungkinan cara yang lebih cepat) danpullRabbitOutOfHtmlElement
langsung menelepon .sumber
Ini adalah JavaScript. Ketika Anda melakukannya dengan lebih baik, Anda akan menemukan sering ada jalan tengah yang membantu meniadakan dilema seperti ini. Juga, itu benar-benar tidak masalah apakah 'tipe' yang tidak didukung ditangkap oleh sesuatu atau rusak ketika seseorang mencoba menggunakannya karena tidak ada kompilasi vs run-time. Jika Anda menggunakannya salah itu rusak. Mencoba menyembunyikan bahwa itu rusak atau membuatnya bekerja setengah jalan ketika rusak tidak mengubah fakta bahwa ada sesuatu yang rusak.
Jadi miliki kue Anda dan makanlah juga dan belajarlah untuk menghindari jenis kebingungan dan kerusakan yang tidak perlu dengan menjaga semuanya benar-benar jelas, seperti dalam nama baik dan dengan semua detail yang tepat di semua tempat yang tepat.
Pertama-tama, saya sangat mendorong kebiasaan membasmi bebek Anda secara berurutan sebelum Anda perlu memeriksa jenisnya. Hal yang paling ramping dan paling efisien (tetapi tidak selalu terbaik di mana konstruktor asli yang bersangkutan) yang harus dilakukan adalah untuk memukul prototipe terlebih dahulu sehingga metode Anda bahkan tidak perlu peduli tentang apa yang didukung jenis yang dimainkan.
Catatan: Ini secara luas dianggap sebagai bentuk buruk untuk melakukan ini pada Object karena semuanya mewarisi dari Object. Saya pribadi akan menghindari Fungsi juga. Beberapa orang mungkin merasa ragu menyentuh prototipe konstruktor asli mana pun yang mungkin bukan kebijakan yang buruk tetapi contohnya masih bisa berfungsi ketika bekerja dengan konstruktor objek Anda sendiri.
Saya tidak akan khawatir tentang pendekatan ini untuk metode penggunaan khusus seperti itu yang tidak mungkin akan menghancurkan sesuatu dari perpustakaan lain dalam aplikasi yang kurang rumit, tetapi itu adalah naluri yang baik untuk menghindari menyatakan sesuatu yang terlalu umum di seluruh metode asli dalam JavaScript jika Anda tidak harus kecuali Anda menormalkan metode yang lebih baru di browser yang kedaluwarsa.
Untungnya, Anda selalu dapat hanya memetakan jenis atau nama konstruktor ke metode (berhati-hatilah dengan IE <= 8 yang tidak memiliki <object> .constructor.name yang mengharuskan Anda menguraikannya dari hasil toString dari properti konstruktor). Anda masih berlaku memeriksa nama konstruktor (typeof adalah jenis tidak berguna di JS ketika membandingkan objek) tetapi setidaknya itu membaca jauh lebih bagus daripada pernyataan switch raksasa atau jika / rantai lain dalam setiap panggilan metode untuk apa yang bisa menjadi lebar berbagai benda.
Atau menggunakan pendekatan peta yang sama, jika Anda ingin akses ke komponen 'ini' dari berbagai jenis objek untuk menggunakannya seolah-olah mereka adalah metode tanpa menyentuh prototipe yang diwarisi:
Prinsip umum yang baik dalam IMO bahasa apa pun, adalah mencoba memilah detail percabangan seperti ini sebelum Anda mendapatkan kode yang benar-benar menarik pelatuknya. Dengan begitu mudah untuk melihat semua pemain yang terlibat di tingkat API atas untuk ikhtisar yang bagus, tetapi juga jauh lebih mudah untuk memilah-milah di mana rincian seseorang mungkin peduli akan ditemukan.
Catatan: ini semua belum teruji, karena saya berasumsi tidak ada yang benar-benar menggunakan RL untuk itu. Saya yakin ada kesalahan ketik / bug.
sumber
Ini (bagi saya) adalah pertanyaan yang menarik dan rumit untuk dijawab. Saya sebenarnya suka pertanyaan ini jadi saya akan melakukan yang terbaik untuk menjawab. Jika Anda melakukan penelitian apa pun ke dalam standar untuk pemrograman javascript, Anda akan menemukan banyak cara "benar" untuk melakukannya karena ada orang yang menggembar-gemborkan cara "benar" melakukannya.
Tetapi karena Anda sedang mencari pendapat tentang jalan mana yang lebih baik. Tidak ada gunanya.
Saya pribadi lebih suka pendekatan desain "adhoc". Berasal dari latar belakang c ++ / C #, ini lebih merupakan gaya pengembangan saya. Anda dapat membuat satu permintaan pullRabbit dan meminta satu jenis permintaan untuk memeriksa argumen yang masuk dan melakukan sesuatu. Ini berarti Anda tidak perlu khawatir tentang jenis argumen yang diajukan pada satu waktu. Jika Anda menggunakan pendekatan yang ketat, Anda masih perlu memeriksa jenis variabelnya, tetapi Anda akan melakukannya sebelum melakukan pemanggilan metode. Jadi pada akhirnya pertanyaannya adalah, apakah Anda ingin memeriksa jenisnya sebelum melakukan panggilan atau sesudahnya.
Saya harap ini membantu, jangan ragu untuk mengajukan lebih banyak pertanyaan sehubungan dengan jawaban ini, saya akan melakukan yang terbaik untuk memperjelas posisi saya.
sumber
Ketika Anda menulis, Magician.pullRabbitOutOfInt, itu mendokumentasikan apa yang Anda pikirkan ketika Anda menulis metode. Penelepon akan mengharapkan ini berfungsi jika melewati bilangan bulat apa pun. Ketika Anda menulis, Magician.pullRabbitOutOfAnything, penelepon tidak tahu harus berpikir apa dan harus menggali kode Anda dan bereksperimen. Ini mungkin bekerja untuk Int, tetapi apakah itu akan bekerja untuk yang lama? Float? A Double? Jika Anda menulis kode ini, seberapa jauh Anda bersedia melangkah? Argumen macam apa yang ingin Anda dukung?
Ambiguitas membutuhkan waktu untuk dipahami. Saya bahkan tidak yakin bahwa lebih cepat menulis:
Vs:
OK, jadi saya menambahkan pengecualian pada kode Anda (yang saya sangat rekomendasikan) untuk memberi tahu penelepon bahwa Anda tidak pernah membayangkan mereka akan memberi Anda apa yang mereka lakukan. Tetapi menulis metode khusus (saya tidak tahu apakah JavaScript memungkinkan Anda melakukan ini) tidak ada lagi kode dan lebih mudah dipahami sebagai penelepon. Ini menetapkan asumsi realistis tentang apa yang dipikirkan oleh pembuat kode ini, dan membuat kode tersebut mudah digunakan.
sumber