Apakah menggunakan pemrograman buruk ELSE? [Tutup]

18

Saya sering menemukan bug yang disebabkan oleh penggunaan ELSEkonstruk. Contoh utama adalah sesuatu di sepanjang baris:

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

Bagi saya ini menjerit masalah keamanan. Saya tahu bahwa passwordCheck kemungkinan besar adalah boolean, tetapi saya tidak akan menempatkan keamanan aplikasi saya di atasnya. Apa yang akan terjadi jika string, int dll?

Saya biasanya mencoba menghindari penggunaan ELSE, dan sebagai gantinya memilih dua pernyataan IF yang sepenuhnya terpisah untuk menguji apa yang saya harapkan. Hal lain yang diabaikan atau ditangani secara khusus.

Tentunya ini adalah cara yang lebih baik untuk mencegah bug / masalah keamanan memasuki aplikasi Anda.

bagaimana kalian melakukannya?

billy.bob
sumber
3
Apa masalah keamanan untuk Anda? Apa yang dimaksud dengan "kata sandiCheck"? Ada pemeriksaan kata sandi? Perlu ada pemeriksaan kata sandi? Pengguna telah lulus? Pengguna gagal memasukkan kata sandi yang tepat?
LennyProgrammers
20
I know that passwordCheck is likely to be a boolean...Apa maksudmu? Dalam bahasa yang diketik dengan kuat. passwordCheckakan menjadi apa pun yang Anda inginkan.
Bobby
31
Saya pikir praktik indentasi yang buruk menyebabkan lebih banyak kesalahan daripada menggunakan elsepernyataan ...
gablin
15
Ini sepertinya aneh. Pertama, Anda mengeluh tentang jenis pengembalian yang passwordCheck()mungkin karena tidak menjadi boolean (yang mungkin menjadi perhatian wajar), dan kemudian Anda menyalahkannya else? Saya tidak melihat masalah apa elsepenyebabnya.
David Thornley
8
mmm, saya pikir bertanya apakah menggunakan yang lain adalah pemrograman yang buruk adalah pemrograman yang buruk
Muad'Dib

Jawaban:

90

The elseblok harus selalu terdiri dari apa yang Anda inginkan perilaku default untuk menjadi.

Tidak perlu menghindarinya, hanya berhati-hatilah untuk menggunakannya dengan tepat.

Dalam contoh Anda, status defaultnya adalah tidak mengizinkan akses. Sedikit refactoring memberi Anda:

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

yaitu jika pemeriksaan kata sandi berfungsi, biarkan masuk, jika tidak selalu berlaku untuk menampilkan beberapa pesan kesalahan.

Tentu saja Anda dapat menambahkan cek tambahan untuk logika Anda dengan menggunakan pernyataan yang tidak else ifsepenuhnya terpisah if.

RYFN
sumber
2
@ dave.b dalam konteks contoh, saya kira itu akan menjadi masalah keamanan, tetapi jika ini adalah semua tempat di basis kode yang Anda lihat, itu lebih merupakan pertanda siapa pun yang menulisnya membutuhkan sedikit lebih banyak latihan :)
RYFN
9
Bisakah seseorang menguraikan aspek keamanan ini? if (! something) {do a} else {do ​​b} versus if (something) {do b} else {do ​​a} secara logika setara, tidak? Saya mencoba memahami apa bedanya dalam hal keamanan ini?
Chris
11
@ Chris: Saya pikir OP menggunakan bahasa yang diketik dengan lemah. Oleh karena itu passwordCheckbisa apa saja, fe null, yang akan membuat passwordCheck == falseuntuk falsedan akan pengguna memungkinkan untuk masuk karena kesalahan internal.
Bobby
1
@ Chris, pemahaman saya adalah bahwa keadaan default adalah untuk mengizinkan akses, yang tidak selalu disarankan.
RYFN
3
Ya, ini sangat tergantung pada bahasa. Dalam C #, di mana ifmemerlukan a bool, variabel harus ditetapkan secara pasti, semua jalur harus mengembalikan nilai, dll., Saya tidak bisa memikirkan alasan apa pun urutan ifdan elseakan penting selain keterbacaan. Artinya, if(a){b();}{c();}harus setara dengan if(!a){c();{b();}. Di JavaScript, di sisi lain, Anda harus menyadari itu passwordCheckbisa terjadi undefined, dll.
Tim Goodman
57

Tidak, tidak ada yang salah dengan itu ELSE. ELSEbukan yang baru GOTO. Bahkan, menggunakan dua IFs bukannya ELSEdapat menyebabkan beberapa masalah.

Contoh satu:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

