Pengembang menegaskan jika pernyataan tidak boleh meniadakan kondisi, dan harus selalu memiliki blok lain

169

Saya memiliki seorang kenalan, pengembang yang lebih berpengalaman daripada saya. Kami berbicara tentang praktik pemrograman dan saya terkejut dengan pendekatannya pada pernyataan 'jika'. Dia bersikeras pada beberapa praktik mengenai jika pernyataan yang saya temukan agak aneh.

Pertama , pernyataan if harus diikuti oleh pernyataan lain, apakah ada sesuatu untuk dimasukkan ke dalamnya atau tidak. Yang mengarah ke kode terlihat seperti ini:

if(condition) 
{
    doStuff();
    return whatever;
}
else
{
}

Kedua , lebih baik untuk menguji nilai-nilai yang benar daripada yang salah. Itu berarti bahwa lebih baik untuk menguji variabel 'doorClosed' daripada variabel '! DoorOpened'

Argumennya adalah bahwa hal itu memperjelas apa yang dilakukan kode.

Yang agak membingungkan saya, karena kombinasi dari dua aturan itu dapat membuatnya menulis kode semacam ini jika dia ingin melakukan sesuatu ketika kondisinya tidak terpenuhi.

if(condition)
{
}
else
{
    doStuff();
    return whatever;
}

Perasaan saya tentang ini adalah bahwa memang sangat jelek dan / atau bahwa peningkatan kualitas, jika ada, dapat diabaikan. Tapi sebagai junior, saya cenderung meragukan insting saya.

Jadi pertanyaan saya adalah: Apakah itu praktik yang baik / buruk / "tidak masalah"? Apakah ini praktik biasa?

Patsuan
sumber
57
Apakah mungkin rekan kerja Anda berasal dari latar belakang di mana kehidupan dan anggota tubuh dipertaruhkan? Saya percaya saya telah melihat beberapa menyebutkan hal semacam ini dalam sistem di mana omong kosong pengujian dan analisis otomatis dilakukan pada kode dan di mana kesalahan itu benar-benar dapat membuat seseorang kehilangan nyawanya.
jpmc26
11
Apa yang dikatakan panduan gaya kode perusahaan Anda?
Thorbjørn Ravn Andersen
4
Sebagian respons terhadap pertanyaan "Kedua" Anda. Jika salah satu blok tindakan panjang dan yang lainnya pendek, uji kondisi yang menempatkan blok pendek terlebih dahulu, sehingga cenderung tidak terjawab saat membaca kode. Kondisi itu bisa berupa negasi, atau diganti namanya agar menjadi ujian bagi kebenaran.
Ethan Bolker
9
Resharper akan memberi tahu teman Anda, di sepanjang baris "Kode Anda berlebihan dan tidak berguna, apakah Anda ingin saya menghapus / membuatnya kembali?"
BgrWorker

Jawaban:

184

elseBlok eksplisit

Aturan pertama hanya mencemari kode dan membuatnya tidak lebih mudah dibaca, juga tidak rawan kesalahan. Tujuan kolega Anda - saya kira - adalah secara eksplisit, dengan menunjukkan bahwa pengembang sepenuhnya menyadari bahwa kondisi tersebut dapat dievaluasi false. Sementara itu adalah hal yang baik untuk menjadi eksplisit, kesaksian seperti itu seharusnya tidak dikenakan biaya tiga baris kode tambahan .

Saya bahkan tidak menyebutkan fakta bahwa sebuah ifpernyataan tidak harus diikuti oleh salah satu elseatau tidak sama sekali: Itu bisa diikuti oleh satu atau lebih elifjuga.

Kehadiran returnpernyataan itu memperburuk keadaan. Bahkan jika Anda benar-benar memiliki kode untuk dieksekusi di dalam elseblok, akan lebih mudah untuk melakukannya seperti ini:

if (something)
{
    doStuff();
    return whatever;
}

doOtherThings();
return somethingElse;

Ini membuat kode mengambil dua baris lebih sedikit, dan membatalkan elseblok. Klausul penjaga semuanya tentang itu.

Perhatikan, bagaimanapun, bahwa teknik kolega Anda sebagian dapat memecahkan pola tumpukan bersyarat yang tidak menyenangkan tanpa spasi:

if (something)
{
}
if (other)
{
}
else
{
}

Dalam kode sebelumnya, tidak adanya jeda baris yang waras setelah ifblok pertama membuatnya sangat mudah untuk salah menafsirkan kode. Namun, meskipun aturan kolega Anda akan membuatnya lebih sulit untuk salah membaca kode, solusi yang lebih mudah adalah dengan menambahkan baris baru.

Tes untuk true, bukan untukfalse

Aturan kedua mungkin masuk akal, tetapi tidak dalam bentuk saat ini.

Tidak salah bahwa pengujian untuk pintu tertutup lebih intuitif daripada pengujian untuk pintu yang tidak terbuka . Negasi, dan khususnya negasi yang bersarang, biasanya sulit dipahami:

if (!this.IsMaster || (!this.Ready && !this.CanWrite))

Untuk mengatasinya, alih-alih menambahkan blok kosong, buat properti tambahan, jika relevan, atau variabel lokal .

Kondisi di atas dapat dibaca dengan mudah:

