Kalau-kalau tangga yang seharusnya menangkap semua kondisi - haruskah klausa akhir yang berlebihan ditambahkan?

10

Ini adalah hal yang saya lakukan banyak akhir-akhir ini.

Contoh:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Seringkali jika / jika tangga secara signifikan lebih rumit daripada ini ...

Lihat klausa terakhir? Itu berlebihan. Tangga pada akhirnya seharusnya menangkap semua kondisi yang memungkinkan. Jadi bisa ditulis ulang seperti itu:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Ini adalah bagaimana saya biasa menulis kode, tapi saya tidak suka gaya ini. Keluhan saya adalah kondisi di mana bagian terakhir dari kode akan dieksekusi tidak jelas dari kode. Saya kemudian mulai menulis kondisi ini secara eksplisit untuk membuatnya lebih jelas.

Namun:

  • Secara eksplisit menulis kondisi akhir yang lengkap adalah ide saya sendiri, dan saya memiliki pengalaman buruk dengan ide-ide saya sendiri - biasanya orang-orang meneriaki saya tentang betapa mengerikannya apa yang saya lakukan - dan (kadang-kadang banyak) kemudian saya mengetahui bahwa itu memang suboptimal;
  • Satu petunjuk mengapa ini mungkin ide yang buruk: Tidak berlaku untuk Javascript, tetapi dalam bahasa lain, kompiler cenderung mengeluarkan peringatan atau bahkan kesalahan tentang kontrol yang mencapai akhir fungsi. Memberitahu melakukan sesuatu seperti itu mungkin tidak terlalu populer atau saya salah melakukannya.
    • Keluhan kompiler membuat saya kadang-kadang menulis kondisi terakhir dalam komentar, tapi saya kira melakukan itu mengerikan karena komentar, tidak seperti kode, tidak berpengaruh pada semantik program yang sebenarnya:
    } else { // i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }

Apakah saya melewatkan sesuatu? Atau apakah boleh melakukan apa yang saya uraikan atau itu ide yang buruk?

gaazkam
sumber
+1 untuk jawaban eksekutif @ Christophe bahwa redundansi dalam kode meningkatkan kemungkinan cacat. Ada juga masalah efisiensi yang jauh lebih rendah . Untuk memperluas itu, mengenai contoh kode yang dimaksud, kita dapat menulis serangkaian if-else-if's sebagai hanya memisahkan if's tanpa elses, tetapi kita biasanya tidak akan karena if-else memberi pembaca gagasan alternatif eksklusif. daripada serangkaian kondisi independen (yang juga akan kurang efisien, karena itu mengatakan bahwa semua kondisi harus dievaluasi bahkan setelah satu pertandingan).
Erik Eidt
@ErikEidt> karena itu mengatakan bahwa semua kondisi harus dievaluasi bahkan setelah satu kecocokan +1 kecuali jika Anda kembali dari fungsi atau "melanggar" dari satu lingkaran (yang tidak terjadi dalam contoh di atas).
Darek Nędza
1
+1, pertanyaan yang bagus. Selain itu mungkin menarik bagi Anda bahwa di hadapan NaNs contohnya tidak lengkap.
monocell

Jawaban:

6

Kedua pendekatan itu valid. Tapi mari kita lihat lebih dekat pro dan kontra.

Untuk- ifrantai dengan kondisi sepele seperti di sini, itu tidak masalah:

  • dengan final else, jelas bagi pembaca untuk mengetahui dalam kondisi apa yang dipicu lainnya;
  • dengan final else if, jelas bagi pembaca bahwa tidak ada tambahan elseyang diperlukan karena Anda membahas semuanya.

Namun, ada banyak ifrantai yang bergantung pada kondisi yang lebih kompleks, menggabungkan keadaan beberapa variabel, mungkin dengan ekspresi logis yang kompleks. Dalam hal ini kurang jelas. Dan inilah konsekuensi dari masing-masing gaya:

  • final else: Anda yakin bahwa salah satu cabang diambil. Jika kebetulan Anda lupa satu kasus, maka akan melewati cabang terakhir itu, jadi selama debugging, jika cabang terakhir dipilih dan Anda mengharapkan sesuatu yang lain, Anda akan segera mencari tahu.
  • final else if: Anda perlu menurunkan kondisi redundan ke kode, dan ini menciptakan sumber kesalahan potensial dengan reisk tidak mencakup semua kasus. Selain itu, jika Anda melewatkan sebuah kasus, tidak ada yang akan dilakukan dan mungkin akan lebih sulit untuk mengetahui bahwa ada sesuatu yang hilang (misalnya jika beberapa variabel yang Anda harapkan akan diset, simpan nilainya dari iterasi sebelumnya).

Jadi kondisi redundan terakhir adalah sumber risiko. Inilah sebabnya saya lebih memilih untuk pergi ke final else.

Sunting: coding keandalan tinggi

Jika Anda mengembangkan dengan keandalan tinggi dalam pikiran, Anda mungkin tertarik pada varian lain: menyelesaikan final eksplisit berlebihan Anda else ifdengan final elseuntuk menangkap situasi yang tidak terduga.

Ini adalah kode defensif. Ini direkomendasikan oleh beberapa spesifikasi keamanan seperti SEI CERT atau MISRA . Beberapa alat analisis statis bahkan menerapkan ini sebagai aturan yang diperiksa secara sistematis (ini bisa menjelaskan peringatan kompiler Anda).

Christophe
sumber
7
Bagaimana jika setelah kondisi redundan terakhir saya menambahkan yang lain yang selalu melempar pengecualian yang mengatakan "Anda tidak seharusnya menghubungi saya!" - Apakah ini akan meringankan beberapa masalah dari pendekatan saya?
gaazkam
3
@gaazkam ya, ini adalah gaya pengkodean yang sangat defensif. Jadi Anda masih perlu menghitung kondisi akhir, tetapi untuk rantai rawan kesalahan yang kompleks, Anda setidaknya akan mengetahuinya dengan cepat. Satu-satunya masalah yang saya lihat dengan varian ini adalah sedikit berlebihan untuk kasus yang jelas.
Christophe
@gaazkam Saya telah mengedit jawaban saya untuk membahas juga ide tambahan Anda yang disebutkan dalam komentar Anda
Christophe
1
@gaazkam Melempar pengecualian itu bagus. Jika Anda melakukannya, jangan lupa untuk memasukkan nilai tak terduga dalam pesan. Mungkin sulit untuk mereproduksi dan nilai tak terduga dapat memberikan petunjuk tentang sumber masalah.
Martin Maat
1
Pilihan lain adalah dengan menempatkan assertdi final else if. Panduan gaya Anda mungkin berbeda-beda apakah itu Ide Bagus. (Kondisi suatu assertseharusnya tidak boleh salah, kecuali jika programmer mengacaukannya. Jadi ini setidaknya menggunakan fitur untuk tujuan yang dimaksudkan. Tetapi begitu banyak orang menyalahgunakannya sehingga banyak toko telah melarangnya secara langsung.)
Kevin
5

Sesuatu yang hilang dari jawaban sejauh ini adalah masalah kegagalan macam apa yang kurang berbahaya.

Jika logika Anda bagus, itu tidak masalah apa yang Anda lakukan, kasus penting adalah apa yang terjadi jika Anda memiliki bug.

Anda menghilangkan syarat akhir: Opsi akhir dijalankan bahkan jika itu bukan hal yang benar untuk dilakukan.

Anda cukup menambahkan syarat akhir: Ini tidak mengeksekusi opsi apa pun, tergantung pada situasi ini mungkin berarti sesuatu gagal untuk ditampilkan (bahaya rendah), atau itu mungkin berarti pengecualian referensi nol di beberapa titik kemudian (yang bisa menjadi debugging rasa sakit.)

Anda menambahkan syarat akhir dan pengecualian: Itu melempar.

Anda harus memutuskan opsi mana yang terbaik. Dalam kode pengembangan saya menganggap ini sebagai no-brainer - ambil kasus ketiga. Namun, saya mungkin akan mengatur circle.src ke gambar kesalahan dan circle.alt ke pesan kesalahan sebelum melempar - jika seseorang memutuskan untuk mematikan pernyataan nanti ini membuatnya gagal tanpa bahaya.

Hal lain yang perlu dipertimbangkan - apa saja opsi pemulihan Anda? Terkadang Anda tidak memiliki jalur pemulihan. Apa yang saya pikirkan adalah contoh utama dari ini adalah peluncuran pertama roket Ariane V. Terjadi kesalahan yang tidak tertangkap / 0 (sebenarnya divisi meluap) yang mengakibatkan penghancuran booster. Pada kenyataannya, kode yang mogok tidak berfungsi apa pun pada saat itu, kode itu telah diperdebatkan seketika penguat strap-on menyala. Begitu mereka menerangi orbit atau boom, Anda melakukan yang terbaik yang Anda bisa, kesalahan tidak bisa diizinkan. (Jika roket tersesat karena ini, petugas keamanan jangkauan memutar kuncinya.)

Loren Pechtel
sumber
4

Yang saya rekomendasikan adalah menggunakan assertpernyataan di final Anda yang lain, dalam salah satu dari kedua gaya ini:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        assert i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Atau pernyataan kode mati:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    } else {
        assert False, "Unreachable code"
    }
}