Anda melihat copy-paste? Itu hanya menunggu hari ketika Anda mengubah satu dan melupakan yang lain.

Contoh dua:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

Seperti yang Anda lihat, IFitem kedua juga akan dieksekusi untuk item pertama, karena kondisinya sudah berubah. Dalam program dunia nyata, bug semacam itu lebih sulit dikenali.

pengguna281377
sumber
12
Saya setuju dengan ini. Copy-paste sebuah ifpernyataan dan pembalik ekspresi boolean hanya untuk menghindari else- sekarang yang merupakan pemrograman yang buruk! Anda tidak hanya menduplikasi kode ( pemrograman buruk ), Anda juga memperlambat kinerja (jika pemeriksaannya sangat rumit, Anda sekarang melakukannya dua kali - ba-- uh, well, Anda tahu apa yang mereka katakan tentang optimasi prematur saat ini ... pemrograman tidak begitu baik !).
gablin
Jujur saja bahwa Jika cek harus dalam metode sendiri untuk mengembalikan benar / salah
billy.bob
Contoh yang baik, jangan lupa kinerja menggunakan 2 jika cek dibandingkan satu jika dipasangkan dengan yang lain. Saya akan berasumsi dalam sebagian besar bahasa menggunakan if / else akan berkinerja lebih baik daripada menggunakan dua jika pernyataan dengan satu menggunakan tidak pada kondisi.
Chris
1
dave.b: tentu, tapi itu bisa mulai sederhana dan tumbuh perlahan; cek rumit dalam contoh saya ada di sini untuk membuat masalah lebih jelas.
user281377
3
Yup - dan jangan lupa bahwa kondisi dapat memiliki efek samping di sebagian besar bahasa, dan jika ada fungsi dalam kondisi yang Anda benar-benar tidak tahu dengan melihat.
David Thornley
18

Selalu ada ELSE. Jika kamu menulis

if(foo)
  bar();

Anda benar-benar menulis

if(foo)
{
  bar();
}
else
{
   // do nothing
}

Apa pun yang Anda masukkan dalam jalur ELSE adalah tanggung jawab Anda.

Program Lenny
sumber
7
-1. Jawaban ini terlalu konyol. Saya tidak mengatakan itu tidak benar. Itu hanya meleset dari intinya. Apa yang dihasilkan kompilasi dari kode Anda tidak ada hubungannya dengan praktik pengkodean dan bug yang Anda tulis
3
Pertanyaannya adalah "Apakah menggunakan pemrograman buruk ELSE?". Jawaban saya adalah: Anda bisa berpura-pura itu tidak ada, tetapi tidak menghindarinya.
LennyProgrammers
5
bahasa apa ? di Jawa yang lain tidak ada dalam bytecode: P
IAdapter
@ acidzombie24 - praktik yang sangat baik untuk mempertimbangkan apa yang 'lain' dari pertanyaan apa pun. Kadang-kadang hanya - lakukan semua tanda lain di fucntion, tetapi itu menunjukkan Anda telah memikirkannya
Martin Beckett
14

Saya pribadi cenderung menghindari elsesebanyak yang saya bisa, tetapi ini bukan untuk masalah keamanan apa pun .

Saat membaca kode, pernyataan bersarang membuat Anda lebih sulit untuk mengikuti logika, karena Anda perlu mengingat serangkaian kondisi yang akan mengarah ke sana. Untuk alasan ini, saya penggemar berat dari awal keluar:

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

Ini juga berlaku untuk fordan whileloop di mana saya akan menggunakan continuedan breaksetiap kali itu menghindari tingkat lekukan.

Chris Lattner mengatakan itu lebih baik daripada yang saya lakukan dalam Standar Pengkodean LLVM .

Matthieu M.
sumber
Saya setuju. "lain" menciptakan "garpu" mental dan kita manusia adalah makhluk berurutan.
user187291
Fyi, ini disebut kondisi penjaga
CaffGeek
@Chad: ah terima kasih, selalu senang mendapatkan nama untuk hal-hal :)
Matthieu M.
1
+1. Ini adalah satu-satunya jawaban yang dapat saya pertahankan yang memiliki skor> 1. *writes an answer*
Saya tidak mencoba untuk menghindari yang lain seperti itu, tetapi saya pikir saya setuju dengan apa yang Anda tulis. Intinya adalah bahwa hal yang Anda uji harus pengecualian dan bukan kasus normal ( stackoverflow.com/questions/114342/… ). Di sini kegagalan otentikasi adalah pengecualian dan (hanya) yang harus diuji.
hlovdal
5