if (this.IsSlave || (this.Synchronizing && this.ReadOnly))
Arseni Mourzenko
sumber
169
Blok kosong selalu terlihat seperti kesalahan bagi saya. Ini akan segera menarik perhatian saya dan saya akan bertanya 'Mengapa ini ada di sini?'
Nelson
50
@Neil Meniadakan suatu kondisi tidak selalu membutuhkan waktu CPU tambahan. C dikompilasi ke x86 hanya bisa menukar instruksi lompat dari JZ(Lompat jika Nol) if (foo)menjadi ke JNZ(Lompat jika Bukan Nol) untuk if (!foo).
8bittree
71
" buat properti tambahan dengan nama yang jelas ": Kecuali Anda memiliki analisis domain yang lebih mendalam, ini mungkin mengubah ruang lingkup global (kelas asli) untuk menyelesaikan masalah lokal (penerapan aturan bisnis tertentu). Satu hal yang dapat dilakukan di sini adalah membuat boolean lokal dengan keadaan yang diinginkan, seperti "var isSlave =! This.IsMaster" dan menerapkan if Anda dengan boolean lokal alih-alih membuat beberapa properti lainnya. Jadi, ikuti saran dengan sebutir garam dan luangkan waktu untuk menganalisis domain sebelum membuat properti baru.
Machado
82
Ini bukan tentang "tiga baris kode", ini tentang menulis untuk audiens yang sesuai (seseorang dengan setidaknya pemahaman dasar pemrograman). Sebuah ifadalah sudah eksplisit tentang fakta bahwa kondisi bisa menjadi palsu, atau Anda tidak akan menguji untuk itu. Sebuah blok kosong membuat segalanya menjadi kurang jelas, tidak lebih, karena tidak ada pembaca yang siap untuk mengharapkannya, dan sekarang mereka harus berhenti dan berpikir tentang bagaimana itu bisa sampai di sana.
hobbs
15
@Mark itu kurang jelas, bahkan dengan komentar, daripada mengatakannya if (!commonCase) { handle the uncommon case },.
Paul
62

Mengenai aturan pertama, ini adalah contoh pengetikan yang tidak berguna. Tidak hanya membutuhkan waktu lebih lama untuk mengetik, itu akan menyebabkan kebingungan yang sangat besar bagi siapa pun yang membaca kode. Jika kode tidak diperlukan, jangan tulis itu. Ini bahkan akan diperluas untuk tidak memiliki populasi elsedalam kasus Anda karena kode kembali dari ifblok:

if(condition) 
{
    doStuff();
    return whatever;
}

doSomethingElse(); // no else needed
return somethingelse;

Mengenai poin kedua, ada baiknya untuk menghindari nama boolean yang mengandung negatif:

bool doorNotOpen = false; // a double negative is hard to reason
bool doorClosed = false; // this is much clearer

Namun, memperluas ini untuk tidak menguji negatif lagi, seperti yang Anda tunjukkan, mengarah pada pengetikan yang tidak berguna. Berikut ini jauh lebih jelas daripada memiliki ifblok kosong :

if(!condition)
{
    doStuff();
    return whatever;
}
David Arno
sumber
120
Jadi ... Anda bisa mengatakan bahwa double negative adalah tidak boleh? ;)
Patsuan
6
@ Patsuan, sangat pintar. Aku suka itu. :)
David Arno
4
Saya merasa seperti tidak banyak orang akan setuju tentang bagaimana jika yang lain kembali harus ditangani, dan beberapa bahkan mungkin mengatakan itu tidak boleh ditangani sama dalam setiap skenario. Saya sendiri tidak memiliki preferensi yang kuat, tetapi saya pasti dapat melihat argumen di balik banyak cara lain untuk melakukannya.
Dukeling
6
Jelas sekali, if (!doorNotOpen)mengerikan. Jadi nama harus se-positif mungkin. if (! doorClosed)baik-baik saja.
David Schwartz
1
Sehubungan dengan contoh pintu Anda, dua nama contoh belum tentu setara. Dalam dunia fisik doorNotOpentidak sama doorCloseddengan ada keadaan transisi antara terbuka dan tertutup. Saya melihat ini sepanjang waktu dalam aplikasi industri saya adalah negara boolean ditentukan oleh sensor fisik. Jika Anda memiliki sensor tunggal di sisi pintu yang terbuka maka Anda hanya bisa mengatakan pintu itu terbuka atau tidak terbuka. Demikian juga jika sensor berada di sisi yang tertutup, Anda hanya bisa mengatakan tertutup atau tidak tertutup. Sensor itu sendiri menentukan bagaimana keadaan logika ditegaskan.
Peter M
45

1. Argumen yang mendukung elsepernyataan kosong .

Saya sering menggunakan (dan berdebat untuk) sesuatu yang mirip dengan konstruksi pertama, yang lain kosong. Ini memberi sinyal kepada pembaca kode (baik alat analisis manusia dan otomatis) bahwa programmer telah memikirkan situasi tersebut. elsePernyataan yang hilang yang seharusnya ada telah menewaskan orang, menabrak kendaraan, dan menelan biaya jutaan dolar. MISRA-C, misalnya, mengamanatkan setidaknya komentar yang mengatakan bahwa final yang hilang disengaja secara if (condition_1) {do_this;} else if (condition_2) {do_that;} ... else if (condition_n) {do_something_else;}berurutan. Standar keandalan tinggi lainnya bahkan melangkah lebih jauh: dengan beberapa pengecualian, pernyataan lain yang hilang dilarang.

Satu pengecualian adalah komentar sederhana, sesuatu di sepanjang baris /* Else not required */. Ini menandakan niat yang sama seperti halnya tiga baris mengosongkan yang lain. Pengecualian lain di mana hal lain yang kosong tidak diperlukan adalah di mana hal itu jelas bagi pembaca kode dan untuk alat analisis otomatis yang kosong itu tidak berguna. Misalnya, if (condition) { do_stuff; return; }Demikian pula, yang kosong tidak diperlukan dalam kasus throw somethingatau goto some_label1 sebagai pengganti return.

