Apakah pernyataan try-catch bersarang masih bau kode jika bersarang dalam satu lingkaran?

9

Saya telah mendengar bahwa pernyataan try-catch bersarang sering kali merupakan bau kode, jadi saya bertanya-tanya apakah situasi ini merupakan pengecualian. Jika tidak, apa cara yang baik untuk refactor?

Kode saya terlihat seperti berikut:

try{
    X x = blah;
    otherStuff;
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
}
catch(Exceptions2 e2){
    ...
}

Saya menggunakan di Exceptionssini untuk menunjukkan tangkapan multi.

e2digunakan untuk menangkap pengecualian yang dilontarkan dengan menginisialisasi xdan melakukan otherStuff. Idealnya, saya hanya akan memiliki surround try-catch saya hanya dua baris, tetapi saya menggunakan xdalam loop saya dan ingin menghindari peringatan "tugas yang tidak terpakai" yang disebabkan oleh inisialisasi ke nol di luar try-catch.

e1tidak multi-tertangkap dengan e2karena saya ingin memberikan pengguna dengan informasi tentang detail iterasi dan dengan demikian ingin menangkap blok di dalam loop.

Weasemunk
sumber

Jawaban:

7

Jika e2tangkapannya, seperti yang Anda katakan, hanya untuk menangkap kesalahan dalam menginisialisasi xdan melakukan otherStuffAnda bisa mengekstraknya ke metode terpisah. Ini juga memisahkan logika dengan baik dan memungkinkan Anda untuk berpotensi memberikan nama yang bermakna otherStuff.

public X Foo() {
    try{
        X x = blah;
        otherStuff;

        return x;
    }
    catch(Exceptions2 e2){
        ...
    }
}

public void Bar() {    
    X x = Foo();
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
    }
}
Martin Brenden
sumber
Jawaban ini masuk akal untuk situasi saya, karena memungkinkan saya untuk secara fungsional memisahkan kesalahan saya daripada memberikan alias untuk tangkapan internal, yang masih akan tetap terjadi
Weasemunk
8

Kecuali jika Anda bermaksud untuk memproses seluruh loop batin apakah pengecualian terjadi atau tidak, kode Anda pada dasarnya setara dengan

try{
    X x = blah;
    otherStuff;
    for (...){ 
       f(x)
    }
}
catch(Exceptions1 e1){
    ...
}
catch(Exceptions2 e2){
    ...
}

yang tidak perlu bersarang.

Jika Anda masih membutuhkan penanganan pengecualian dalam, refactor pengecualian dalam dan metode penutupnya menjadi metode baru. Ini juga memiliki manfaat tambahan memaksa Anda berpikir tentang cara lain untuk memproses loop batin; pengecualian bisa menjadi sangat mahal dalam hal kinerja, jika banyak dari mereka terlempar.

Robert Harvey
sumber
Saya mengerti apa yang kamu maksud. Saya telah menambahkan beberapa detail untuk lebih menentukan situasi saya. Di loop dalam, saya ingin menggunakan nilai y, yang tergantung pada iterasi i (saya sebenarnya menggunakan a untuk masing-masing, tetapi ini harus mendapatkan poin saya di seberang). Tangkapan dalam saya akan mengatakan sesuatu tentang nilai y ketika ditangkap, sedangkan tangkapan luar tidak tergantung pada loop. Masih bisakah saya menyiasati sarang, atau apakah saya terkunci oleh keputusan saya untuk memberikan info yang bergantung pada loop?
Weasemunk
Saya masih berpikir saya akan memperbaiki loop dalam ke metode sendiri, di mana Anda dapat melakukan apa pun yang Anda inginkan dengan pengecualian.
Robert Harvey
Terima kasih, saya menggabungkan jawaban Anda dengan @Martin Brendan's untuk menghasilkan solusi bersih.
Weasemunk
3

Saya memiliki beberapa pengamatan pada pertanyaan Anda:

  1. Dalam contoh yang Anda berikan, Anda tidak perlu pengecualian sama sekali. Anda hanya perlu memeriksa string y, mencatat kesalahan apa pun, dan keluar dari loop. Tidak ada yang luar biasa tentang Anda yang tidak valid dan perlu dicatat. Fakta Anda menggunakan pengecualian dalam hal ini adalah indikasi bau kode.

  2. Pengecualian multi-tangkapan tidak benar-benar dimaksudkan untuk digunakan untuk membungkus petak besar kode dan menangkap apa pun yang mungkin salah. Mereka dimaksudkan untuk membantu membedakan antara pengecualian dalam kasus di mana satu bagian kode dapat menghasilkan serangkaian pengecualian yang berbeda. Karena Anda menggunakan pengecualian mutli-catch sebagai semacam pendekatan "tangkap semua", ini mengindikasikan adanya bau kode.

  3. Tidak masuk akal untuk mencoba menangkap dalam loop kecuali jika Anda berencana untuk melanjutkan beralih melalui sisa loop bahkan jika pengecualian ditemukan pada salah satu elemen. Kode sampel Anda menunjukkan Anda berencana untuk keluar dari loop jika ada satu item yang luar biasa.

Saya pikir Anda mungkin lebih baik dengan sesuatu seperti:

try
{
     var x = blah;
     DoStuff();
     foreach(var i in icollection) // i is an int
     {
          var y = SomethingDependentOnI(i);

          // check y for explicit exceptional states
          if (IsSomethingWrongWithY(y))
               throw new Exception1(y);
     }
}
catch (Exception1 e1)
{
}
catch (Exception2 e2)
{
}

Ini jauh lebih jelas, tetapi masih menderita masalah yang dijelaskan di atas.

Harga Jones
sumber
Terima kasih atas komentarnya. 1 + 2 tidak benar-benar relevan sehubungan dengan kode aktual, tapi saya pikir perspektif Anda tentang melempar ke dalam lingkaran masuk akal
Weasemunk