Alat cakupan kode sering dapat dikonfigurasi untuk mengabaikan kode seperti "menegaskan Salah" dari laporan cakupan.


Dengan menempatkan kondisi dalam pernyataan, Anda secara efektif mendokumentasikan kondisi cabang secara eksplisit, tetapi tidak seperti komentar, kondisi pernyataan dapat benar-benar diperiksa dan akan gagal jika Anda membiarkan pernyataan diaktifkan selama pengembangan atau pada produksi (saya biasanya merekomendasikan menjaga pernyataan diaktifkan dalam produksi jika mereka tidak terlalu mempengaruhi kinerja).

Lie Ryan
sumber
1
Saya tidak suka opsi pertama Anda, yang kedua jauh lebih jelas bahwa itu adalah kasus yang seharusnya tidak mungkin.
Loren Pechtel
0

Saya mendefinisikan makro "menegaskan" yang mengevaluasi suatu kondisi, dan dalam membangun debug jatuh ke debugger.

Jadi, jika saya 100 persen yakin bahwa satu dari tiga syarat itu pasti benar, saya menulis

If condition 1 ...
Else if condition 2 .,,
Else if asserted (condition3) ...

Itu membuatnya cukup jelas bahwa satu syarat akan benar, dan tidak ada cabang tambahan untuk penegasan diperlukan.