2. Sebuah argumen untuk memilih if (condition)lebih if (!condition).

Ini adalah item faktor manusia. Logika boolean yang rumit membuat banyak orang bingung. Bahkan seorang programmer berpengalaman harus memikirkan if (!(complex || (boolean && condition))) do_that; else do_this;. Minimal, tulis ulang ini sebagai if (complex || (boolean && condition)) do_this; else do_that;.

3. Ini tidak berarti orang harus lebih suka thenpernyataan kosong .

Bagian kedua mengatakan "lebih suka" daripada "engkau". Itu adalah pedoman dan bukan aturan. Alasan pedoman itu untuk lebih memilih ifkondisi positif adalah karena kode harus jelas dan jelas. Klausa kosong lalu (misalnya, if (condition) ; else do_something;) melanggar ini. Ini adalah pemrograman yang dikaburkan, bahkan menyebabkan programmer yang paling berpengalaman untuk mendukung dan membaca kembali ifkondisi dalam bentuk yang dinegasikan. Jadi, tuliskan dalam bentuk yang dinegasikan di tempat pertama dan hilangkan pernyataan lain (atau kosongkan komentar lain atau komentar untuk efek itu jika diamanatkan untuk melakukannya).



1 Saya menulis bahwa kemudian klausa yang berakhir dengan return, throwatau gototidak memerlukan yang lain kosong. Jelas bahwa klausa lain tidak diperlukan. Tapi bagaimana dengan itu goto? Sebagai tambahan, aturan pemrograman yang kritis terhadap keselamatan terkadang melarang pengembalian lebih awal, dan hampir selalu melarang untuk melemparkan pengecualian. Namun mereka mengizinkan gotodalam bentuk terbatas (misalnya, goto cleanup1;). Penggunaan terbatas ini gotoadalah praktik yang disukai di beberapa tempat. Kernel Linux, misalnya, penuh dengan gotopernyataan seperti itu .

David Hammen
sumber
6
!juga mudah dilupakan. Itu terlalu singkat.
Thorbjørn Ravn Andersen
4
Tidak, hal lain yang kosong tidak memperjelas bahwa programmer telah memikirkannya. Ini menimbulkan kebingungan yang lebih besar, "Apakah ada beberapa pernyataan yang direncanakan dan mereka lupa atau mengapa ada klausa kosong?" Sebuah komentar jauh lebih jelas.
Simon
9
@Simon: Bentuk tipikal adalah else { /* Intentionally empty */ }, atau sesuatu di sepanjang garis itu. Yang kosong memenuhi analisa statis yang tanpa berpikir mencari pelanggaran aturan. Komentar menginformasikan pembaca bahwa yang kosong disengaja. Kemudian lagi, programmer berpengalaman biasanya menghilangkan komentar sebagai tidak perlu - tetapi bukan yang kosong. Pemrograman reliabilitas tinggi adalah domainnya sendiri. Konstruksi terlihat agak aneh bagi orang luar. Konstruksi ini digunakan karena pelajaran dari sekolah ketukan keras, ketukan yang sangat sulit ketika hidup & mati atau $$$$$$$$$ ada di tangan.
David Hammen
2
Saya tidak mengerti bagaimana kosong maka klausa adalah hal yang buruk ketika kosong klausa lain tidak (dengan asumsi kita memilih gaya itu). Saya bisa melihatif (target == source) then /* we need to do nothing */ else updateTarget(source)
Bergi
2
@ ThorbjørnRavnAndersen tidak, notadalah kata kunci dalam C ++, seperti juga operator bitwise dan logis lainnya. Lihat representasi operator alternatif .
Ruslan
31

Saya menggunakan cabang-lain yang kosong (dan kadang-kadang cabang-kosong yang kosong) dalam kasus yang sangat jarang: Ketika jelas bahwa bagian if dan else harus ditangani, entah bagaimana, untuk alasan non-sepele kasus dapat ditangani oleh tidak melakukan apapun. Dan oleh karena itu siapa pun yang membaca kode dengan tindakan lain yang diperlukan akan segera curiga bahwa ada sesuatu yang hilang dan buang waktu mereka.

if (condition) {
    // No action needed because ...
} else {
    do_else_action()
}

if (condition) {
    do_if_action()
} else {
    // No action needed because ...
}

Tapi tidak:

if (condition) {
    do_if_action()
} else {
    // I was told that an if always should have an else ...
}
gnasher729
sumber
5
Ini. Saya menemukan pernyataan itu if test succeeds -> no action needed -> else -> action neededsecara semantik sangat berbeda dengan pernyataan itu if it applies that the opposite of a test outcome is true then an action is needed. Saya teringat akan kutipan Martin Fowler: "siapa pun dapat menulis kode yang dapat dimengerti komputer; programmer yang baik menulis kode yang dapat dipahami manusia".
Tasos Papastylianou
2
@TasosPapastylianou Itu curang. Kebalikan dari 'jika tes berhasil -> tidak ada tindakan yang diperlukan' adalah 'jika tes tidak berhasil -> tindakan diperlukan'. Anda menambahkannya dengan sekitar 10 kata.
Jerry B
@ JerryB tergantung pada "tes". Kadang-kadang negasi (linguistik) mudah, tetapi kadang tidak, dan akan menyebabkan kebingungan. Dalam kasus-kasus itu, lebih jelas untuk berbicara tentang "penolakan / kebalikan dari hasil menjadi benar" vs "hasil tidak benar"; namun intinya adalah bahwa akan lebih jelas lagi untuk memperkenalkan langkah 'kosong', atau membalikkan makna nama variabel. Ini tipikal dalam situasi "de morgan": mis. if ((missing || corrupt) && !backup) -> skip -> else do stuffJauh lebih jelas (+ maksud tersirat berbeda) daripadaif ((!missing && !corrupt) || backup) -> do stuff
Tasos Papastylianou
1
@TasosPapastylianou Anda bisa menulis if(!((missing || corrupt) && !backup)) -> do stuffatau lebih baik if((aviable && valid) || backup) -> do stuff. (Tetapi dalam contoh nyata, Anda harus memeriksa apakah cadangannya valid sebelum menggunakannya)
12431234123412341234123
2
@TasosPapastylianou di mana ada negatif ganda pada apa yang saya katakan? Tentu, jika Anda menggunakan tidak rusak sebagai nama variabel maka Anda akan memiliki titik yang jelas. Meskipun ketika Anda datang ke operator boolean campuran seperti ini, mungkin lebih baik untuk memiliki variabel sementara untuk digunakan dalam if: file_ok = !missing && !corrupt; if(file_ok || backup) do_stuff();yang menurut saya pribadi membuatnya lebih jelas.
Baldrickk
23

