Memiliki rangkaian operasi "contoh" dianggap sebagai "bau kode". Jawaban standarnya adalah "gunakan polimorfisme". Bagaimana saya melakukannya dalam kasus ini?
Ada sejumlah subclass dari kelas dasar; tidak satupun dari mereka di bawah kendali saya. Situasi serupa adalah dengan kelas Java Integer, Double, BigDecimal dll.
if (obj instanceof Integer) {NumberStuff.handle((Integer)obj);}
else if (obj instanceof BigDecimal) {BigDecimalStuff.handle((BigDecimal)obj);}
else if (obj instanceof Double) {DoubleStuff.handle((Double)obj);}
Saya memiliki kendali atas NumberStuff dan seterusnya.
Saya tidak ingin menggunakan banyak baris kode di mana beberapa baris bisa digunakan. (Terkadang saya membuat pemetaan HashMap Integer.class ke instance IntegerStuff, BigDecimal.class ke instance BigDecimalStuff, dll. Tapi hari ini saya menginginkan sesuatu yang lebih sederhana.)
Saya ingin sesuatu yang sederhana seperti ini:
public static handle(Integer num) { ... }
public static handle(BigDecimal num) { ... }
Tapi Java tidak berfungsi seperti itu.
Saya ingin menggunakan metode statis saat memformat. Hal-hal yang saya format adalah gabungan, di mana Thing1 dapat berisi array Thing2s dan Thing2 dapat berisi array Thing1s. Saya mengalami masalah ketika saya menerapkan format saya seperti ini:
class Thing1Formatter {
private static Thing2Formatter thing2Formatter = new Thing2Formatter();
public format(Thing thing) {
thing2Formatter.format(thing.innerThing2);
}
}
class Thing2Formatter {
private static Thing1Formatter thing1Formatter = new Thing1Formatter();
public format(Thing2 thing) {
thing1Formatter.format(thing.innerThing1);
}
}
Ya, saya tahu HashMap dan sedikit lebih banyak kode dapat memperbaikinya juga. Tapi "contoh" tampaknya begitu mudah dibaca dan dipelihara sebagai perbandingan. Adakah yang sederhana tapi tidak berbau?
Catatan ditambahkan pada 5/10/2010:
Ternyata subclass baru mungkin akan ditambahkan di masa mendatang, dan kode saya yang ada harus menanganinya dengan baik. HashMap di Kelas tidak akan berfungsi dalam kasus itu karena Kelas tidak akan ditemukan. Rantai pernyataan if, dimulai dengan yang paling spesifik dan diakhiri dengan yang paling umum, mungkin adalah yang terbaik:
if (obj instanceof SubClass1) {
// Handle all the methods and properties of SubClass1
} else if (obj instanceof SubClass2) {
// Handle all the methods and properties of SubClass2
} else if (obj instanceof Interface3) {
// Unknown class but it implements Interface3
// so handle those methods and properties
} else if (obj instanceof Interface4) {
// likewise. May want to also handle case of
// object that implements both interfaces.
} else {
// New (unknown) subclass; do what I can with the base class
}
sumber
[text](link)
untuk memposting link di komentar.Jawaban:
Anda mungkin tertarik dengan entri ini dari blog Amazon Steve Yegge: "ketika polimorfisme gagal" . Pada dasarnya dia menangani kasus seperti ini, ketika polimorfisme menyebabkan lebih banyak masalah daripada menyelesaikannya.
Masalahnya adalah bahwa untuk menggunakan polimorfisme Anda harus membuat logika "menangani" bagian dari setiap kelas 'switching' - yaitu Integer dll. Dalam kasus ini. Jelas ini tidak praktis. Terkadang secara logis bahkan bukan tempat yang tepat untuk meletakkan kode. Dia merekomendasikan pendekatan 'instanceof' sebagai yang lebih kecil dari beberapa kejahatan.
Seperti semua kasus di mana Anda dipaksa untuk menulis kode yang berbau, biarkan kancingnya dalam satu metode (atau paling banyak satu kelas) sehingga baunya tidak bocor.
sumber
instanceof
.Monster
kelas, tanggung jawab yang pada dasarnya adalah untuk memperkenalkan objek, seperti "Halo, saya Orc. Apa pendapat Anda tentang saya?". Peri beropini kemudian dapat menilai monster berdasarkan "salam" ini, dengan kode yang mirip denganbool visitOrc(Orc orc) { return orc.stench()<threshold; } bool visitFlower(Flower flower) { return flower.colour==magenta; }
. Satu-satunya kode spesifik monster kemudian akanclass Orc { <T> T accept(MonsterVisitor<T> v) { v.visitOrc(this); } }
cukup untuk setiap inspeksi monster sekali dan untuk semua.Seperti yang disorot di komentar, pola pengunjung akan menjadi pilihan yang baik. Tetapi tanpa kontrol langsung atas target / akseptor / visitee Anda tidak dapat menerapkan pola itu. Berikut salah satu cara pola pengunjung masih dapat digunakan di sini meskipun Anda tidak memiliki kontrol langsung atas subkelas dengan menggunakan pembungkus (mengambil Integer sebagai contoh):
Tentu saja, membungkus kelas terakhir mungkin dianggap baunya sendiri, tetapi mungkin cocok dengan subkelas Anda. Secara pribadi, saya tidak berpikir
instanceof
itu bau busuk di sini, terutama jika itu terbatas pada satu metode dan saya akan dengan senang hati menggunakannya (mungkin atas saran saya sendiri di atas). Seperti yang Anda katakan, ini cukup mudah dibaca, aman dan mudah dipelihara. Seperti biasa, tetap sederhana.sumber
instanceof
untuk memanggilhandle()
metode yang benar Anda sekarang harus menggunakannya panggilanXWrapper
konstruktor yang tepat ...Alih-alih yang sangat besar
if
, Anda bisa meletakkan instance yang Anda tangani di peta (key: class, value: handler).Jika pencarian dengan kunci kembali
null
, panggil metode penangan khusus yang mencoba menemukan penangan yang cocok (misalnya dengan memanggilisInstance()
setiap kunci di peta).Ketika penangan ditemukan, daftarkan dengan kunci baru.
Ini membuat kasus umum cepat dan sederhana dan memungkinkan Anda menangani warisan.
sumber
Anda dapat menggunakan refleksi:
Anda dapat memperluas ide untuk menangani subclass dan class secara umum yang mengimplementasikan antarmuka tertentu.
sumber
if then else
rantai yang berkembang untuk menambah, menghapus atau memodifikasi penangan. Kode tidak terlalu rentan terhadap perubahan. Jadi saya akan mengatakan bahwa karena alasan ini lebih unggul dariinstanceof
pendekatannya. Bagaimanapun, saya hanya ingin memberikan alternatif yang valid.getMethod(String name, Class<?>... parameterTypes)
? Atau saya akan mengganti==
denganisAssignableFrom
untuk pemeriksaan tipe parameter.Anda dapat mempertimbangkan pola Rantai Tanggung Jawab . Untuk contoh pertama Anda, seperti:
dan juga untuk penangan Anda yang lain. Maka ini adalah kasus merangkai StuffHandlers secara berurutan (paling spesifik hingga paling tidak spesifik, dengan penangan 'fallback' terakhir), dan kode despatcher Anda hanya
firstHandler.handle(o);
.(Alternatifnya adalah, daripada menggunakan chain, hanya memiliki a
List<StuffHandler>
di kelas dispatcher Anda, dan mengulanginya melalui daftar sampaihandle()
mengembalikan true).sumber
Saya pikir solusi terbaik adalah HashMap dengan Kelas sebagai kunci dan Penangan sebagai nilai. Perhatikan bahwa solusi berbasis HashMap berjalan dalam kompleksitas algoritme yang konstan θ (1), sedangkan rantai penciuman if-instanceof-else berjalan dalam kompleksitas algoritmik linier O (N), di mana N adalah jumlah tautan dalam rantai if-instanceof-else (yaitu jumlah kelas berbeda yang akan ditangani). Jadi kinerja solusi berbasis HashMap secara asimtotik lebih tinggi N kali daripada kinerja solusi rantai if-instanceof-else. Pertimbangkan bahwa Anda perlu menangani turunan kelas Message yang berbeda secara berbeda: Message1, Message2, dll. Di bawah ini adalah potongan kode untuk penanganan berbasis HashMap.
Info lebih lanjut tentang penggunaan variabel tipe Class di Java: http://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
sumber
Pergi saja dengan instanceof. Semua solusi tampak lebih rumit. Berikut adalah entri blog yang membahasnya: http://www.velocityreviews.com/forums/t302491-instanceof-not-always-bad-the-instanceof-myth.html
sumber
Saya telah memecahkan masalah ini menggunakan
reflection
(sekitar 15 tahun yang lalu di era pra Generik).Saya telah mendefinisikan satu Kelas Generik (kelas Basis abstrak). Saya telah mendefinisikan banyak implementasi konkret dari kelas dasar. Setiap kelas beton akan dimuat dengan className sebagai parameter. Nama kelas ini didefinisikan sebagai bagian dari konfigurasi.
Kelas dasar mendefinisikan keadaan umum di semua kelas beton dan kelas beton akan mengubah keadaan dengan menimpa aturan abstrak yang ditentukan dalam kelas dasar.
Saat itu, saya tidak tahu yang namanya mekanisme ini, yang lebih dikenal dengan
reflection
.Beberapa alternatif lagi terdaftar dalam artikel ini :
Map
danenum
selain refleksi.sumber
GenericClass
sebuahinterface
?