Kemudian ganti saja,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}
Ahmet Kakıcı
sumber
21
Bagaimana dengan If ((passwordCheck == true) == true)? :-)
Hippo
1
Yah itu gaya saya, untuk meningkatkan keterbacaan. Anda dapat menulis if (! Value) tetapi saya lebih suka if (value! = True)
Ahmet Kakıcı
7
Itu tidak meningkatkan keterbacaan. Itu hanya menambah kebisingan. Lebih buruk lagi adalah programmer yang menulis bersarang jika konstruksi hanya karena mereka takut dengan operator Boolean.
ak2
3
Jika nama variabel deskriptif, tidak ada alasan mengapa: jika (someBooleanValue == true) harus diperlukan. Ini akan lebih baik: jika (validPassword) {...
Mark Freedman
Yah saya baru saja mengganti blok kode dalam pertanyaan. Jika Anda membaca komentar pertama saya, saya mengatakan bahwa saya lebih suka menggunakan if (value! = True) daripada if (! Value). Saya harap Anda bisa melihat perbedaan antara if (value) dan if (! Value). Maksud saya saya tidak menggunakan operator pelengkap. Kalau tidak, Anda benar, benar berarti benar.
Ahmet Kakıcı
3

Seperti Matthieu M., saya lebih suka keluar awal untuk blok lain yang sangat bersarang ... Ini menggambarkan pemrograman defensif dengan baik (jika kondisi buruk, tidak ada gunanya untuk melanjutkan). Banyak orang akan tidak setuju dengan kami, lebih memilih titik keluar yang unik; itu bukan inti dari perdebatan (saya pikir).

Sekarang, saya tentu menggunakan elseketika itu masuk akal, terutama untuk alternatif yang sederhana dan pendek. Seperti yang dikatakan, menduplikasi tes adalah buang-buang waktu (programmer dan CPU), sumber kebingungan dan, kemudian, bug (ketika satu diubah, bukan yang lain).
Kadang-kadang, saya menambahkan komentar pada elsebagian itu, mengingatkan apa kondisinya (terutama jika ifbagian itu panjang, misalnya dalam kode warisan) atau apa alternatifnya.

Perhatikan bahwa beberapa pendukung ekstrem pemrograman fungsional mengusulkan untuk menyingkirkan seluruhnya if, demi pencocokan pola ... Agak terlalu ekstrem untuk selera saya. :-)

PhiLho
sumber
1
sebagai catatan: jika Anda memiliki konstruksi if dalam bahasa fungsional murni Anda, maka Anda benar-benar perlu memiliki yang lain. Setiap ekspresi harus mengembalikan sesuatu!
tokland
2

Tidak ada yang salah dengan menggunakan ELSE. Namun itu dapat menyebabkan kode yang terlalu rumit yang sulit dibaca dan dipahami. Ini mungkin menunjukkan desain yang buruk. Ini tentu menunjukkan kasus penggunaan tambahan yang perlu diuji.

Cobalah untuk menghapus ELSE jika Anda bisa - tetapi jangan paranoid tentang hal itu. Steve McConnell menyebut kode garis lurus ini dalam Kode Lengkap. Yaitu ada jalur yang jelas sederhana melalui kode Anda.

Pendekatan untuk mencoba masalah khusus Anda:

  • gunakan polimorfisme. Pada batas sistem Anda, validasikan kredensial keamanan pengguna. Jika mereka sah maka kembalikan objek sesi - dengan akses ke bagian yang relevan dari sistem atau melemparkan pengecualian. Namun ini bisa membuat sistem Anda lebih kompleks. Jadi Anda memutuskan apa yang lebih mudah dipahami dan dipelihara.

Secara umum - berikut ini dapat membantu mengurangi ELSE dalam kode Anda:

  • persyaratan yang baik dapat mengurangi kebutuhan akan keputusan semacam itu dalam kode. Anda mungkin tidak perlu menerapkan use case (lain) sama sekali.
  • desain yang lebih jelas. Kohesi maksimum dan meminimalkan kopling. Ini memastikan komponen tidak menduplikasi keputusan yang dibuat dalam komponen lain.
  • penanganan pengecualian untuk mengelola kasus kesalahan.
  • polimorfisme (lihat contoh di atas).
  • beralih pernyataan - ini adalah ELSE yang dimuliakan tetapi lebih baik dalam situasi tertentu.
Conor
sumber
2
Saya juga akan memasukkan "Pindahkan cek boolean kompleks ke fungsi terpisah".
gablin
2