Semuanya sama, lebih memilih singkat.

Apa yang tidak Anda tulis, tidak ada yang harus membaca dan mengerti.

Meskipun eksplisit bisa bermanfaat, hanya itu masalahnya jika itu menjadi jelas tanpa kata-kata yang tidak tepat bahwa apa yang Anda tulis adalah apa yang ingin Anda tulis.


Jadi, hindari cabang-cabang yang kosong, mereka tidak hanya tidak berguna tetapi juga tidak umum dan dengan demikian menyebabkan kebingungan.

Selain itu, hindari menulis cabang lain jika Anda keluar langsung dari cabang-if.

Aplikasi explicitness yang bermanfaat akan memberikan komentar setiap kali Anda jatuh kasus beralih // FALLTHRU, dan menggunakan komentar atau blok kosong di mana Anda memerlukan pernyataan kosong for(a;b;c) /**/;.

Deduplicator
sumber
7
// FALLTHRUUgh, bukan di SCREAMING CAPS atau dengan singkatan txtspk. Kalau tidak, saya setuju dan ikuti sendiri latihan ini.
Cody Grey
7
@CodyGray - Ini adalah satu tempat di mana SHOUTING adalah ide yang bagus. Kebanyakan pergantian pernyataan mengakhiri setiap kasus dengan break. Kegagalan untuk memiliki yang breakmenyebabkan beberapa kegagalan perangkat lunak yang spektakuler. Sebuah komentar yang berteriak kepada pembaca tentang kode yang sengaja dibuat-buat adalah hal yang baik. Alat analisis statis itu dapat mencari pola spesifik ini dan tidak mengeluh tentang kejatuhan melalui situasi membuatnya menjadi hal yang lebih baik.
David Hammen
1
@Wossname: Saya baru saja memeriksa vim, dan tidak mengenali variasi FALLTHRU. Dan saya pikir, untuk alasan yang baik: TODOdan FIXMEkomentar adalah komentar yang perlu disyukuri karena Anda ingin dapat menanyakan git grepcacat yang diketahui yang Anda miliki dalam kode Anda, dan di mana mereka berada. Sebuah kesalahan bukanlah cacat, dan tidak ada alasan untuk menerimanya, apa pun yang terjadi.
cmaster
4
@DavidHammen Tidak, berteriak bukanlah ide yang baik: Jika Anda memiliki // terobosan sebagai ganti dari yang diharapkan ;, itu sudah cukup bagi pengembang untuk segera mengerti. Selain itu, // fallthrough tidak berbicara tentang cacat, itu hanya menandakan bahwa keadaan telah dipertimbangkan, dan bahwa istirahat; yang tampaknya hilang, memang dimaksudkan tidak ada di sana. Ini murni tujuan informasi, dan tidak ada yang perlu diteriaki.
cmaster
1
@ cmaster - Teriakan itu bukan ide yang bagus menurut Anda . Pendapat orang lain berbeda. Dan hanya karena Anda konfigurasi vim tidak mengakui FALLTHRUtidak berarti bahwa orang lain belum dikonfigurasi vim untuk melakukannya.
David Hammen
6

Tidak ada aturan yang keras dan cepat tentang kondisi positif atau negatif untuk pernyataan IF, tidak sepengetahuan saya. Saya pribadi lebih suka pengkodean untuk kasus positif daripada negatif, jika berlaku. Saya pasti tidak akan melakukan ini, jika itu membuat saya membuat blok IF kosong, diikuti oleh ELSE yang penuh dengan logika. Jika situasi seperti itu muncul, butuh waktu 3 detik untuk mengembalikannya ke pengujian untuk kasus positif.

Apa yang saya benar-benar tidak suka tentang contoh Anda, adalah ruang vertikal yang sama sekali tidak perlu diambil oleh ELSE kosong. Tidak ada alasan apa pun untuk melakukan ini. Itu tidak menambahkan apa pun pada logika, tidak membantu mendokumentasikan apa yang dilakukan kode, dan tidak meningkatkan keterbacaan sama sekali. Bahkan, saya berpendapat bahwa menambahkan ruang vertikal mungkin dapat mengurangi keterbacaan.

