Pertanyaan saya adalah tentang kasus khusus Hewan kelas super.
- Saya
Animal
bisamoveForward()
daneat()
. Seal
meluasAnimal
.Dog
meluasAnimal
.- Dan ada makhluk khusus yang juga
Animal
disebut memanjangHuman
. Human
mengimplementasikan juga metodespeak()
(tidak diterapkan olehAnimal
).
Dalam implementasi metode abstrak yang menerima Animal
saya ingin menggunakan speak()
metode ini. Tampaknya tidak mungkin tanpa melakukan downcast. Jeremy Miller menulis dalam artikelnya bahwa orang yang tertekan berbau.
Apa yang akan menjadi solusi untuk menghindari downcasting dalam situasi ini?
java
object-oriented
type-casting
Bart Weber
sumber
sumber
Jawaban:
Jika Anda memiliki metode yang perlu tahu apakah kelas spesifik adalah tipe
Human
untuk melakukan sesuatu, maka Anda melanggar beberapa prinsip SOLID , terutama:Menurut pendapat saya, jika metode Anda mengharapkan tipe kelas tertentu, untuk memanggilnya metode tertentu, maka ubah metode itu untuk hanya menerima kelas itu, dan bukan antarmuka itu.
Sesuatu seperti ini :
dan tidak seperti ini:
sumber
Animal
pemanggilancanSpeak
dan setiap implementasi konkret harus menentukan apakah ia dapat "berbicara" atau tidak.public void makeAnimalDoDailyThing(Animal animal) {animal.moveForward(); animal.eat()}
danpublic void makeAnimalDoDailyThing(Human human) {human.moveForward(); human.eat(); human.speak();}
Masalahnya bukan bahwa Anda downcasting - itu yang Anda downcasting
Human
. Alih-alih, buat antarmuka:Dengan cara ini, syaratnya bukan bahwa hewan itu
Human
- syaratnya adalah ia dapat berbicara. Ini berartimysteriousMethod
dapat bekerja dengan subkelas non-manusia lainnyaAnimal
selama mereka menerapkannyaCanSpeak
.sumber
Animal
, dan bahwa semua pengguna metode itu akan menyimpan objek yang ingin mereka kirim ke sana melaluiCanSpeak
referensi tipe (atau bahkanHuman
tipe referensi). Jika itu masalahnya, metode itu bisa digunakanHuman
sejak awal dan kita tidak perlu memperkenalkannyaCanSpeak
.CanSpeak
di tempat pertama adalah bukan karena kami memiliki sesuatu yang mengimplementasikannya (Human
) tetapi kami memiliki sesuatu yang menggunakannya (metode). MaksudnyaCanSpeak
adalah untuk memisahkan metode itu dari kelas betonHuman
. Jika kita tidak memiliki metode yang memperlakukan hal-hal yangCanSpeak
berbeda, tidak ada gunanya membedakan hal-hal ituCanSpeak
. Kami tidak membuat antarmuka hanya karena kami memiliki metode ...Anda dapat menambahkan Berkomunikasi ke Hewan. Anjing menggonggong, Manusia berbicara, Anjing laut .. uhh .. Saya tidak tahu anjing laut apa.
Tetapi sepertinya metode Anda dirancang untuk jika (Hewan adalah Manusia) Bicara ();
Pertanyaan yang mungkin ingin Anda tanyakan adalah, apa alternatifnya? Sulit memberikan saran karena saya tidak tahu persis apa yang ingin Anda capai. Ada situasi teoretis di mana downcasting / upcasting adalah pendekatan terbaik.
sumber
Dalam hal ini, implementasi default
speak()
diAbstractAnimal
kelas adalah:Pada titik itu, Anda memiliki implementasi default di kelas Abstrak - dan itu berlaku dengan benar.
Ya, ini berarti Anda punya try-catch yang tersebar di seluruh kode untuk menangani semuanya
speak
, tetapi alternatifnya adalahif(thingy is Human)
membungkus semua yang berbicara.Keuntungan dari pengecualian adalah bahwa jika Anda memiliki jenis hal lain di beberapa titik yang berbicara (burung beo) Anda tidak perlu menerapkan kembali semua tes Anda.
sumber
canSpeak()
metode untuk menangani ini dengan lebih baik.Downcasting terkadang perlu dan tepat. Secara khusus, itu sering tepat dalam kasus di mana seseorang memiliki objek yang mungkin atau mungkin tidak memiliki beberapa kemampuan, dan seseorang ingin menggunakan kemampuan itu ketika ada saat menangani objek tanpa kemampuan itu dalam beberapa mode standar. Sebagai contoh sederhana, anggap a
String
ditanya apakah itu sama dengan beberapa objek arbitrer lainnya. Agar satuString
sama dengan yang lainString
, ia harus memeriksa panjang dan dukungan array karakter dari string lainnya. JikaString
ditanya apakah sama denganDog
, bagaimanapun, ia tidak dapat mengakses panjangnyaDog
, tetapi seharusnya tidak harus; sebagai gantinya, jika objek yangString
seharusnya dibandingkan itu sendiri bukan aString
, perbandingan harus menggunakan perilaku default (melaporkan bahwa objek lain tidak sama).Waktu ketika downcasting harus dianggap sebagai yang paling meragukan adalah ketika objek yang dilemparkan "diketahui" memiliki tipe yang tepat. Secara umum, jika suatu objek dikenal sebagai
Cat
, seseorang harus menggunakan variabel tipeCat
, bukan variabel tipeAnimal
, untuk merujuknya. Namun ada kalanya ini tidak selalu berhasil. Sebagai contoh,Zoo
koleksi mungkin menahan pasangan objek dalam slot array genap / ganjil, dengan harapan bahwa objek di setiap pasangan akan dapat saling bertindak, bahkan jika mereka tidak dapat bertindak terhadap objek di pasangan lain. Dalam kasus seperti itu, objek dalam setiap pasangan masih harus menerima tipe parameter non-spesifik sehingga mereka, secara sintaksis , dapat melewati objek dari pasangan lain. Jadi, bahkan jikaCat
ituplayWith(Animal other)
Metode hanya akan bekerja ketikaother
suatuCat
,Zoo
akan perlu untuk dapat melewatinya elemen dariAnimal[]
, jadi tipe parameternya harusAnimal
lebih daripadaCat
.Dalam kasus di mana downcasting secara sah tidak dapat dihindari, seseorang harus menggunakannya tanpa keraguan. Pertanyaan kuncinya adalah menentukan kapan seseorang secara bijaksana dapat menghindari downcasting, dan menghindarinya jika memungkinkan.
sumber
Object.equalToString(String string)
. Maka Anda memilikiboolean String.equal(Object object) { return object.equalStoString(this); }
Jadi, tidak perlu downcast: Anda dapat menggunakan pengiriman dinamis.Object
memilikiequalStoString
metode virtual, dan saya akui saya tidak tahu bagaimana contoh yang dikutip akan bekerja di Jawa, tetapi dalam C #, pengiriman dinamis (berbeda dari pengiriman virtual) akan berarti bahwa kompiler pada dasarnya memiliki melakukan Refleksi berbasis nama pencarian pertama kali metode digunakan pada kelas, yang berbeda dari pengiriman virtual (yang hanya membuat panggilan melalui slot di tabel metode virtual yang diperlukan untuk berisi alamat metode yang valid).Anda punya beberapa pilihan:
Gunakan refleksi untuk menelepon
speak
jika ada. Keuntungan: tidak ada ketergantunganHuman
. Kerugian: sekarang ada ketergantungan tersembunyi pada nama "berbicara".Memperkenalkan antarmuka baru
Speaker
dan downcast ke antarmuka. Ini lebih fleksibel daripada tergantung pada jenis beton tertentu. Ini memiliki kelemahan yang harus Anda modifikasiHuman
untuk diterapkanSpeaker
. Ini tidak akan berfungsi jika Anda tidak dapat memodifikasiHuman
Downcast ke
Human
. Ini memiliki kelemahan yaitu Anda harus mengubah kode kapan pun Anda ingin subclass lain berbicara. Idealnya Anda ingin memperpanjang aplikasi dengan menambahkan kode tanpa berulang kali kembali dan mengubah kode lama.sumber