Dengan asumsi sebuah bahasa dengan keamanan tipe bawaan (mis., Bukan JavaScript):
Diberikan metode yang menerima a SuperType
, kita tahu bahwa dalam kebanyakan kasus di mana kita mungkin tergoda untuk melakukan pengujian tipe untuk mengambil tindakan:
public void DoSomethingTo(SuperType o) {
if (o isa SubTypeA) {
o.doSomethingA()
} else {
o.doSomethingB();
}
}
Kita biasanya, jika tidak selalu, membuat metode tunggal yang dapat ditimpa pada SuperType
dan melakukan ini:
public void DoSomethingTo(SuperType o) {
o.doSomething();
}
... dimana setiap subtipe diberikan doSomething()
implementasinya sendiri . Sisa dari aplikasi kita kemudian dapat dengan tepat tidak mengetahui apakah yang diberikan SuperType
benar-benar a SubTypeA
atau a SubTypeB
.
Hebat.
Tapi, kami masih diberi is a
operasi mirip-di sebagian besar, jika tidak semua, jenis bahasa yang aman. Dan itu menunjukkan potensi kebutuhan untuk pengujian tipe eksplisit.
Jadi, dalam situasi apa, jika ada, harus kita atau harus kita melakukan pengujian jenis eksplisit?
Maafkan ketidakhadiran pikiran saya atau kurangnya kreativitas. Saya tahu saya pernah melakukannya sebelumnya; tapi, sejujurnya sudah lama sekali aku tidak bisa mengingat apakah yang kulakukan itu bagus! Dan dalam memori baru-baru ini, saya rasa saya tidak perlu menemukan jenis untuk menguji di luar JavaScript koboi saya.
sumber
Jawaban:
"Tidak pernah" adalah jawaban kanonik untuk "kapan pengujian tipe baik-baik saja?" Tidak ada cara untuk membuktikan atau membantah ini; itu adalah bagian dari sistem kepercayaan tentang apa yang membuat "desain yang baik" atau "desain berorientasi objek yang baik." Itu juga hokum.
Yang pasti, jika Anda memiliki satu set kelas terintegrasi dan juga lebih dari satu atau dua fungsi yang memerlukan pengujian jenis langsung, Anda mungkin MELAKUKAN SALAH. Yang benar-benar Anda butuhkan adalah metode yang diimplementasikan secara berbeda di dalam
SuperType
dan subtipe-nya. Ini adalah bagian tak terpisahkan dari pemrograman berorientasi objek, dan seluruh alasan kelas dan warisan ada.Dalam hal ini, pengujian tipe secara eksplisit salah bukan karena pengujian tipe secara inheren salah, tetapi karena bahasa tersebut sudah memiliki cara yang bersih, dapat diperluas, idiomatis untuk menyelesaikan diskriminasi tipe, dan Anda tidak menggunakannya. Alih-alih, Anda kembali pada idiom primitif, rapuh, dan tidak dapat diperluas.
Solusi: Gunakan idiom. Seperti yang Anda sarankan, tambahkan metode ke masing-masing kelas, lalu biarkan algoritma pewarisan standar dan pemilihan metode menentukan kasus mana yang berlaku. Atau jika Anda tidak dapat mengubah jenis dasar, subkelas dan tambahkan metode Anda di sana.
Begitu banyak untuk kebijaksanaan konvensional, dan untuk beberapa jawaban. Beberapa kasus di mana pengujian jenis eksplisit masuk akal:
Ini satu kali. Jika Anda memiliki banyak jenis diskriminasi untuk dilakukan, Anda dapat memperluas jenis, atau subkelas. Tapi kamu tidak. Anda hanya memiliki satu atau dua tempat di mana Anda perlu pengujian eksplisit, jadi tidak ada gunanya saat Anda kembali dan bekerja melalui hierarki kelas untuk menambahkan fungsi sebagai metode. Atau tidak sepadan dengan upaya praktis untuk menambahkan jenis generalisasi, pengujian, ulasan desain, dokumentasi, atau atribut lain dari kelas dasar untuk penggunaan yang sederhana dan terbatas. Dalam hal itu, menambahkan fungsi yang melakukan pengujian langsung adalah rasional.
Anda tidak dapat menyesuaikan kelas. Anda berpikir tentang subklasifikasi - tetapi Anda tidak bisa. Banyak kelas di Jawa, misalnya, ditunjuk
final
. Anda mencoba memasukkan apublic class ExtendedSubTypeA extends SubTypeA {...}
dan kompiler memberi tahu Anda, tanpa syarat yang tidak pasti, bahwa apa yang Anda lakukan tidak mungkin. Maaf, rahmat dan kecanggihan model berorientasi objek! Seseorang memutuskan Anda tidak dapat memperpanjang tipenya! Sayangnya, banyak perpustakaan standarfinal
, dan membuat kelasfinal
adalah panduan desain umum. Fungsi end-run adalah yang tersisa untuk Anda.BTW, ini tidak terbatas pada bahasa yang diketik secara statis. Bahasa dinamis Python memiliki sejumlah kelas dasar yang, di bawah penutup yang diimplementasikan dalam C, tidak dapat benar-benar dimodifikasi. Seperti Java, itu termasuk sebagian besar tipe standar.
Kode Anda eksternal. Anda berkembang dengan kelas dan objek yang berasal dari berbagai server basis data, mesin middleware, dan basis kode lain yang tidak dapat Anda kontrol atau sesuaikan. Kode Anda hanya konsumen rendah dari objek yang dihasilkan di tempat lain. Bahkan jika Anda bisa membuat subkelas
SuperType
, Anda tidak akan bisa mendapatkan pustaka yang Anda gunakan untuk menghasilkan objek di dalam subkelas Anda. Mereka akan memberi Anda contoh jenis yang mereka tahu, bukan varian Anda. Ini tidak selalu terjadi ... kadang-kadang mereka dibangun untuk fleksibilitas, dan mereka secara dinamis membuat instance kelas yang Anda beri mereka makan. Atau mereka menyediakan mekanisme untuk mendaftarkan subclass yang Anda ingin pabrik mereka bangun. Parser XML tampaknya sangat baik dalam menyediakan titik masuk seperti itu; lihat misalnya atau lxml dengan Python . Tetapi sebagian besar basis kode tidak menyediakan ekstensi tersebut. Mereka akan mengembalikan kelas yang mereka bangun dan ketahui. Biasanya tidak masuk akal untuk mem-proksi hasil mereka ke dalam hasil khusus Anda hanya agar Anda dapat menggunakan pemilih jenis berorientasi objek murni. Jika Anda akan melakukan diskriminasi jenis, Anda harus melakukannya dengan relatif kasar. Kode pengujian tipe Anda kemudian terlihat cukup sesuai.Obat-obatan generik / pengiriman banyak orang miskin. Anda ingin menerima berbagai jenis berbeda untuk kode Anda, dan merasa bahwa memiliki array metode yang sangat spesifik jenis tidak anggun.
public void add(Object x)
tampaknya logis, tapi tidak arrayaddByte
,addShort
,addInt
,addLong
,addFloat
,addDouble
,addBoolean
,addChar
, danaddString
varian (untuk beberapa nama). Memiliki fungsi atau metode yang menggunakan tipe super tinggi dan kemudian menentukan apa yang harus dilakukan berdasarkan tipe per tipe - mereka tidak akan memenangkan Anda Penghargaan Kemurnian pada Simposium Desain Booch-Liskov tahunan, tetapi menjatuhkan Penamaan Hongaria akan memberi Anda API yang lebih sederhana. Dalam arti tertentu, Andais-a
atauis-instance-of
pengujian mensimulasikan generik atau multi-pengiriman dalam konteks bahasa yang tidak mendukungnya secara asli.Dukungan bahasa bawaan untuk generik dan pengetikan bebek mengurangi kebutuhan untuk pengecekan jenis dengan membuat "melakukan sesuatu yang anggun dan sesuai" menjadi lebih mungkin. Pilihan banyak pengiriman / antarmuka yang terlihat dalam bahasa seperti Julia dan Go juga menggantikan pengujian jenis langsung dengan mekanisme bawaan untuk pemilihan "apa yang harus dilakukan" berbasis jenis. Tetapi tidak semua bahasa mendukung ini. Java misalnya umumnya pengiriman tunggal, dan idiomnya tidak super-friendly untuk mengetik bebek.
Tetapi bahkan dengan semua fitur diskriminasi jenis ini - pewarisan, generik, pengetikan bebek, dan pengiriman banyak - kadang-kadang hanya nyaman untuk memiliki satu rutin, konsolidasi yang membuat fakta bahwa Anda melakukan sesuatu berdasarkan jenis objek jelas dan langsung. Dalam metaprogramming, saya merasa hal itu pada dasarnya tidak dapat dihindari. Apakah kembali ke tipe langsung bertanya merupakan "pragmatisme dalam aksi" atau "coding kotor" akan tergantung pada filosofi dan keyakinan desain Anda.
sumber
(1.0).Equals(1.0f)
menghasilkan true [argumen dipromosikan kedouble
], tetapi(1.0f).Equals(1.0)
menghasilkan [argumen dipromosikan keobject
] salah; di Jawa,Math.round(123456789*1.0)
menghasilkan 123456789, tetapiMath.round(123456789*1.0)
menghasilkan 123456792 [argumen mempromosikanfloat
bukandouble
].Math.round
terlihat identik dengan saya. Apa bedanya?Math.round(123456789)
[menunjukkan apa yang mungkin terjadi jika seseorang menulis ulangMath.round(thing.getPosition() * COUNTS_PER_MIL)
untuk mengembalikan nilai posisi yang tidak dinaikkan, tidak menyadari bahwagetPosition
mengembalikan suatuint
ataulong
.]Situasi utama yang pernah saya butuhkan adalah ketika membandingkan dua objek, seperti dalam suatu
equals(other)
metode, yang mungkin memerlukan algoritma yang berbeda tergantung pada jenis persisnyaother
. Meski begitu, itu cukup langka.Situasi lain yang saya alami muncul, sekali lagi sangat jarang, adalah setelah deserialization atau parsing, di mana Anda kadang-kadang membutuhkannya untuk dengan aman dilemparkan ke jenis yang lebih spesifik.
Juga, terkadang Anda hanya perlu meretas untuk memperbaiki kode pihak ketiga yang tidak Anda kontrol. Ini adalah salah satu hal yang tidak benar-benar ingin Anda gunakan secara teratur, tetapi senang ada di sana ketika Anda benar-benar membutuhkannya.
sumber
BaseClass base = deserialize(input)
, karena Anda belum tahu jenisnya, maka Anda lakukanif (base instanceof Derived) derived = (Derived)base
untuk menyimpannya sebagai jenis turunan yang tepat.Kasus standar (tapi mudah-mudahan jarang) terlihat seperti ini: jika dalam situasi berikut
fungsi
DoSomethingA
atauDoSomethingB
tidak dapat dengan mudah diimplementasikan sebagai fungsi anggota dari pohon warisanSuperType
/SubTypeA
/SubTypeB
. Misalnya, jikaDoSomethingXXX
pustaka itu berarti memperkenalkan ketergantungan terlarang.Perhatikan ada sering situasi di mana Anda dapat menghindari masalah ini (misalnya, dengan membuat pembungkus atau adaptor untuk
SubTypeA
danSubTypeB
, atau mencoba untuk mengimplementasikan kembali secara lengkapDoSomething
dalam hal operasi dasarSuperType
), tetapi kadang-kadang solusi ini tidak sepadan dengan kerumitan atau membuat hal-hal yang lebih rumit dan kurang dapat dikembangkan daripada melakukan tes tipe eksplisit.Sebuah contoh dari pekerjaan kemarin saya: Saya punya situasi di mana saya akan memparalelkan pemrosesan daftar objek (tipe
SuperType
, dengan tepat dua subtipe berbeda, di mana sangat tidak mungkin ada lebih banyak). Versi tak tertandingi berisi dua loop: satu loop untuk objek subtipe A, panggilanDoSomethingA
, dan loop kedua untuk objek subtipe B, panggilanDoSomethingB
.Metode "DoSomethingA" dan "DoSomethingB" adalah perhitungan intensif waktu, menggunakan informasi konteks yang tidak tersedia pada ruang lingkup subtipe A dan B. (sehingga tidak masuk akal untuk mengimplementasikannya sebagai fungsi anggota dari subtipe). Dari sudut pandang "loop paralel" yang baru, itu membuat banyak hal lebih mudah dengan berurusan dengan mereka secara seragam, jadi saya mengimplementasikan fungsi yang mirip dengan
DoSomethingTo
dari atas. Namun, melihat implementasi dari "DoSomethingA" dan "DoSomethingB" menunjukkan mereka bekerja sangat berbeda secara internal. Jadi mencoba menerapkan generik "DoSomething" dengan memperluasSuperType
dengan banyak metode abstrak tidak benar-benar akan berhasil, atau akan berarti untuk merancang hal-hal sepenuhnya.sumber
SuperType
dan subclass itu?NSJSONSerialization
dalam Obj-C), tetapi Anda tidak ingin hanya percaya bahwa respons berisi tipe yang Anda harapkan, jadi sebelum menggunakannya Anda memeriksanya (misalnyaif ([theResponse isKindOfClass:[NSArray class]])...
) .Seperti Paman Bob menyebutnya:
Dalam salah satu episode Clean Coder-nya ia memberi contoh pemanggilan fungsi yang digunakan untuk mengembalikan
Employee
s.Manager
adalah sub-tipe dariEmployee
. Mari kita asumsikan kita memiliki layanan aplikasi yang menerimaManager
id dan memanggilnya ke kantor :) FungsigetEmployeeById()
mengembalikan tipe superEmployee
, tapi saya ingin memeriksa apakah manajer dikembalikan dalam kasus penggunaan ini.Sebagai contoh:
Di sini saya sedang memeriksa untuk melihat apakah karyawan yang dikembalikan oleh permintaan sebenarnya adalah seorang manajer (yaitu saya berharap itu menjadi manajer dan jika gagal cepat).
Bukan contoh terbaik, tapi bagaimanapun juga paman Bob.
Memperbarui
Saya memperbarui contoh sebanyak yang saya ingat dari memori.
sumber
Manager
implementasi tidaksummon()
hanya melemparkan pengecualian dalam contoh ini?CEO
bisa memanggilManager
s.Employee
, itu hanya harus peduli bahwa ia mendapatkan sesuatu yang berperilaku sepertiEmployee
. Jika subclass yang berbedaEmployee
memiliki izin yang berbeda, tanggung jawab, dll., Apa yang membuat pengujian jenis opsi yang lebih baik daripada sistem izin nyata?Tidak pernah.
is
klausa, atau (dalam beberapa bahasa, atau tergantung pada skenario) karena Anda tidak dapat memperpanjang jenis tanpa memodifikasi internal fungsi melakukanis
pemeriksaan.is
cek adalah pertanda kuat bahwa Anda melanggar Prinsip Substitusi Liskov . Apa pun yang bekerja denganSuperType
harus benar-benar tidak tahu apa jenis sub ada.Semua yang dikatakan,
is
cek bisa kurang buruk daripada alternatif lain. Menempatkan semua fungsi umum ke dalam kelas dasar adalah tangan yang berat dan seringkali menyebabkan masalah yang lebih buruk. Menggunakan satu kelas yang memiliki bendera atau enum untuk "tipe" contohnya adalah ... lebih buruk daripada mengerikan, karena Anda sekarang menyebarkan jenis sistem pengelakan ke semua konsumen.Singkatnya, Anda harus selalu mempertimbangkan pemeriksaan tipe sebagai bau kode yang kuat. Namun seperti semua panduan, ada saatnya Anda dipaksa untuk memilih di antara pelanggaran pedoman mana yang paling tidak ofensif.
sumber
instanceof
kebocoran detail implementasi dan memecah abstraksi.IEnumerable<T>
tidak menjanjikan elemen "terakhir" ada. Jika metode Anda membutuhkan elemen seperti itu, ia harus membutuhkan tipe yang menjamin keberadaannya. Dan subtipe jenis itu kemudian dapat memberikan implementasi efisien dari metode "Terakhir".Jika Anda mendapat basis kode yang besar (lebih dari 100 ribu baris kode) dan dekat dengan pengiriman atau sedang bekerja di cabang yang nantinya harus digabung, dan oleh karena itu ada banyak biaya / risiko untuk mengubah banyak mengatasi.
Anda kemudian kadang-kadang memiliki opsi refraktor besar sistem, atau "pengujian tipe" sederhana yang dilokalkan. Ini menciptakan hutang teknis yang harus dibayar kembali sesegera mungkin, tetapi seringkali tidak.
(Tidak mungkin menghasilkan contoh, karena kode apa pun yang cukup kecil untuk digunakan sebagai contoh, juga cukup kecil untuk desain yang lebih baik agar terlihat jelas.)
Atau dengan kata lain, ketika tujuannya adalah untuk membayar upah Anda alih - alih mendapatkan "suara" untuk kebersihan desain Anda.
Kasus umum lainnya adalah kode UI, ketika misalnya Anda menunjukkan UI yang berbeda untuk beberapa jenis karyawan, tetapi jelas Anda tidak ingin konsep UI lolos ke semua "domain" kelas Anda.
Anda dapat menggunakan “pengujian tipe” untuk memutuskan versi UI mana yang akan ditampilkan, atau memiliki tabel pencarian mewah yang mengubah dari “kelas domain” ke “kelas UI”. Tabel pencarian hanyalah cara menyembunyikan "pengujian tipe" di satu tempat.
(Kode pembaruan basis data dapat memiliki masalah yang sama dengan kode UI, namun Anda cenderung hanya memiliki satu set kode pembaruan basis data, tetapi Anda dapat memiliki banyak layar berbeda yang harus beradaptasi dengan jenis objek yang ditampilkan.)
sumber
Implementasi LINQ menggunakan banyak jenis pemeriksaan untuk kemungkinan optimasi kinerja, dan kemudian mundur untuk IEnumerable.
Contoh yang paling jelas mungkin adalah metode ElementAt (kutipan kecil dari sumber .NET 4.5):
Tetapi ada banyak tempat di kelas Enumerable di mana pola yang sama digunakan.
Jadi mungkin mengoptimalkan kinerja untuk subtipe yang umum digunakan adalah penggunaan yang valid. Saya tidak yakin bagaimana ini bisa dirancang lebih baik.
sumber
IEnumerable<T>
memasukkan banyak metode seperti yang ada di dalamnyaList<T>
, bersama denganFeatures
properti yang menunjukkan metode mana yang dapat diharapkan bekerja dengan baik, perlahan, atau tidak sama sekali, serta berbagai asumsi yang dapat dilakukan konsumen dengan aman tentang koleksi (mis. ukuran dan / atau konten yang ada dijamin tidak akan pernah berubah [suatu tipe mungkin mendukungAdd
sementara masih menjamin bahwa konten yang ada tidak akan berubah]).Ada contoh yang sering muncul dalam pengembangan game, khususnya dalam deteksi tabrakan, yang sulit dihadapi tanpa menggunakan beberapa bentuk pengujian tipe.
Asumsikan bahwa semua objek game berasal dari kelas dasar umum
GameObject
. Setiap objek memiliki bentuk tubuh tabrakan kakuCollisionShape
yang dapat memberikan interface umum (untuk mengatakan posisi query, orientasi, dll) tapi bentuk tabrakan sebenarnya semua akan subclass beton sepertiSphere
,Box
,ConvexHull
, dll menyimpan informasi spesifik untuk jenis objek geometris (lihat di sini untuk contoh nyata)Sekarang, untuk menguji tabrakan saya perlu menulis fungsi untuk setiap pasangan jenis bentuk tabrakan:
yang berisi matematika spesifik yang diperlukan untuk melakukan persimpangan dari dua tipe geometris tersebut.
Pada setiap 'centang' loop permainan saya, saya perlu memeriksa pasangan objek untuk tabrakan. Tapi saya hanya memiliki akses ke
GameObject
dan sesuai merekaCollisionShape
. Jelas saya perlu tahu tipe konkret untuk mengetahui fungsi deteksi tabrakan untuk memanggil. Bahkan pengiriman ganda (yang secara logis tidak berbeda dengan memeriksa jenisnya) dapat membantu di sini *.Dalam prakteknya dalam situasi ini mesin fisika yang pernah saya lihat (Bullet dan Havok) mengandalkan pengujian tipe dari satu bentuk atau lainnya.
Saya tidak mengatakan ini tentu merupakan solusi yang baik , hanya saja itu mungkin yang terbaik dari sejumlah kecil kemungkinan solusi untuk masalah ini
* Secara teknis itu adalah mungkin untuk menggunakan pengiriman ganda dengan cara menghebohkan dan rumit yang akan memerlukan N (N + 1) / 2 kombinasi (di mana N adalah jumlah jenis bentuk yang Anda miliki) dan hanya akan mengaburkan apa yang Anda benar-benar melakukan yang secara bersamaan mencari tahu jenis dari dua bentuk jadi saya tidak menganggap ini sebagai solusi yang realistis.
sumber
Kadang-kadang Anda tidak ingin menambahkan metode umum ke semua kelas karena itu bukan tanggung jawab mereka untuk melakukan tugas tertentu.
Misalnya Anda ingin menggambar beberapa entitas tetapi tidak ingin menambahkan kode gambar langsung ke mereka (yang masuk akal). Dalam bahasa yang tidak mendukung banyak pengiriman, Anda mungkin berakhir dengan kode berikut:
Ini menjadi bermasalah ketika kode ini muncul di beberapa tempat dan Anda perlu memodifikasinya di mana saja ketika menambahkan tipe Entitas baru. Jika itu masalahnya maka hal itu dapat dihindari dengan menggunakan pola Pengunjung tetapi kadang-kadang lebih baik untuk menjaga hal-hal sederhana dan tidak memaksakannya. Itulah situasi ketika pengujian tipe OK.
sumber
Satu-satunya waktu yang saya gunakan adalah kombinasi dengan refleksi. Tetapi meskipun demikian sebagian besar pemeriksaan dinamis, tidak dikodekan ke kelas tertentu (atau hanya dikodekan ke kelas khusus seperti
String
atauList
).Maksud saya dengan memeriksa dinamis:
dan tidak hard-coded
sumber
Pengujian tipe dan tipe casting adalah dua konsep yang sangat terkait erat. Begitu erat hubungannya sehingga saya merasa yakin untuk mengatakan bahwa Anda tidak boleh melakukan tes tipe kecuali niat Anda adalah mengetikkan objek berdasarkan hasil.
Ketika Anda berpikir tentang desain Berorientasi Objek yang ideal, pengujian tipe (dan casting) seharusnya tidak pernah terjadi. Tapi semoga sekarang Anda sudah tahu bahwa pemrograman Berorientasi Objek tidak ideal. Terkadang, terutama dengan kode level lebih rendah, kode tersebut tidak dapat tetap sesuai dengan ideal. Ini adalah kasus dengan ArrayLists di Jawa; karena mereka tidak tahu pada saat run-time kelas apa yang disimpan dalam array, mereka membuat
Object[]
array dan secara statis melemparkannya ke tipe yang benar.Telah ditunjukkan bahwa kebutuhan umum untuk mengetik pengujian (dan mengetikkan casting) berasal dari
Equals
metode ini, yang dalam kebanyakan bahasa seharusnya dianggap sederhana.Object
. Implementasi harus memiliki beberapa pemeriksaan terperinci untuk membuat jika kedua objek adalah tipe yang sama, yang mengharuskan untuk dapat menguji tipe apa mereka.Pengujian tipe juga sering muncul dalam refleksi. Seringkali Anda akan memiliki metode yang mengembalikan
Object[]
atau beberapa array generik lainnya, dan Anda ingin menarik semuaFoo
objek untuk alasan apa pun. Ini adalah penggunaan sah untuk pengujian dan casting tipe.Secara umum, pengujian tipe buruk ketika kode pasangan Anda sia-sia dengan bagaimana implementasi spesifik ditulis. Ini dapat dengan mudah menyebabkan diperlukan tes khusus untuk setiap jenis atau kombinasi jenis, seperti jika Anda ingin menemukan persimpangan garis, persegi panjang, dan lingkaran, dan fungsi persimpangan memiliki algoritma yang berbeda untuk setiap kombinasi. Tujuan Anda adalah untuk menempatkan detail apa pun yang spesifik untuk satu jenis objek di tempat yang sama dengan objek itu, karena itu akan membuatnya lebih mudah untuk mempertahankan dan memperluas kode Anda.
sumber
ArrayLists
tidak tahu kelas yang disimpan pada saat runtime karena Java tidak memiliki obat generik dan ketika mereka akhirnya diperkenalkan Oracle memilih kompatibilitas mundur dengan kode generik-kurang.equals
memiliki masalah yang sama, dan itu adalah keputusan desain yang dipertanyakan; perbandingan kesetaraan tidak masuk akal untuk setiap jenis.String x = (String) myListOfStrings.get(0)
Object
sampai diakses pula; generik di Jawa hanya menyediakan pengecoran implisit yang dibuat aman oleh aturan kompiler.Ini dapat diterima dalam kasus di mana Anda harus membuat keputusan yang melibatkan dua jenis dan keputusan ini dikemas dalam objek di luar hierarki jenis itu. Misalnya, Anda menjadwalkan objek mana yang akan diproses berikutnya dalam daftar objek yang menunggu diproses:
Sekarang katakanlah logika bisnis kita secara harfiah adalah "semua mobil lebih diutamakan daripada kapal dan truk". Menambahkan
Priority
properti ke kelas tidak memungkinkan Anda untuk mengekspresikan logika bisnis ini dengan bersih karena Anda akan berakhir dengan ini:Masalahnya adalah bahwa sekarang untuk memahami prioritas pemesanan Anda harus melihat semua subclass, atau dengan kata lain Anda telah menambahkan kopling ke subclass.
Tentu saja Anda harus membuat prioritas menjadi konstanta dan menempatkannya ke dalam kelas sendiri, yang membantu menjaga penjadwalan logika bisnis bersama:
Namun, pada kenyataannya algoritma penjadwalan adalah sesuatu yang mungkin berubah di masa depan dan pada akhirnya mungkin bergantung pada lebih dari sekadar mengetik. Misalnya, bisa dikatakan "truk dengan berat lebih dari 5000 kg mendapat prioritas khusus di atas semua kendaraan lain." Itu sebabnya algoritma penjadwalan termasuk dalam kelasnya sendiri, dan itu ide yang baik untuk memeriksa jenis untuk menentukan mana yang harus duluan:
Ini adalah cara paling mudah untuk mengimplementasikan logika bisnis dan masih yang paling fleksibel untuk perubahan di masa depan.
sumber
null
, aString
, atau aString[]
. Jika 99% dari objek akan membutuhkan tepat satu string, mengenkapsulasi setiap string dalam konstruksi yang terpisahString[]
dapat menambah overhead penyimpanan yang cukup besar. Menangani kasing tunggal menggunakan referensi langsung keString
akan membutuhkan lebih banyak kode, tetapi akan menghemat penyimpanan dan dapat membuat segalanya lebih cepat.Pengujian tipe adalah alat, gunakan dengan bijak dan bisa menjadi sekutu yang kuat. Gunakan dengan buruk dan kode Anda akan mulai berbau.
Dalam perangkat lunak kami, kami menerima pesan melalui jaringan sebagai tanggapan atas permintaan. Semua pesan deserialized berbagi kelas dasar yang sama
Message
.Kelas-kelas itu sendiri sangat sederhana, hanya muatannya seperti mengetikkan properti C # dan rutinitas untuk menyusun dan mengosongkannya (Bahkan saya membuat sebagian besar kelas menggunakan templat t4 dari deskripsi XML format pesan)
Kode akan menjadi seperti:
Memang, orang dapat berargumentasi bahwa arsitektur pesan bisa dirancang lebih baik tetapi dirancang sejak lama dan bukan untuk C # jadi memang demikian. Di sini pengujian tipe memecahkan masalah nyata bagi kita dengan cara yang tidak terlalu buruk.
Yang perlu dicatat adalah bahwa C # 7.0 mendapatkan pencocokan pola (yang dalam banyak hal adalah pengujian jenis steroid) tidak mungkin semuanya buruk ...
sumber
Ambil parser JSON generik. Hasil penguraian yang sukses adalah array, kamus, string, angka, boolean, atau nilai nol. Itu bisa salah satunya. Dan elemen-elemen dari array atau nilai-nilai dalam kamus dapat kembali menjadi tipe-tipe tersebut. Karena data disediakan dari luar program Anda, Anda harus menerima hasil apa pun (yaitu Anda harus menerimanya tanpa menabrak; Anda dapat menolak hasil yang bukan yang Anda harapkan).
sumber