Langecrew
sumber
2
Ruang vertikal yang tidak perlu itu adalah konsekuensi dari mandat untuk selalu menggunakan kawat gigi, untuk melarang kehilangan yang lain, dan untuk menggunakan gaya Allman untuk lekukan.
David Hammen
Yah, saya akan berpikir bahwa selalu menggunakan kawat gigi adalah hal yang baik, karena itu membuat niat pengembang eksplisit - tidak ada ruang untuk menebak ketika membaca kode mereka. Itu mungkin terdengar konyol, tapi percayalah, terutama ketika membimbing pengembang junior, kesalahan terkait dengan kawat gigi yang hilang bisa terjadi. Saya pribadi tidak pernah menjadi penggemar lekukan gaya Allman, untuk alasan persisnya yang Anda sebutkan: ruang vertikal yang tidak perlu. Tapi itu hanya pendapat dan gaya pribadi saya. Itu hanya apa yang saya pelajari pada awalnya, jadi selalu terasa lebih nyaman untuk dibaca.
Langecrew
Gaya pribadi: terlepas dari gaya kurung mana yang saya gunakan (Allman, 1TB, ...), saya selalu menggunakan kawat gigi.
David Hammen
Kondisional yang kompleks atau yang menurut Anda mungkin sulit dipahami hampir selalu dapat diselesaikan dengan difaktorkan ulang ke dalam metode / fungsi mereka sendiri. Sesuatu seperti ini if(hasWriteAccess(user,file))bisa jadi rumit di bawahnya, tetapi sekilas Anda tahu persis apa hasilnya. Bahkan jika itu hanya beberapa kondisi, mis. Pemanggilan if(user.hasPermission() && !file.isLocked())metode dengan nama yang tepat dapat menjelaskan, sehingga negatif menjadi kurang menjadi masalah.
Chris Schneider
Sepakat. Kemudian lagi, saya penggemar berat refactoring bila memungkinkan :)
Langecrew
5

elseBlok eksplisit

Saya tidak setuju dengan ini sebagai pernyataan selimut yang mencakup semua ifpernyataan tetapi ada kalanya menambahkan elsekebiasaan karena kebiasaan adalah hal yang baik.

Sebuah ifpernyataan, pikiran saya, sebenarnya mencakup dua fungsi yang berbeda.

Jika kita seharusnya melakukan sesuatu, lakukan di sini.

Hal-hal seperti ini jelas tidak membutuhkan elsebagian.

    if (customer.hasCataracts()) {
        appointmentSuggestions.add(new CataractAppointment(customer));
    }
    if (customer.isDiabetic()) {
        customer.assignNurse(DiabeticNurses.pickBestFor(customer));
    }

dan dalam beberapa kasus bersikeras menambahkan elsekekuatan yang menyesatkan.

    if (k > n) {
        return BigInteger.ZERO;
    }
    if (k <= 0 || k == n) {
        return BigInteger.ONE;
    }

adalah tidak sama dengan

    if (k > n) {
        return BigInteger.ZERO;
    } else {
        if (k <= 0 || k == n) {
            return BigInteger.ONE;
        }
    }

meskipun secara fungsional sama. Menulis yang pertama ifdengan kosong elsedapat membawa Anda ke hasil kedua yang tidak perlu jelek.

Jika kami memeriksa keadaan tertentu, sering kali merupakan ide bagus untuk menambahkan yang kosong elsehanya untuk mengingatkan Anda untuk menutupi kemungkinan itu

        // Count wins/losses.
        if (doors[firstChoice] == Prize.Car) {
            // We would have won without switching!
            winWhenNotSwitched += 1;
        } else {
            // We win if we switched to the car!
            if (doors[secondChoice] == Prize.Car) {
                // We picked right!
                winWhenSwitched += 1;
            } else {
                // Bad choice.
                lost += 1;
            }
        }

Ingatlah bahwa aturan ini hanya berlaku ketika Anda menulis kode baru . IMHO elseKlausa kosong harus dihapus sebelum checkin.


Tes untuk true, bukan untukfalse

Sekali lagi ini adalah saran yang baik pada tingkat umum tetapi dalam banyak kasus ini membuat kode tidak perlu rumit dan kurang dapat dibaca.

Meskipun suka kode

    if(!customer.canBuyAlcohol()) {
        // ...
    }

mengguncang pembaca, tetapi membuatnya

    if(customer.canBuyAlcohol()) {
        // Do nothing.
    } else {
        // ...
    }

setidaknya sama buruknya, jika tidak lebih buruk.

Aku dikodekan dalam BCPL bertahun-tahun lalu dan dalam bahasa yang ada IFklausul dan sebuah UNLESSklausul sehingga Anda bisa kode jauh lebih readably sebagai:

    unless(customer.canBuyAlcohol()) {
        // ...
    }

yang secara signifikan lebih baik, tetapi masih belum sempurna.


Proses pribadi saya

Secara umum, ketika saya menulis kode baru saya akan sering menambahkan elseblok kosong ke ifpernyataan hanya untuk mengingatkan saya bahwa saya belum membahas kemungkinan itu. Ini membantu saya menghindari DFSjebakan dan memastikan bahwa ketika saya meninjau kode saya perhatikan ada banyak hal yang harus dilakukan. Namun, saya biasanya menambahkan TODOkomentar untuk melacak.

  if (returnVal == JFileChooser.APPROVE_OPTION) {
    handleFileChosen();
  } else {
    // TODO: Handle case where they pressed Cancel.
  }

Saya menemukan bahwa secara umum saya elsejarang menggunakan kode saya karena sering dapat menunjukkan bau kode.