Anggapan Anda tentang kode yang menjadi kebocoran keamanan mungkin atau mungkin tidak benar tergantung pada bahasa yang Anda gunakan. Dalam kode C itu bisa menjadi masalah (terutama karena dalam C a boolean hanyalah sebuah int yang bukan nol atau nol) - tetapi dalam bahasa yang diketik paling kuat (yaitu pengecekan tipe runtime) jika passwordCheckvariabel dideklarasikan sebagai boolean, tidak ada cara untuk menetapkan sesuatu yang lain untuk itu. Faktanya, segala sesuatu dalam ifpredikat harus diselesaikan ke boolean, apakah Anda menggunakan operator boolean atau hanya menggunakan nilai. Jika Anda berhasil membuat jenis objek lain terikat ke passwordCheckruntime akan membuang beberapa jenis pengecualian pemain ilegal.

Sederhana jika / jika konstruksi lebih mudah dibaca daripada jika / jika konstruksi - dan kurang rentan terhadap masalah yang tidak disengaja jika seseorang mencoba untuk membalikkan konstruksi. Mari kita ambil contoh yang sama sebentar:

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

Arti dari klausa yang saling eksklusif yang ingin Anda jalankan di atas hilang. Itulah yang konstruksi if / else sampaikan. Dua cabang eksekusi yang saling eksklusif, di mana salah satunya akan selalu berjalan. Ini adalah bagian penting dari keamanan - memastikan tidak ada cara untuk letThemInsetelah Anda menelepon denyAccess.

Untuk tujuan kejelasan kode, dan untuk tujuan memastikan bagian kritis paling terlindungi, mereka harus berada di dalam klausa utama ( ifbagian). Perilaku default yang tidak patuh harus dalam klausa alternatif ( elsebagian). Sebagai contoh:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

CATATAN: dalam bekerja dengan bahasa yang berbeda, saya telah mengembangkan habbit pengkodean yang membantu menghindari pertanyaan "bagaimana jika itu sebuah string?" Pada dasarnya, ini adalah untuk menempatkan konstanta pertama dalam ekspresi boolean. Misalnya, alih-alih memeriksa, passwordCheck == falsesaya memeriksa false == passwordCheck. Ini juga menghindari masalah penugasan tak disengaja yang mungkin terjadi di C ++. Menggunakan pendekatan ini, kompiler akan mengeluh jika saya mengetik =bukan ==. Dalam bahasa seperti Java dan C #, kompiler akan memperlakukan tugas dalam klausa if sebagai kesalahan, tetapi C ++ akan menerimanya dengan senang hati. Itu sebabnya saya juga cenderung melakukan pemeriksaan nol dengan yang nullpertama.

Jika Anda secara rutin mengubah bahasa, menempatkan konstanta terlebih dahulu sangat membantu. Namun, di tim saya itu berlawanan dengan standar pengkodean dan kompiler menangkap masalah itu pula. Ini bisa menjadi kebiasaan yang sulit untuk dilanggar.

Berin Loritsch
sumber
1

Mengatakan bahwa menggunakan elsesaat pemrograman buruk adalah seperti mengatakan bahwa menggunakan otherwisesaat berbicara itu buruk.

Tentu, keduanya dapat digunakan dengan cara yang buruk, tetapi itu tidak berarti mereka harus dihindari hanya karena Anda membuat kesalahan yang kebetulan termasuk mereka. Saya tidak akan terkejut jika banyak bug bergantung pada defaultkasus yang hilang dalam sebuah switchpernyataan.

Gablin
sumber
1

Anggap Elsesebagai daftar putih aliran aplikasi Anda. Anda memeriksa kondisi yang HARUS memungkinkan aliran aplikasi untuk melanjutkan, dan jika ini tidak terpenuhi, maka Anda Elsedieksekusi untuk menyelesaikan masalah, menghentikan eksekusi aplikasi, atau yang serupa.

Else itu sendiri tidak buruk, tetapi jika Anda menggunakannya dengan buruk, Anda dapat melihat efek yang tidak diinginkan.

Juga, sehubungan dengan pernyataan Anda tentang

"Saya tahu kata sandi yang mungkin menjadi boolean, tetapi saya tidak akan menempatkan keamanan aplikasi saya di atasnya."

Untuk metode yang Anda kembangkan, SELALU mengembalikan satu tipe data. Meskipun PHP Core dipenuhi dengan kode yang mengembalikan dua atau lebih tipe data, ini adalah praktik yang buruk karena membuat dugaan panggilan fungsi. Jika Anda harus mengembalikan lebih dari satu tipe data, pertimbangkan untuk melempar pengecualian (saya menemukan ini sering menjadi alasan saya ingin mengembalikan tipe data lain - ada yang tidak beres, sangat salah), atau pertimbangkan untuk menyusun kembali kode Anda sehingga Anda dapat mengembalikan hanya satu tipe data.

