Sebagai bagian dari penulisan Iterator, saya menemukan diri saya menulis potongan kode berikut (stripping error handling)
public T next() {
try {
return next;
} finally {
next = fetcher.fetchNext(next);
}
}
merasa sedikit lebih mudah dibaca daripada
public T next() {
T tmp = next;
next = fetcher.fetchNext(next);
return tmp;
}
Saya tahu ini adalah contoh sederhana, di mana perbedaan dalam keterbacaan mungkin tidak terlalu besar, tapi saya tertarik pada pendapat umum, apakah itu buruk untuk menggunakan coba-akhirnya dalam kasus-kasus seperti ini di mana tidak ada pengecualian yang terlibat, atau jika itu sebenarnya lebih disukai ketika menyederhanakan kode.
Jika buruk: mengapa? Gaya, kinerja, jebakan, ...?
Kesimpulan Terima kasih atas semua jawaban Anda! Saya kira kesimpulannya (setidaknya bagi saya) adalah bahwa contoh pertama mungkin lebih mudah dibaca jika itu adalah pola umum, tetapi tidak. Oleh karena itu kebingungan yang diperkenalkan dengan menggunakan konstruksi di luar tujuannya, bersama dengan kemungkinan aliran pengecualian, akan lebih besar daripada penyederhanaan apa pun.
sumber
finally
balok.Iterator
, di mana Anda memang perlu semacam prefetching agarhasNext()
dapat bekerja. Cobalah sendiri.Iterator
adalah bahwa Anda perlu mengambil nilaihasNext()
(karena mengambilnya sering kali merupakan satu-satunya cara untuk mengetahui apakah ada) dan mengembalikannyanext()
seperti OP lakukan.Jawaban:
Secara pribadi, saya akan memikirkan gagasan pemisahan permintaan perintah . Ketika Anda langsung melakukannya, next () memiliki dua tujuan: tujuan yang diiklankan mengambil elemen berikutnya, dan efek samping tersembunyi dari mutasi keadaan internal berikutnya. Jadi, apa yang Anda lakukan adalah melakukan tujuan yang diiklankan di tubuh metode dan kemudian menempel pada efek samping tersembunyi dalam klausa akhirnya, yang tampaknya ... aneh, meskipun tidak 'salah', tepatnya.
Apa yang sebenarnya menjadi intinya adalah bagaimana dimengerti kode itu, saya katakan, dan dalam hal ini jawabannya adalah "meh". Anda "mencoba" pernyataan pengembalian sederhana dan kemudian menjalankan efek samping tersembunyi dalam blok kode yang seharusnya untuk pemulihan kesalahan gagal-aman. Apa yang Anda lakukan adalah 'pintar', dan kode 'pintar' sering mendorong pengelola untuk bergumam, "apa ... oh, saya rasa saya mengerti." Lebih baik bagi orang yang membaca kode Anda untuk bergumam "ya, uh-ya, masuk akal, ya ..."
Jadi, bagaimana jika Anda memisahkan mutasi negara dari panggilan accessor? Saya membayangkan masalah keterbacaan yang Anda khawatirkan menjadi diperdebatkan, tetapi saya tidak tahu bagaimana hal itu memengaruhi abstraksi Anda yang lebih luas. Sesuatu yang perlu dipertimbangkan.
sumber
Murni dari sudut pandang gaya, saya pikir tiga baris ini:
... keduanya lebih jelas dan lebih pendek daripada blok coba / akhirnya. Karena Anda tidak mengharapkan pengecualian untuk dilempar, menggunakan
try
blok hanya akan membingungkan orang.sumber
Itu akan tergantung pada tujuan kode di dalam blok akhirnya. Contoh kanonik adalah menutup aliran setelah membaca / menulis darinya, semacam "pembersihan" yang harus selalu dilakukan. Mengembalikan objek (dalam hal ini sebuah iterator) ke keadaan IMHO yang valid juga dianggap sebagai pembersihan, jadi saya tidak melihat masalah di sini. Jika OTOH yang Anda gunakan
return
segera setelah nilai pengembalian Anda ditemukan, dan menambahkan banyak kode yang tidak terkait di blok akhirnya, maka itu akan mengaburkan tujuan dan membuatnya kurang dimengerti.Saya tidak melihat ada masalah dalam menggunakannya ketika "tidak ada pengecualian yang terlibat". Ini sangat umum untuk digunakan
try...finally
tanpacatch
kode ketika hanya bisa melemparRuntimeException
dan Anda tidak berencana menangani mereka. Kadang-kadang, yang terakhir hanyalah perlindungan, dan Anda tahu untuk logika program Anda bahwa tidak ada pengecualian yang akan dibuang (kondisi klasik "ini seharusnya tidak pernah terjadi").Jebakan: setiap pengecualian yang muncul di dalam
try
blok akan membuatfinally
bock berjalan. Itu dapat menempatkan Anda dalam keadaan tidak konsisten. Jadi, jika pernyataan pengembalian Anda adalah sesuatu seperti:dan kode ini menimbulkan pengecualian,
fetchNext
akan tetap berjalan. OTOH jika Anda mengkodekannya seperti:maka itu tidak akan berjalan. Saya tahu Anda mengasumsikan kode coba tidak pernah dapat memunculkan pengecualian, tetapi untuk kasus yang lebih kompleks dari ini, bagaimana Anda bisa yakin? Jika iterator Anda adalah objek yang berumur panjang, itu akan terus ada bahkan jika kesalahan yang tidak dapat dipulihkan terjadi di utas saat ini, daripada itu akan penting untuk tetap dalam keadaan valid setiap saat. Kalau tidak, itu tidak terlalu penting ...
Kinerja: akan menarik untuk mendekompilasi kode seperti itu untuk melihat cara kerjanya di bawah tenda, tapi saya tidak cukup tahu tentang JVM untuk mengambil tebakan yang bagus ... Pengecualian biasanya "luar biasa", jadi kode yang menangani mereka tidak perlu dioptimalkan untuk kecepatan (maka saran untuk tidak pernah menggunakan pengecualian dalam aliran kontrol normal program Anda), tetapi saya tidak tahu
finally
.sumber
finally
cara ini? Maksud saya, seperti yang Anda katakan, kode tersebut akhirnya memiliki konsekuensi yang tidak diinginkan; lebih buruk lagi, Anda mungkin bahkan tidak memikirkan pengecualian.null
oleh kode kereta; mencoba membaca darinya akan memunculkan eksepsi, mencoba menutupnya difinally
blok akan memunculkan eksepsi lain . Selain itu, ini adalah situasi di mana keadaan perhitungan tidak masalah, Anda ingin menutup aliran apakah pengecualian terjadi. Mungkin ada situasi lain seperti itu juga. Untuk alasan ini saya memperlakukannya sebagai perangkap untuk penggunaan yang valid, daripada merekomendasikan untuk tidak pernah menggunakannya.Pernyataan judul: "... akhirnya klausa untuk melakukan pekerjaan setelah kembali ..." adalah salah. Blok akhirnya terjadi sebelum fungsi kembali. Itulah inti akhirnya pada kenyataannya.
Apa yang Anda pelecehkan di sini adalah urutan evaluasi, di mana nilai selanjutnya disimpan untuk dikembalikan sebelum Anda mengubahnya. Ini bukan praktik umum dan menurut saya salah karena membuat Anda kode tidak berurutan dan karenanya jauh lebih sulit untuk diikuti.
Tujuan akhirnya adalah untuk penanganan pengecualian di mana beberapa konsekuensi harus terjadi terlepas dari pengecualian yang dilemparkan misalnya membersihkan beberapa sumber daya, menutup koneksi DB, menutup file / socket dll.
sumber
Saya akan sangat berhati-hati, karena (secara umum, bukan dalam contoh Anda) bagian dalam percobaan dapat membuang pengecualian, dan jika itu dibuang, tidak ada yang akan dikembalikan dan jika Anda benar-benar berniat untuk mengeksekusi apa yang ada di akhirnya setelah kembali, itu akan mengeksekusi ketika Anda tidak menginginkannya.
Dan cacing lain adalah, bahwa secara umum, beberapa tindakan yang bermanfaat pada akhirnya dapat membuang pengecualian, sehingga Anda dapat mengakhiri dengan metode yang benar-benar berantakan.
Karena itu, seseorang yang membacanya harus memikirkan masalah ini, sehingga lebih sulit untuk dipahami.
sumber