OldCurmudgeon
sumber
Penanganan blok "kosong" Anda berbunyi seperti kode stubbing standar ketika mengimplementasikan thngs ...
Deduplicator
The unlessblok adalah saran yang baik, dan hampir tidak terbatas pada BCPL.
tchrist
@TobySpeight Ups. Entah bagaimana itu luput dari perhatian saya bahwa apa yang saya tulis ...sebenarnya return. (Sebenarnya, dengan k=-1,, n=-2kembalinya yang pertama diambil tetapi ini sebagian besar dapat diperdebatkan.)
David Richerby
Perl modern memiliki ifdan unless. Terlebih lagi, ini juga memungkinkan ini setelah pernyataan, sehingga Anda dapat mengatakan print "Hello world!" if $born;atau print "Hello world!" unless $dead;dan keduanya akan melakukan apa yang Anda pikir mereka lakukan.
CVn
1

Untuk poin pertama, saya telah menggunakan bahasa yang memaksa pernyataan IF untuk digunakan dengan cara ini (dalam Opal, bahasa di belakang scraper layar mainframe untuk meletakkan ujung depan GUI ke sistem mainframe), dan dengan hanya satu baris untuk IF dan ELSE. Itu bukan pengalaman yang menyenangkan!

Saya berharap ada kompiler untuk mengoptimalkan klausa ELSE tambahan tersebut. Tetapi untuk kode langsung itu bukan menambahkan apa-apa (dalam pengembangan itu bisa menjadi penanda yang berguna untuk kode selanjutnya).

Suatu waktu saya menggunakan sesuatu seperti klausa tambahan ini adalah ketika menggunakan pemrosesan KASUS / KAPAN. Saya selalu menambahkan klausa standar meskipun kosong. Ini adalah kebiasaan jangka panjang dari bahasa yang akan keliru jika klausa seperti itu tidak digunakan, dan memaksakan pemikiran apakah hal-hal yang benar-benar harus dijatuhkan.

Praktek mainframe yang lama (misalnya, PL / 1 dan COBOL) diterima bahwa pengecekan negatif kurang efisien. Ini bisa menjelaskan poin ke-2, meskipun saat ini ada penghematan efisiensi yang jauh lebih penting yang diabaikan sebagai optimisasi mikro.

Logika negatif memang cenderung kurang dapat dibaca, meskipun tidak begitu banyak pada pernyataan IF sederhana.

Kickstart
sumber
2
Begitu ya, jadi itu adalah praktik yang biasa di beberapa titik.
Patsuan
@ Patsuan - ya, sampai batas tertentu. Meskipun saat itulah mainframe assembler merupakan alternatif serius untuk kode kinerja kritis.
Kickstart
1
"Dulu ... diterima bahwa cek negatif kurang efisien." Yah, mereka adalah kecuali compiler mengoptimalkan if !A then B; else Cuntuk if A then C; else B. Menghitung kondisi negasi adalah instruksi CPU tambahan. (Meskipun seperti yang Anda katakan, ini tidak mungkin menjadi kurang efisien secara signifikan .)
David Richerby
@ DavidRicherby Dan jika kita berbicara secara umum, beberapa bahasa dapat membuat optimasi seperti itu sangat sulit. Ambil C ++ dengan kelebihan beban !dan ==operator, misalnya. Bukan berarti siapa pun harus melakukan itu , ingatlah ...
CVn
1

Saya akan mendukung pendirian sebagian besar jawaban bahwa elseblok kosong hampir selalu merupakan limbah elektronik yang berbahaya. Jangan tambahkan ini kecuali Anda memiliki alasan yang sangat bagus untuk itu, dalam hal ini blok kosong tidak boleh kosong sama sekali, itu harus berisi komentar yang menjelaskan mengapa itu ada.

Masalah tentang menghindari negatif patut mendapat perhatian lebih: Namun, biasanya, setiap kali Anda perlu menggunakan nilai boolean, Anda memerlukan beberapa blok kode untuk beroperasi saat disetel, dan beberapa blok lain untuk beroperasi ketika tidak disetel. Dengan demikian, jika Anda menegakkan aturan no-negatif, Anda menegakkan memiliki if() {} else {...}pernyataan (dengan ifblok kosong !), Atau Anda membuat boolean kedua untuk setiap boolean yang berisi nilai negasinya. Kedua opsi itu buruk, karena membingungkan pembaca Anda.

Kebijakan yang bermanfaat adalah ini: Jangan pernah menggunakan formulir yang dinegasikan dalam nama boolean, dan nyatakan negasi sebagai satu !. Pernyataan seperti if(!doorLocked)sangat jelas, pernyataan seperti if(!doorUnlocked)otak knot. Jenis ekspresi nanti adalah hal yang perlu Anda hindari dengan cara apa pun, bukan kehadiran satu orang !dalam suatu if()kondisi.

cmaster
sumber
0

Saya akan mengatakan itu pasti praktik yang buruk. Menambahkan pernyataan lain akan menambahkan sejumlah baris tak berguna ke kode Anda yang tidak melakukan apa pun dan membuatnya lebih sulit untuk dibaca.

ayrton clark
sumber
1
Saya dengar Anda belum pernah bekerja untuk pemberi kerja yang menilai kinerja Anda berdasarkan SLOCs yang diproduksi per periode ...
CVn
0

Ada pola lain yang belum disebutkan tentang penanganan kasus kedua yang memiliki blok if kosong. Salah satu cara untuk mengatasinya adalah dengan kembali di blok if. Seperti ini:

void foo()
{
    if (condition) return;

    doStuff();
    ...
}

Ini adalah pola umum untuk memeriksa kondisi kesalahan. Kasus afirmatif masih digunakan, dan bagian dalamnya tidak lagi kosong meninggalkan programmer masa depan bertanya-tanya apakah no-op disengaja atau tidak. Ini juga dapat meningkatkan keterbacaan karena Anda mungkin diminta untuk membuat fungsi baru (sehingga memecah fungsi besar menjadi fungsi individu yang lebih kecil).

Shaz
sumber
0