Mendambakan
sumber
Saya tidak tahu bahwa ada bahasa yang mengembalikan lebih dari satu tipe data! Apakah Anda mengacu pada fungsi polimorfik? Saya percaya setiap kali saya membebani suatu fungsi, ia selalu mengembalikan tipe data yang sama, meskipun mungkin perlu argumen yang berbeda.
Michael K
1
Dalam bahasa yang diketik secara longgar, dan terutama dalam PHP, fungsi dapat mengembalikan lebih dari satu tipe data. misal: stristr- "Mengembalikan substrat yang cocok. Jika jarum tidak ditemukan, kembalikan FALSE"
Craige
@Michael, Fungsi PHP dapat mengembalikan apa pun yang Anda inginkan. Tidak ada batasan pada tipe data. Fungsi saya yang paling rumit mengembalikan true / false / null, tetapi tidak ada (kecuali akal sehat) yang menghentikan Anda menulis fungsi yang mengembalikan true / integer / null / string / float / array.
TRiG
Menarik. Saya tidak pernah bekerja dengan PHP. Terima kasih atas penjelasannya - mungkin membantu saya di masa depan! semacam Javascript lalu?
Michael K
@Michael - Cukup mirip dengan Javascript.
Craige
1

Pertama-tama. LOL! TIDAK ADA ALASAN untuk menghindari yang lain, sama sekali. Ini BUKAN praktik yang buruk dengan cara apapun, bentuk atau bentuk.

Jika ada kode harus

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

Tidak ada dua seandainya ada dan tidak ada yang lain. Ini adalah apa yang saya lakukan di semua aplikasi saya kecuali satu di mana saya melemparkan pengecualian. Pengecualian tertangkap dalam fungsi saya yang memeriksa url untuk halaman yang tepat untuk ditampilkan (atau sebagai alternatif saya dapat menempatkan catch / check di fungsi kesalahan asp.net). Mencetak halaman umum yang mengatakan tidak mengotorisasi atau pesan apa pun yang saya gunakan dalam pengecualian (saya selalu memeriksa jenis pengecualian dan mengatur kode status http).

-Edit- seperti yang ditunjukkan pada contoh ammoQ dua jika tidak masuk akal. Sungguh yang lain sama baiknya atau lebih baik daripada jika. Jika ada yang harus dihindari (walaupun saya pribadi tidak. Tapi saya menggunakan kembali dan banyak istirahat) seperti yang dikatakan lebih banyak jalur kode meningkatkan kemungkinan bug. Lihat Kompleksitas Siklomatik

-Edit 2- Jika Anda khawatir tentang penggunaan if / else. Saya juga akan mencatat bahwa preferensi saya adalah meletakkan blok kode terpendek di atas seperti

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

Sebaliknya

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}

sumber
0

Saya suka mengatur default sebelum kondisi ketika saya bisa. Saya merasa sedikit lebih mudah membaca dan sedikit lebih eksplisit, tetapi ini hanya preferensi. Saya memiliki kecenderungan untuk mencoba dan menghindari kondisi negatif dalam kode saya. Saya bukan penggemar berat untuk memeriksa! Foo atau false == foo dan saya merasa seperti itu adalah sejenis persyaratan bersyarat negatif.

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

dari pada ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

Blok kode sebelumnya sepertinya sedikit lebih mudah bagi saya untuk membaca. Tampaknya lebih alami bagi saya untuk memiliki semacam paranoia skeptis tentang kode saya. Pengaturan default terlepas dari kondisi apa pun membuat saya merasa nyaman: P


sumber
0

Saya berpendapat bahwa menggunakan logika percabangan dalam bentuk apa pun harus dihindari sebanyak mungkin. Meskipun tidak ada yang salah dengan ELSE atau IF, ada banyak cara untuk menulis kode untuk meminimalkan kebutuhan untuk menggunakan logika percabangan apa pun. Saya tidak mengatakan logika percabangan dapat sepenuhnya dihilangkan - itu akan diperlukan di beberapa tempat - tetapi dimungkinkan untuk refactor kode untuk menghilangkan sebagian yang baik dari itu. Dalam kebanyakan kasus ini akan meningkatkan kejelasan dan akurasi kode Anda.

Sebagai salah satu contoh, operator ternary juga biasanya merupakan kandidat yang baik:

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

Menggunakan pendekatan ternary:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

Operator ternary menggeser cabang ke kanan dengan cara yang baik.

Mario T. Lanza
sumber