Gunakan yang lain setelah pengecualian (atau tidak)

9

Pertimbangkan sedikit kode ini:

if (x == 1)
{
  throw "no good; aborting" ;
}

[... more code ...]

Sekarang pertimbangkan kode ini:

if (x == 1)
{
  throw "no good; aborting" ;
}
else
{
  [... more code ...]
}

Kedua kasing bekerja dengan cara yang persis sama. Kasing pertama memiliki keuntungan bahwa Anda tidak perlu "membungkus" sisa kode dalam sebuah else. Yang kedua memiliki keuntungan mengikuti praktik memiliki secara eksplisit memiliki elseuntuk setiap if.

Adakah yang bisa memberikan argumen yang solid yang mendukung salah satu dari yang lain?

rlandster
sumber
5
Praktek yang Anda kutip dalam mendukung eksplisit elsetampaknya palsu. Cukup sering, tidak ada yang bisa dimasukkan ke dalam elseblok kecuali Anda membungkuk ke belakang.
Konsistensi? Izinkan kekokohan dalam menghadapi perubahan kode? Keterbacaan?
Thomas Eding
1
Ehhhh, saya bukan penggemar ide bahwa setiap ifkebutuhan else. Pemrogram terakhir yang bekerja pada basis kode kami mengikuti dengan kaku ( well, kadang-kadang ... itu semacam skizofrenia ). Akibatnya, kami memiliki banyak else { /* do nothing */ }blok yang sama sekali tidak berarti yang mengotori kode ...
KChaloux
4
"Yang lain untuk setiap jika" tampaknya seperti proklamasi aneh yang dikeluarkan oleh arsitek cloud atas nama konsistensi (bodoh). Saya melihat tidak ada untungnya mengikuti latihan itu dan bahkan belum pernah mendengarnya di mana pun.
Erik Dietrich
Itu hal yang berlebihan. Jika Anda bekerja dengan .NET stack, daripada menginstal ReSharper dan itu akan mengingatkan Anda untuk menghapus semua pernyataan lain yang mubazir.
CodeART

Jawaban:

16

Anda tidak harus menambahkan elsesetelah ifcabang yang memutus aliran kontrol tanpa syarat , seperti yang mengandung a throwatau a return. Ini akan meningkatkan keterbacaan program Anda dengan menghapus tingkat sarang yang tidak perlu yang diperkenalkan oleh elsecabang.

Apa yang terlihat kurang lebih baik dengan satu throwmenjadi benar-benar jelek ketika beberapa lemparan berturut-turut dilanggar:

void myMethod(int arg1, int arg2, int arg3) {
    // This is demonstrably ugly - do not code like that!
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    } else {
        if (!isValid(arg2)) {
            throw new ArgumentException("arg2 is invalid");
        } else {
            if (!isValid(arg3)) {
                throw new ArgumentException("arg3 is invalid");
            } else {
                // The useful code starts here
            }
        }
    }
}

Cuplikan ini melakukan hal yang sama, tetapi terlihat jauh lebih baik:

void myMethod(int arg1, int arg2, int arg3) {
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    }
    if (!isValid(arg2)) {
        throw new ArgumentException("arg2 is invalid");
    }
    if (!isValid(arg3)) {
        throw new ArgumentException("arg3 is invalid");
    }
    // The useful code starts here
}
dasblinkenlight
sumber
+1 Benar. Kasus OP kedua memaksa Anda untuk membaca dengan seksama, lalu meninggalkan Anda dengan WTF. Tapi ... selalu berusaha mempersingkat metode. Pengembalian di tengah metode 200 baris juga buruk.
Tulains Córdova
1
Agar adil, jika Anda hanya menggunakan pengulangan jika Anda dapat melakukannya else if.
Guvante
2
@ Guvante: Setiap iftes untuk satu kondisi dan mengatasinya jika kondisinya benar, dan kecuali ada beberapa hal alternatif yang harus terjadi jika kondisinya salah, else ifs tidak perlu. Kami memiliki istilah di kantor saya untuk kode seperti cuplikan pertama dasblinkenlight: " mesin pachinko ."
Blrfl
@Blrfl mesin pachinko hah, analogi sempurna +1
Jimmy Hoffa
@ Blrfl: Saya mereferensikan bahwa if yang diulang adalah contoh buruk dari terlalu banyak bersarang. Lagipula kamu seharusnya tidak bersarang berulang. Saya setuju bahwa secara umum kecuali Anda berbicara tentang sejumlah kecil kode, tidak ada alasan untuk memasukkan else.
Guvante
5

Saya akan menyebut praktik "eksplisit lain" yang Anda sebut sebagai anti-pola, karena mengaburkan fakta bahwa tidak ada kode kasus khusus sebagai lain untuk if Anda.

Keterbacaan / pemeliharaan umumnya ditingkatkan ketika Anda sebagian besar tidak memiliki apa pun kecuali konstruksi aliran kode yang diperlukan, dan Anda menguranginya. Ini berarti redundant elses dan if's yang akan menambah ruang lingkup ke seluruh fungsi membuat mengikuti dan mempertahankannya lebih sulit.

Katakan misalnya Anda memiliki fungsi ini:

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
    }
    else
    {
        oblogonToConfigure.Color = _validColors[0];
        oblogonToConfigure.ColorIndex = 0;
    }
}

Sekarang persyaratan datang bahwa selama konfigurasi Anda juga harus menentukan tipe / tipe indeks oblogon, ada beberapa lingkup di mana seseorang dapat menempatkan kode itu dan berakhir dengan kode yang tidak valid yaitu

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validOblogons.Contains(oblogonToConfigure.Type))
    {
        oblogonToConfigure.Type = _validOblogons[0];
        oblogonToConfigure.TypeIndex = 0;
        if (_validColors.Contains(oblogonToConfigure.Color))
        {
            oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
        }
        else
        {
            oblogonToConfigure.Color = _validColors[0];
            oblogonToConfigure.ColorIndex = 0;
        }
    }
    else
    {
        oblogonToConfigure.TypeIndex = _validOblogons.IndexOf(oblogonToConfigure.Type);
    }
}

Bandingkan ini dengan jika kode asli ditulis dengan konstruksi aliran kontrol minimal yang diperlukan dan diminimalkan pada saat itu.

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.Color = _validColors[0];
    }

    oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
}

Sekarang akan jauh lebih sulit untuk secara tidak sengaja menempatkan sesuatu dalam ruang lingkup yang salah atau berakhir pada kembung yang menyebabkan duplikasi dalam pertumbuhan jangka panjang dan pemeliharaan fungsi ini. Plus jelas apa yang mungkin mengalir melalui fungsi ini sehingga keterbacaan ditingkatkan.

Saya tahu, contohnya sedikit dibuat-buat, tapi saya sudah berkali-kali melihatnya

SomeFunction()
{
    if (isvalid)
    {
        /* ENTIRE FUNCTION */
    }
    /* Nothing should go here but something does on accident, and an invalid scenario is created. */
}

Jadi memformalkan aturan-aturan tentang konstruksi aliran kontrol saya pikir dapat membantu orang mengembangkan intuisi yang diperlukan untuk mencium sesuatu ketika mereka mulai menulis kode seperti itu. Kemudian mereka akan mulai menulis ..

SomeFunction()
{
    if (!isvalid)
    {
        /* Nothing should go here, and it's so small no one will likely accidentally put something here */
        return;
    }

    /* ENTIRE FUNCTION */
}
Jimmy Hoffa
sumber