Ada satu hal ketika mempertimbangkan argumen "selalu ada klausa lain" yang belum saya lihat dalam jawaban lain: itu masuk akal dalam gaya pemrograman fungsional. Semacam.

Dalam gaya pemrograman fungsional, Anda berurusan dengan ekspresi daripada pernyataan. Jadi setiap blok kode memiliki nilai balik - termasuk if-then-elseekspresi. Namun, itu akan menghalangi elseblok kosong . Biarkan saya memberi Anda contoh:

var even = if (n % 2 == 0) {
  return "even";
} else {
  return "odd";
}

Sekarang, dalam bahasa dengan gaya C atau sintaks yang terinspirasi gaya C (seperti Java, C # dan JavaScript, hanya untuk beberapa nama) ini terlihat aneh. Namun terlihat jauh lebih akrab ketika ditulis seperti itu:

var even = (n % 2 == 0) ? "even" : "odd";

Membiarkan elsecabang kosong di sini akan menyebabkan nilai tidak terdefinisi - dalam kebanyakan kasus, bukan apa yang kita inginkan menjadi kasus yang valid saat memprogram fungsionalitas. Sama dengan meninggalkannya sepenuhnya. Namun, ketika Anda pemrograman iteratif saya menetapkan sedikit alasan untuk selalu memiliki elseblok.

blalasaadri
sumber
0

... sebuah pernyataan if harus diikuti oleh pernyataan lain, apakah ada sesuatu untuk dimasukkan ke dalamnya atau tidak.

Saya tidak setuju if (a) { } else { b(); }harus ditulis ulang if (!a) { b(); }dan if (a) { b(); } else { }harus ditulis ulang sebagai if (a) { b(); }.

Namun, ada baiknya menunjukkan bahwa saya jarang memiliki cabang kosong. Ini karena biasanya saya mencatat bahwa saya pergi ke cabang kosong. Dengan cara ini saya dapat mengembangkan murni dari log pesan. Saya jarang menggunakan debugger. Di lingkungan produksi Anda tidak mendapatkan debugger; senang bisa memecahkan masalah produksi dengan alat yang sama yang Anda gunakan untuk mengembangkan.

Kedua, lebih baik untuk menguji nilai-nilai yang benar daripada yang salah. Itu berarti bahwa lebih baik untuk menguji variabel 'doorClosed' daripada variabel '! DoorOpened'

Saya memiliki perasaan campur aduk tentang ini. Kerugian untuk memiliki doorCloseddan doorOpenedberpotensi menggandakan jumlah kata / istilah yang perlu Anda ketahui. Kerugian lain adalah dari waktu ke waktu makna doorCloseddan doorOpeneddapat berubah (pengembang lain datang setelah Anda) dan Anda mungkin berakhir dengan dua properti yang tidak lagi tepat meniadakan satu sama lain. Alih-alih menghindari negasi, saya lebih menghargai mengadaptasi bahasa kode (nama kelas, nama variabel, dll.) Ke kosakata pengguna bisnis dan dengan persyaratan yang saya berikan. Saya tidak ingin membuat istilah yang sama sekali baru untuk menghindari a!jika istilah itu hanya memiliki arti bagi pengembang yang pengguna bisnis tidak akan mengerti. Saya ingin pengembang berbicara dalam bahasa yang sama dengan persyaratan pengguna dan orang yang menulis. Sekarang jika istilah baru menyederhanakan model, maka itu penting, tetapi itu harus ditangani sebelum persyaratan diselesaikan, bukan setelahnya. Pengembangan harus menangani masalah ini sebelum mereka mulai membuat kode.

Saya cenderung meragukan insting saya.

Adalah baik untuk terus mempertanyakan diri Anda sendiri.

Apakah ini praktik yang baik / buruk / "tidak masalah"?

Untuk beberapa derajat banyak ini tidak masalah. Dapatkan kode Anda ditinjau dan beradaptasi dengan tim Anda. Itu biasanya hal yang benar untuk dilakukan. Jika semua orang di tim Anda ingin membuat kode dengan cara tertentu, mungkin lebih baik melakukannya dengan cara seperti itu (mengubah 1 orang - diri sendiri - mengambil lebih sedikit poin cerita daripada mengubah sekelompok orang).

Apakah ini praktik biasa?

Saya tidak ingat pernah melihat cabang yang kosong. Saya melihat orang menambahkan properti untuk menghindari negasi.

Kata-kata Seperti Jared
sumber
0

Saya hanya akan membahas bagian kedua dari pertanyaan dan ini berasal dari sudut pandang saya bekerja di sistem industri dan memanipulasi perangkat di dunia nyata.

Namun ini adalah suatu komentar yang diperluas daripada jawaban.

Ketika dinyatakan

Kedua, lebih baik untuk menguji nilai-nilai yang benar daripada yang salah. Itu berarti bahwa lebih baik untuk menguji variabel 'doorClosed' daripada variabel '! DoorOpened'

Argumennya adalah bahwa hal itu memperjelas apa yang dilakukan kode.

Dari sudut pandang saya, asumsi dibuat di sini bahwa keadaan pintu adalah keadaan biner padahal sebenarnya itu adalah keadaan terner:

  1. Pintunya terbuka.
  2. Pintunya tidak terbuka penuh atau tertutup sepenuhnya.
  3. Pintunya tertutup.

Jadi tergantung di mana sensor digital biner tunggal berada, Anda hanya dapat menduga salah satu dari dua konsep:

  1. Jika sensor berada di terbuka sisi pintu: Pintu terbuka atau tidak terbuka
  2. Jika sensor berada di sisi pintu yang tertutup : Pintu itu tertutup atau tidak tertutup.

Dan Anda tidak bisa menukar konsep-konsep ini melalui penggunaan negasi biner. Dengan demikian doorCloseddan !doorOpenedkemungkinan tidak sama dan upaya untuk berpura-pura sama artinya adalah pemikiran yang salah yang mengasumsikan pengetahuan yang lebih besar tentang keadaan sistem daripada yang sebenarnya ada.

Dalam kembali ke pertanyaan Anda, saya akan mendukung bertele-tele yang cocok dengan asal informasi yang diwakili variabel. Jadi, jika informasi tersebut berasal dari sisi pintu yang tertutup maka lanjutkan dengan doorCloseddll. Ini dapat menyiratkan menggunakan salah satu doorClosedatau !doorClosedsesuai kebutuhan dalam berbagai pernyataan sebagaimana diperlukan, yang mengakibatkan potensi kebingungan dengan penggunaan negasi. Tetapi apa yang tidak dilakukannya adalah secara implisit menyebarkan asumsi tentang keadaan sistem.


Untuk keperluan diskusi untuk pekerjaan yang saya lakukan, jumlah informasi yang saya miliki tentang keadaan sistem tergantung pada fungsionalitas yang diperlukan oleh keseluruhan sistem itu sendiri.

Terkadang saya hanya perlu tahu apakah pintunya tertutup atau tidak. Dalam hal ini hanya satu sensor biner tunggal yang akan cukup. Tetapi di lain waktu saya perlu tahu bahwa pintu terbuka, tertutup atau dalam transisi. Dalam kasus seperti itu akan ada dua sensor biner (pada setiap gerakan pintu yang ekstrim - dengan pengecekan error terhadap pintu yang secara bersamaan membuka dan menutup) atau akan ada sensor analog yang mengukur seberapa 'terbuka' pintu itu.

Contoh yang pertama adalah pintu di oven microwave. Anda mengaktifkan pengoperasian oven berdasarkan pintu yang ditutup atau tidak ditutup. Anda tidak peduli seberapa terbuka pintunya.

Contoh yang kedua adalah aktuator yang digerakkan motor sederhana. Anda menghambat mengemudi motor maju ketika aktuator sepenuhnya keluar. Dan Anda menghambat mengemudi motor secara terbalik ketika aktuator sepenuhnya masuk.

Pada dasarnya jumlah dan jenis sensor turun untuk menjalankan analisis biaya sensor terhadap analisis persyaratan tentang apa yang diperlukan untuk mencapai fungsionalitas yang diperlukan.

Peter M
sumber
"Dari sudut pandang saya, asumsi dibuat di sini bahwa keadaan pintu adalah keadaan biner padahal sebenarnya itu adalah kondisi terner:" dan "Anda mengaktifkan pengoperasian oven berdasarkan pintu yang ditutup atau tidak ditutup. Anda tidak peduli seberapa terbuka pintunya. " saling bertentangan. Juga, saya tidak berpikir pertanyaannya adalah tentang pintu dan sensor sama sekali.
Sanchises
@Sanchises Pernyataan tidak bertentangan karena Anda telah mengeluarkannya di luar konteks. Pernyataan pertama adalah dalam konteks bahwa dua pengukuran biner yang berlawanan dari negara terner tidak dapat dipertukarkan melalui negasi biner. Pernyataan kedua adalah dalam konteks bahwa hanya sebagian pengetahuan dari negara terner yang cukup untuk aplikasi untuk bekerja dengan benar. Adapun pintu, pertanyaan OP referensi pintu terbuka dan tertutup. Saya hanya menunjukkan asumsi yang jelas yang dapat mempengaruhi bagaimana data diperlakukan.
Peter M
-1

Aturan gaya beton selalu buruk. Pedomannya bagus, tetapi pada akhirnya sering ada kasus di mana Anda bisa memperjelas hal dengan melakukan sesuatu yang tidak cukup mengikuti pedoman.

Yang mengatakan, ada banyak kebencian untuk yang lain kosong "itu menyia-nyiakan garis" "mengetik ekstra", itu hanya buruk. Anda dapat membuat argumen untuk memindahkan tanda kurung ke baris yang sama jika Anda benar-benar membutuhkan ruang vertikal, tetapi jika itu merupakan masalah Anda tidak boleh meletakkan {pada baris yang terpisah pula.

Seperti disebutkan di tempat lain, memiliki blok lain sangat berguna untuk menunjukkan bahwa Anda secara eksplisit tidak menginginkan apa pun terjadi dalam kasus lain. Setelah melakukan banyak pemrograman fungsional (di mana lagi diperlukan), saya telah belajar bahwa Anda harus selalu mempertimbangkan yang lain ketika menulis if, meskipun seperti @OldCurmudgeon menyebutkan sebenarnya ada dua kasus penggunaan yang berbeda. Seseorang harus memiliki yang lain, yang seharusnya tidak. Sayangnya itu bukan sesuatu yang selalu bisa Anda ceritakan, apalagi dengan linter, maka dogmatis 'selalu menempatkan blok lain'.

Adapun 'no negative', sekali lagi, aturan absolut itu buruk. Memiliki yang kosong jika bisa menjadi aneh, terutama jika itu tipe jika itu tidak membutuhkan yang lain, jadi tuliskan semua itu daripada! atau == false salah. Yang mengatakan, ada banyak kasus di mana yang negatif masuk akal. Contoh umum adalah caching nilai:

static var cached = null

func getCached() {
    if !cached {
        cached = (some calculation, etc)
    }

    return cached
}

Jika logika aktual (mental / Inggris) melibatkan negatif, demikian juga pernyataan if.

zacaj
sumber