Saya memiliki situasi di mana saya mencoba mengambil objek. Jika pencarian gagal, saya memiliki beberapa fallback di tempat, yang masing-masing mungkin gagal. Jadi kodenya terlihat seperti:
try {
return repository.getElement(x);
} catch (NotFoundException e) {
try {
return repository.getSimilarElement(x);
} catch (NotFoundException e1) {
try {
return repository.getParentElement(x);
} catch (NotFoundException e2) {
//can't recover
throw new IllegalArgumentException(e);
}
}
}
Ini terlihat sangat jelek. Saya benci untuk mengembalikan nol, tetapi apakah itu lebih baik dalam situasi ini?
Element e = return repository.getElement(x);
if (e == null) {
e = repository.getSimilarElement(x);
}
if (e == null) {
e = repository.getParentElement(x);
}
if (e == null) {
throw new IllegalArgumentException();
}
return e;
Apakah ada alternatif lain?
Apakah menggunakan blok try-catch bersarang merupakan anti-pola? terkait, tetapi jawabannya ada di sepanjang baris "kadang-kadang, tetapi biasanya dapat dihindari", tanpa mengatakan kapan atau bagaimana cara menghindarinya.
java
exception-handling
Alex Wittig
sumber
sumber
NotFoundException
sesuatu yang benar-benar luar biasa?Jawaban:
Cara biasa untuk menghilangkan sarang adalah menggunakan fungsi:
Jika aturan fallback ini bersifat universal, Anda bisa mempertimbangkan untuk menerapkan ini secara langsung di
repository
objek, di mana Anda mungkin bisa menggunakanif
pernyataan sederhana alih-alih pengecualian.sumber
method
akan menjadi kata yang lebih baik daripadafunction
.Ini akan sangat mudah dengan sesuatu seperti Opsi monad. Sayangnya, Java tidak memilikinya. Di Scala, saya akan menggunakan
Try
tipe untuk menemukan solusi sukses pertama.Dalam pola pikir fungsional-pemrograman saya, saya akan membuat daftar panggilan balik yang mewakili berbagai sumber yang mungkin, dan mengulanginya sampai kita menemukan yang sukses pertama:
Ini dapat direkomendasikan hanya jika Anda benar-benar memiliki sejumlah besar sumber, atau jika Anda harus mengkonfigurasi sumber saat runtime. Jika tidak, ini adalah abstraksi yang tidak perlu dan Anda akan mendapat lebih banyak manfaat dari menjaga kode Anda sederhana dan bodoh, dan gunakan saja hasil tangkapan yang jelek itu.
sumber
Try
jenis dalam Scala, untuk menyebutkan monad, dan untuk solusi menggunakan loop.Optional
monad ( bukti ) sudah dirilis.Jika Anda mengantisipasi bahwa banyak panggilan repositori akan dilemparkan
NotFoundException
, Anda bisa menggunakan pembungkus di sekitar repositori untuk merampingkan kode. Saya tidak akan merekomendasikan ini untuk operasi normal, ingatlah:sumber
Atas saran @ amon, inilah jawaban yang lebih monadik. Ini adalah versi yang sangat matang, di mana Anda harus menerima beberapa asumsi:
fungsi "unit" atau "return" adalah konstruktor kelas
operasi "bind" terjadi pada waktu kompilasi, jadi itu disembunyikan dari doa
fungsi "action" juga terikat ke kelas pada waktu kompilasi
walaupun kelasnya generik dan membungkus sembarang kelas E, saya pikir itu sebenarnya berlebihan dalam hal ini. Tetapi saya membiarkannya sebagai contoh dari apa yang dapat Anda lakukan.
Dengan pertimbangan tersebut, monad diterjemahkan ke dalam kelas pembungkus yang lancar (meskipun Anda memberikan banyak fleksibilitas yang akan Anda dapatkan dalam bahasa yang murni fungsional):
(ini tidak akan dikompilasi ... detail tertentu dibiarkan belum selesai untuk menjaga sampel tetap kecil)
Dan doa akan terlihat seperti ini:
Perhatikan bahwa Anda memiliki fleksibilitas untuk menyusun operasi "ambil" sesuka Anda. Itu akan berhenti ketika mendapat jawaban atau pengecualian selain tidak ditemukan.
Saya melakukan ini dengan sangat cepat; itu tidak benar, tetapi mudah-mudahan menyampaikan gagasan itu
sumber
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- menurut saya: kode jahat (dalam pengertian yang diberikan oleh Jon Skeet)return this
untuk membuat objek panggilan rantai telah ada sejak lama. Karena OO melibatkan objek yang bisa berubah,return this
kurang lebih setara denganreturn null
tanpa rantai. Namun,return new Thing<E>
buka pintu ke kemampuan lain yang tidak dicontoh oleh contoh ini, jadi penting untuk pola ini jika Anda memilih untuk menempuh jalan ini.CustomerBuilder.withName("Steve").withID(403)
dan kode ini, karena hanya dari melihat.fetchElement().fetchParentElement().fetchSimilarElement()
itu tidak jelas apa yang terjadi, dan itulah kuncinya di sini. Apakah mereka semua dijemput? Ini tidak akumulatif dalam kasus ini, dan karena itu tidak intuitif. Saya perlu melihat ituif (answer != null) return this
sebelum saya benar-benar mendapatkannya. Mungkin itu hanya masalah penamaan yang tepat (orFetchParent
), tapi itu sih "sihir".answer
masukgetAnswer
dan mengatur ulang (hapus)answer
bidang itu sendiri sebelum mengembalikan nilainya. Kalau tidak, ini semacam melanggar prinsip pemisahan perintah / kueri, karena meminta untuk mengambil elemen (permintaan) mengubah keadaan objek repositori Anda (answer
tidak pernah diatur ulang) dan memengaruhi perilakufetchElement
ketika Anda menyebutnya waktu berikutnya. Ya, saya sedikit rewel, saya pikir jawabannya valid, saya bukan orang yang menurunkannya.Cara lain untuk menyusun serangkaian kondisi seperti ini adalah dengan membawa bendera, atau tes nol (lebih baik, gunakan Pilihan Guava untuk menentukan kapan ada jawaban yang baik) untuk menyatukan kondisi.
Dengan begitu, Anda memperhatikan status elemen, dan melakukan panggilan yang benar berdasarkan kondisinya - yaitu, selama Anda belum memiliki jawaban.
(Tapi saya setuju dengan @amon. Saya akan merekomendasikan melihat pola Monad, dengan objek pembungkus seperti
class Repository<E>
yang memiliki anggotaE answer;
danException error;
. Pada setiap tahap periksa untuk melihat apakah ada pengecualian, dan jika demikian, lewati setiap langkah yang tersisa. Di akhirnya, Anda memiliki jawaban, tidak ada jawaban, atau pengecualian & Anda dapat memutuskan apa yang harus dilakukan dengan itu.)sumber
Pertama, menurut saya harus ada fungsi seperti
repository.getMostSimilar(x)
(Anda harus memilih nama yang lebih tepat) karena tampaknya ada logika yang digunakan untuk menemukan elemen terdekat atau paling mirip untuk elemen tertentu.Repositori kemudian dapat mengimplementasikan logika seperti yang ditunjukkan pada post amons. Itu berarti, satu-satunya kasus pengecualian harus dilemparkan adalah ketika tidak ada elemen tunggal yang dapat ditemukan.
Namun ini tentu saja hanya mungkin jika logika untuk menemukan elemen terdekat dapat dienkapsulasi ke dalam repositori. Jika ini tidak memungkinkan, berikan informasi lebih lanjut bagaimana (dengan kriteria apa) elemen terdekat dapat dipilih.
sumber