gnasher729
sumber
-2

Saya sarankan menghindari yang lain sama sekali . Gunakan ifuntuk menyatakan apa yang seharusnya ditangani oleh blok kode, dan akhiri blok dengan keluar dari fungsinya.

Ini menghasilkan kode yang sangat jelas:

setCircle(circle, i, { current })
{
    if (i == current)
    {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
        return
    }
    if (i < current)
    {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
        return
    }
    if (i > current)
    {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
        return
    }
    throw new Exception("Condition not handled.");
}

Final iftentu saja berlebihan ... hari ini. Ini bisa menjadi pemikiran yang sangat penting jika / ketika beberapa pengembang masa depan mengatur ulang blok. Jadi sangat membantu untuk membiarkannya di sana.

John Wu
sumber
1
Sebenarnya sangat tidak jelas karena sekarang saya perlu mempertimbangkan apakah mengeksekusi cabang pertama dapat mengubah hasil tes kedua. Ditambah beberapa pengembalian yang tidak berguna. Plus kemungkinan tidak ada kondisi yang benar.
gnasher729
Saya benar-benar menulis jika pernyataan seperti ini ketika saya secara sengaja mengharapkan beberapa kasus dapat diambil. Ini sangat berguna dalam bahasa dengan pernyataan pergantian yang tidak memungkinkan jatuh. Saya pikir jika pilihannya saling eksklusif itu harus ditulis sehingga jelas bahwa kasus-kasus itu saling eksklusif (menggunakan yang lain). Dan jika tidak ditulis seperti itu maka implikasinya adalah bahwa kasus-kasus tersebut tidak eksklusif satu sama lain (gagal mungkin).
Jerry Jeremiah
@ gnasher729 Saya sangat mengerti reaksi Anda. Saya kenal beberapa orang yang sangat pintar yang bermasalah dengan "menghindari yang lain" dan "kembali lebih awal" ... pada awalnya. Saya mendorong Anda untuk meluangkan waktu untuk membiasakan diri dengan ide itu dan melihatnya - karena ide-ide ini, secara objektif dan terukur, mengurangi kompleksitas. Pengembalian awal sebenarnya membahas poin pertama Anda (perlu mempertimbangkan apakah cabang mungkin mengubah tes lain). Dan "kemungkinan bahwa tidak ada kondisi yang benar" masih ada terlepas; dengan gaya ini, Anda mendapatkan pengecualian yang jelas daripada cacat logika tersembunyi.
John Wu
Tetapi bukankah kompleksitas siklomatik dari kedua gaya itu persis sama? yaitu sama dengan jumlah kondisi
gaazkam