Dari mana asal usul “satu kembali saja”?

1056

Saya sering berbicara dengan programmer yang mengatakan " Jangan letakkan banyak pernyataan pengembalian dalam metode yang sama. " Ketika saya meminta mereka untuk memberi tahu alasannya, yang saya dapatkan hanyalah " Standar pengkodean mengatakan demikian. " Atau " Ini membingungkan. " Ketika mereka menunjukkan solusi kepada saya dengan pernyataan pengembalian tunggal, kode tersebut terlihat lebih buruk bagi saya. Sebagai contoh:

if (condition)
   return 42;
else
   return 97;

" Ini jelek, kamu harus menggunakan variabel lokal! "

int result;
if (condition)
   result = 42;
else
   result = 97;
return result;

Bagaimana kode 50% mengasapi ini membuat program lebih mudah dimengerti? Secara pribadi, saya merasa lebih sulit, karena ruang keadaan baru saja meningkat oleh variabel lain yang dapat dengan mudah dicegah.

Tentu saja, biasanya saya hanya menulis:

return (condition) ? 42 : 97;

Tetapi banyak programmer menghindari operator bersyarat dan lebih suka bentuk panjang.

Dari mana gagasan "satu kembali saja" ini berasal? Adakah alasan historis mengapa kebaktian ini terjadi?

fredoverflow
sumber
2
Ini agak terhubung ke refactoring Guard Clause. stackoverflow.com/a/8493256/679340 Penjaga Klausa akan menambahkan kembali ke awal metode Anda. Dan itu membuat kode jauh lebih bersih menurut saya.
Piotr Perak
3
Itu datang dari gagasan pemrograman terstruktur. Beberapa orang mungkin berpendapat bahwa hanya dengan satu pengembalian memungkinkan Anda dengan mudah memodifikasi kode untuk melakukan sesuatu sebelum kembali atau dengan mudah melakukan debug.
martinkunev
3
Saya pikir contohnya adalah kasus yang cukup sederhana di mana saya tidak akan memiliki pendapat yang kuat. ideal single-entry-single-exit lebih untuk membimbing kita menjauh dari situasi gila seperti 15 pernyataan pengembalian dan dua cabang lagi yang tidak kembali sama sekali!
mendota
2
Itu adalah salah satu artikel terburuk yang pernah saya baca. Sepertinya penulis menghabiskan lebih banyak waktu berfantasi tentang kemurnian OOP daripada benar-benar mencari cara untuk mencapai apa pun. Pohon ekspresi dan evaluasi memiliki nilai tetapi tidak ketika Anda hanya dapat menulis fungsi normal saja.
DeadMG
3
Anda harus menghapus kondisinya sama sekali. Jawabannya adalah 42.
cambunctious

Jawaban:

1120

"Single Entry, Single Exit" ditulis ketika sebagian besar pemrograman dilakukan dalam bahasa assembly, FORTRAN, atau COBOL. Ini telah banyak disalahtafsirkan secara luas, karena bahasa modern tidak mendukung praktik-praktik yang Dijkstra peringatkan.

"Entri Tunggal" berarti "jangan membuat titik entri alternatif untuk fungsi". Dalam bahasa assembly, tentu saja, dimungkinkan untuk memasukkan fungsi pada instruksi apa pun. FORTRAN mendukung banyak entri ke fungsi dengan ENTRYpernyataan:

      SUBROUTINE S(X, Y)
      R = SQRT(X*X + Y*Y)
C ALTERNATE ENTRY USED WHEN R IS ALREADY KNOWN
      ENTRY S2(R)
      ...
      RETURN
      END

C USAGE
      CALL S(3,4)
C ALTERNATE USAGE
      CALL S2(5)

"Single Exit" berarti bahwa suatu fungsi hanya boleh kembali ke satu tempat: pernyataan segera setelah panggilan. Itu tidak berarti bahwa suatu fungsi hanya boleh kembali dari satu tempat. Ketika Pemrograman Terstruktur ditulis, itu adalah praktik umum untuk fungsi untuk menunjukkan kesalahan dengan kembali ke lokasi alternatif. FORTRAN mendukung ini melalui "pengembalian alternatif":

C SUBROUTINE WITH ALTERNATE RETURN.  THE '*' IS A PLACE HOLDER FOR THE ERROR RETURN
      SUBROUTINE QSOLVE(A, B, C, X1, X2, *)
      DISCR = B*B - 4*A*C
C NO SOLUTIONS, RETURN TO ERROR HANDLING LOCATION
      IF DISCR .LT. 0 RETURN 1
      SD = SQRT(DISCR)
      DENOM = 2*A
      X1 = (-B + SD) / DENOM
      X2 = (-B - SD) / DENOM
      RETURN
      END

C USE OF ALTERNATE RETURN
      CALL QSOLVE(1, 0, 1, X1, X2, *99)
C SOLUTION FOUND
      ...
C QSOLVE RETURNS HERE IF NO SOLUTIONS
99    PRINT 'NO SOLUTIONS'

Kedua teknik ini sangat rawan kesalahan. Penggunaan entri alternatif sering menyebabkan beberapa variabel tidak diinisialisasi. Penggunaan pengembalian alternatif memiliki semua masalah pernyataan GOTO, dengan komplikasi tambahan bahwa kondisi cabang tidak berdekatan dengan cabang, tetapi di suatu tempat dalam subrutin.

kevin cline
sumber
39
Dan jangan lupa kode spageti . Itu tidak diketahui bagi subrutin untuk keluar menggunakan GOTO alih-alih kembali, meninggalkan parameter fungsi panggilan dan mengembalikan alamat pada stack. Keluar tunggal dipromosikan sebagai cara untuk setidaknya menyalurkan semua jalur kode ke pernyataan RETURN.
TMN
2
@ TMN: pada hari-hari awal, kebanyakan mesin tidak memiliki tumpukan perangkat keras. Rekursi umumnya tidak didukung. Argumen subrutin dan alamat pengirim disimpan di lokasi tetap yang berdekatan dengan kode subrutin. Pengembalian hanyalah goto tidak langsung.
kevin cline
5
@ kevin: Ya, tetapi menurut Anda ini bahkan tidak berarti lagi seperti apa yang diciptakan. (BTW, aku benar-benar cukup yakin bahwa Fred bertanya adalah preferensi untuk saat interpretasi "Exit Tunggal" berasal dari.) Juga, C telah memiliki constsejak sebelum banyak pengguna di sini lahir, sehingga tidak perlu untuk konstanta modal lagi bahkan di C. Tapi Jawa mempertahankan semua kebiasaan buruk C yang lama itu .
sbi
3
Jadi, apakah pengecualian melanggar interpretasi Single Exit ini? (Atau sepupu mereka yang lebih primitif setjmp/longjmp,?)
Mason Wheeler
2
Meskipun op bertanya tentang interpretasi pengembalian tunggal saat ini, jawaban ini adalah yang memiliki akar paling historis. Tidak ada gunanya menggunakan pengembalian tunggal sebagai aturan , kecuali jika Anda ingin bahasa Anda cocok dengan kemegahan VB (bukan. NET). Hanya ingat untuk menggunakan logika boolean non-hubung singkat juga.
acelent
912

Gagasan Single Entry, Single Exit (SESE) ini berasal dari bahasa dengan manajemen sumber daya eksplisit , seperti C dan assembly. Dalam C, kode seperti ini akan membocorkan sumber daya:

void f()
{
  resource res = acquire_resource();  // think malloc()
  if( f1(res) )
    return; // leaks res
  f2(res);
  release_resource(res);  // think free()
}

Dalam bahasa tersebut, Anda pada dasarnya memiliki tiga opsi:

  • Replikasi kode pembersihan.
    Ugh. Redundansi selalu buruk.

  • Gunakan a gotountuk melompat ke kode pembersihan.
    Ini membutuhkan kode pembersihan untuk menjadi hal terakhir dalam fungsi. (Dan inilah mengapa beberapa orang berargumentasi bahwa gotoada tempatnya. Dan memang ada - dalam C.)

  • Memperkenalkan variabel lokal dan memanipulasi aliran kontrol melalui itu.
    Kelemahannya adalah bahwa aliran kontrol dimanipulasi melalui sintaks (berpikir break, return, if, while) jauh lebih mudah untuk mengikuti dari aliran kontrol dimanipulasi melalui keadaan variabel (karena variabel tersebut tidak memiliki negara ketika Anda melihat algoritma).

Dalam perakitan itu bahkan lebih aneh, karena Anda dapat melompat ke alamat mana pun dalam suatu fungsi ketika Anda memanggil fungsi itu, yang secara efektif berarti Anda memiliki jumlah titik masuk yang hampir tidak terbatas untuk fungsi apa pun. (Kadang-kadang ini membantu. Pemukul seperti itu adalah teknik yang umum bagi kompiler untuk mengimplementasikan thispenyesuaian pointer yang diperlukan untuk memanggil virtualfungsi dalam beberapa skenario warisan di C ++.)

Ketika Anda harus mengelola sumber daya secara manual, mengeksploitasi opsi memasukkan atau keluar dari fungsi di mana saja mengarah ke kode yang lebih kompleks, dan dengan demikian ke bug. Karena itu, muncul aliran pemikiran yang menyebarkan SESE, untuk mendapatkan kode yang lebih bersih dan lebih sedikit bug.


Namun, ketika suatu bahasa memiliki pengecualian, (hampir) fungsi apa pun mungkin keluar sebelum waktunya di (hampir) titik mana pun, jadi Anda harus tetap membuat ketentuan untuk pengembalian prematur. (Saya pikir finallyini terutama digunakan untuk itu di Jawa dan using(ketika menerapkan IDisposable, finallysebaliknya) di C #; C ++ sebagai gantinya mempekerjakan RAII .) Setelah Anda melakukan ini, Anda tidak dapat gagal untuk membersihkan setelah Anda sendiri karena returnpernyataan awal , jadi apa yang mungkin argumen terkuat yang mendukung SESE telah menghilang.

Itu membuat keterbacaan. Tentu saja, fungsi 200 LoC dengan setengah lusin returnpernyataan ditaburkan secara acak di atasnya bukan gaya pemrograman yang baik dan tidak membuat kode yang dapat dibaca. Tetapi fungsi seperti itu tidak akan mudah dipahami tanpa pengembalian prematur itu.

Dalam bahasa di mana sumber daya tidak atau tidak seharusnya dikelola secara manual, ada sedikit atau tidak ada nilai dalam mematuhi konvensi SESE lama. OTOH, seperti yang saya katakan di atas, SESE sering membuat kode lebih kompleks . Ini adalah dinosaurus yang (kecuali untuk C) tidak cocok dengan sebagian besar bahasa saat ini. Alih-alih membantu pemahaman kode, justru menghambatnya.


Mengapa programmer Java tetap pada ini? Saya tidak tahu, tetapi dari POV (luar) saya, Java mengambil banyak konvensi dari C (di mana mereka masuk akal) dan menerapkannya ke dunia OO (di mana mereka tidak berguna atau sangat buruk), di mana sekarang menempel mereka, tidak peduli berapa biayanya. (Suka konvensi untuk mendefinisikan semua variabel Anda di awal ruang lingkup.)

Programmer berpegang pada semua jenis notasi aneh karena alasan irasional. (Pernyataan struktural yang sangat bersarang - "panah" - dalam bahasa seperti Pascal, pernah dipandang sebagai kode yang indah.) Menerapkan penalaran logis murni untuk ini tampaknya gagal meyakinkan mayoritas dari mereka untuk menyimpang dari cara yang telah mereka tetapkan. Cara terbaik untuk mengubah kebiasaan seperti itu mungkin mengajar mereka sejak dini untuk melakukan yang terbaik, bukan apa yang konvensional. Anda, sebagai guru pemrograman, memilikinya di tangan Anda.:)

sbi
sumber
52
Baik. Di Jawa, kode pembersihan termasuk dalam finallyklausa di mana ia dieksekusi terlepas dari awal returnatau pengecualian.
dan04
15
@ dan04 di Java 7 Anda bahkan tidak perlu finallybanyak waktu.
R. Martinho Fernandes
93
@ Seven: Tentu saja Anda bisa menunjukkan itu! Bahkan, Anda dapat menunjukkan kode yang berbelit-belit dan kompleks dengan fitur apa pun yang juga dapat ditampilkan untuk membuat kode lebih sederhana dan lebih mudah dipahami. Semuanya bisa disalahgunakan. Intinya adalah untuk menulis kode sehingga lebih mudah untuk dipahami , dan ketika itu melibatkan membuang SESE keluar jendela, biarlah, dan sialan kebiasaan lama yang diterapkan pada bahasa yang berbeda. Tapi saya tidak akan ragu untuk mengontrol eksekusi oleh variabel baik jika saya pikir itu membuat kode lebih mudah dibaca. Hanya saja saya tidak ingat pernah melihat kode seperti itu dalam hampir dua dekade.
sbi
21
@ Kar: Memang, itu adalah kekurangan parah dari bahasa-bahasa GC seperti Java bahwa mereka membebaskan Anda dari keharusan untuk membersihkan satu sumber daya, tetapi gagal dengan yang lainnya. (C ++ memecahkan masalah ini untuk semua sumber daya menggunakan RAII .) Tapi saya bahkan tidak berbicara hanya memori (saya hanya memasukkan malloc()dan free()menjadi komentar sebagai contoh), saya berbicara tentang sumber daya secara umum. Saya juga tidak menyiratkan bahwa GC akan menyelesaikan masalah ini. (Saya memang menyebutkan C ++, yang tidak memiliki GC di luar kotak.) Dari apa yang saya mengerti, di Jawa finallydigunakan untuk memecahkan masalah ini.
sbi
10
@ sbi: Lebih penting untuk fungsi (prosedur, metode, dll.) daripada menjadi tidak lebih dari satu halaman adalah fungsi memiliki kontrak yang jelas; jika tidak melakukan sesuatu yang jelas karena telah dipotong untuk memenuhi batasan panjang yang sewenang-wenang, itu buruk. Pemrograman adalah tentang memainkan kekuatan yang berbeda, terkadang saling bertentangan satu sama lain.
Donal Fellows
81

Di satu sisi, pernyataan pengembalian tunggal membuat logging lebih mudah, serta bentuk debugging yang mengandalkan logging. Saya ingat banyak kali saya harus mengurangi fungsi menjadi pengembalian tunggal hanya untuk mencetak nilai pengembalian pada satu titik.

  int function() {
     if (bidi) { print("return 1"); return 1; }
     for (int i = 0; i < n; i++) {
       if (vidi) { print("return 2"); return 2;}
     }
     print("return 3");
     return 3;
  }

Di sisi lain, Anda bisa mengubah ini menjadi function()panggilan itu _function()dan mencatat hasilnya.

perreal
sumber
31
Saya juga akan menambahkan bahwa itu membuat debugging lebih mudah karena Anda hanya perlu mengatur satu breakpoint untuk menangkap semua keluar * dari fungsi. Saya percaya bahwa beberapa IDE membiarkan Anda meletakkan breakpoint pada kurung dekat fungsi untuk melakukan hal yang sama. (* kecuali Anda memanggil keluar)
Skizz
3
Untuk alasan yang sama, ini juga memudahkan untuk memperluas (menambah) fungsi, karena fungsionalitas baru Anda tidak harus dimasukkan sebelum setiap pengembalian. Misalnya Anda perlu memperbarui log dengan hasil panggilan fungsi, misalnya.
JeffSahol
63
Jujur, jika saya mempertahankan kode itu, saya lebih suka memiliki yang masuk akal _function(), dengan returns di tempat-tempat yang tepat, dan pembungkus bernama function()yang menangani penebangan asing, daripada memiliki satu function()dengan logika terdistorsi untuk membuat semua pengembalian masuk ke satu jalan keluar -titik supaya saya bisa memasukkan pernyataan tambahan sebelum titik itu.
ruakh
11
Dalam beberapa debuggers (MSVS) Anda dapat menempatkan breakpoint pada brace penutupan terakhir
Abyx
6
printing! = debugging. Itu sama sekali bukan argumen.
Piotr Perak
53

"Single Entry, Single Exit" berasal dari revolusi Pemrograman Terstruktur pada awal 1970-an, yang dimulai oleh surat Edsger W. Dijkstra kepada Editor " Pernyataan GOTO Dianggap Berbahaya ". Konsep-konsep di balik pemrograman terstruktur dituangkan secara rinci dalam buku klasik "Structured Programming" oleh Ole Johan-Dahl, Edsger W. Dijkstra, dan Charles Anthony Richard Hoare.

"Pernyataan GOTO Dianggap Berbahaya" wajib dibaca, bahkan hari ini. "Structured Programming" sudah ketinggalan zaman, tetapi masih sangat, sangat bermanfaat, dan harus berada di puncak daftar "Harus Dibaca" pengembang, jauh di atas apa pun dari misalnya Steve McConnell. (Bagian Dahl menjabarkan dasar-dasar kelas dalam Simula 67, yang merupakan dasar teknis untuk kelas-kelas dalam C ++ dan semua pemrograman berorientasi objek.)

John R. Strohm
sumber
6
Artikel ini ditulis beberapa hari sebelum C ketika GOTO banyak digunakan. Mereka bukan musuh, tetapi jawaban ini jelas benar. Pernyataan pengembalian yang tidak ada di akhir fungsi secara efektif adalah kebohongan.
user606723
31
Artikel itu juga ditulis pada masa-masa ketika gotobenar-benar dapat pergi ke mana saja , seperti langsung ke beberapa titik acak di fungsi lain, melewati setiap gagasan tentang prosedur, fungsi, tumpukan panggilan, dll. Tidak ada bahasa waras yang mengizinkan hari-hari ini dengan garis lurus goto. C setjmp/ longjmpadalah satu-satunya kasus semi-luar biasa yang saya ketahui, dan bahkan itu membutuhkan kerja sama dari kedua ujungnya. (Semi-ironis bahwa saya menggunakan kata "luar biasa" di sana, mengingat bahwa pengecualian melakukan hal yang hampir sama ...) Pada dasarnya, artikel tersebut mengecilkan praktik yang sudah lama mati.
cHao
5
Dari paragraf terakhir "Pernyataan Goto yang dianggap berbahaya": "dalam [2] Guiseppe Jacopini tampaknya telah membuktikan berlebihan (logis) dari pernyataan pergi. Latihan untuk menerjemahkan diagram alir yang sewenang-wenang secara mekanis menjadi sebuah lompatan. kurang satu, namun, tidak direkomendasikan . Maka diagram alir yang dihasilkan tidak dapat diharapkan lebih transparan daripada yang asli. "
hugomg
10
Apa hubungannya ini dengan pertanyaan? Ya, karya Dijkstra akhirnya mengarah ke bahasa SESE, dan jadi apa? Begitu juga Babbage bekerja. Dan mungkin Anda harus membaca ulang makalah jika menurut Anda ada sesuatu tentang memiliki beberapa titik keluar dalam suatu fungsi. Karena tidak.
jalf
10
@ John, Anda sepertinya mencoba menjawab pertanyaan tanpa benar-benar menjawabnya. Ini adalah daftar bacaan yang bagus, tetapi Anda tidak mengutip atau memparafrasekan apa pun untuk membenarkan klaim Anda bahwa esai dan buku ini memiliki sesuatu untuk dikatakan tentang keprihatinan si penanya. Memang, di luar komentar Anda mengatakan apa-apa yang substansial tentang pertanyaan apapun. Pertimbangkan untuk memperluas jawaban ini.
Shog9
35

Selalu mudah untuk menautkan Fowler.

Salah satu contoh utama yang bertentangan dengan SESE adalah klausa penjaga:

Ganti Nested Conditional dengan Guard Clauses

Gunakan Klausul Penjaga untuk semua kasus khusus

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
return result;
};  

                                                                                                         http://www.refactoring.com/catalog/arrow.gif

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();
    return normalPayAmount();
};  

Untuk informasi lebih lanjut lihat halaman 250 dari Refactoring ...

Pieter B
sumber
11
Contoh buruk lainnya: bisa dengan mudah diperbaiki dengan yang lain-seandainya.
Jack
1
Contoh Anda tidak adil, bagaimana dengan ini: double getPayAmount () {double ret = normalPayAmount (); if (_isDead) ret = deadAmount (); if (_isSeparated) ret = separatedAmount (); if (_isRetired) ret = retiredAmount (); ret ret; };
Charbel
6
@ Charbel Itu bukan hal yang sama. Jika _isSeparateddan _isRetireddapat keduanya benar (dan mengapa itu tidak mungkin?) Anda mengembalikan jumlah yang salah.
hvd
2
@Konchog " conditional bersyarat akan memberikan waktu eksekusi yang lebih baik daripada klausa penjaga " Ini sangat membutuhkan kutipan. Saya ragu bahwa itu bisa dipercaya benar. Dalam kasus ini, misalnya, bagaimana pengembalian awal berbeda dari hubungan arus pendek logis dalam hal kode yang dihasilkan? Bahkan jika itu penting, saya tidak bisa membayangkan kasus di mana perbedaannya akan lebih dari sepotong sangat kecil. Jadi, Anda menerapkan pengoptimalan prematur dengan membuat kode lebih mudah dibaca, hanya untuk memuaskan beberapa poin teoretis yang belum terbukti tentang apa yang Anda pikir mengarah pada kode yang sedikit lebih cepat. Kami tidak melakukannya di sini
underscore_d
1
@underscore_d, Anda benar. itu sangat tergantung pada kompiler, tetapi dapat mengambil lebih banyak ruang .. Lihatlah dua pseudo-rakitan dan mudah untuk melihat mengapa klausa penjaga berasal dari bahasa tingkat tinggi. Tes "A" (1); branch_fail end; tes (2); branch_fail end; tes (3); branch_fail end; {CODE} end: return; Tes "B" (1); branch_good next1; kembali; selanjutnya1: tes (2); branch_good next2; kembali; next2: test (3); branch_good next3; kembali; next3: {CODE} return;
Konchog
11

Saya menulis posting blog tentang topik ini beberapa waktu lalu.

Intinya adalah bahwa aturan ini berasal dari usia bahasa yang tidak memiliki pengumpulan sampah atau penanganan pengecualian. Tidak ada studi formal yang menunjukkan bahwa aturan ini mengarah pada kode yang lebih baik dalam bahasa modern. Jangan ragu untuk mengabaikannya kapan pun ini akan menyebabkan kode lebih pendek atau lebih mudah dibaca. Orang-orang Jawa bersikeras tentang ini secara membabi buta dan tidak mempertanyakan mengikuti aturan usang, tidak berguna.

Pertanyaan ini juga ditanyakan pada Stackoverflow

Anthony
sumber
Hei, saya tidak bisa mencapai tautan itu lagi. Apakah Anda memiliki versi yang dihosting di suatu tempat yang masih dapat diakses?
Nic Hartley
Hai, QPT, tempat yang bagus. Saya telah mengembalikan posting blog dan memperbarui URL di atas. Seharusnya tautan sekarang!
Anthony
Tapi ada lebih dari itu. Jauh lebih mudah untuk mengatur waktu eksekusi yang tepat dengan menggunakan SESE. Conditional bersarang sering dapat di refactored dengan saklar. Ini bukan hanya tentang apakah ada nilai balik.
Konchog
Jika Anda akan mengklaim tidak ada studi formal yang mendukungnya, sebaiknya Anda menautkannya dengan yang bertentangan.
Mehrdad
Mehrdad, Jika ada studi formal yang mendukungnya, perlihatkan. Itu saja. Bersikeras pada bukti menentang adalah menggeser beban pembuktian.
Anthony
7

Satu pengembalian membuat refactoring lebih mudah. Cobalah untuk melakukan "ekstrak metode" ke tubuh bagian dalam loop for yang berisi kembali, istirahat atau melanjutkan. Ini akan gagal karena Anda telah merusak aliran kendali Anda.

Intinya adalah: Saya kira tidak ada yang pura-pura menulis kode yang sempurna. Jadi kode biasanya sedang dalam refactoring untuk "ditingkatkan" dan diperluas. Jadi tujuan saya adalah untuk menjaga kode saya sebagai refactoring ramah mungkin.

Seringkali saya menghadapi masalah bahwa saya harus memformulasi ulang fungsi sepenuhnya jika mengandung pemutus aliran kontrol dan jika saya ingin menambahkan sedikit fungsionalitas saja. Ini sangat rawan kesalahan saat Anda mengubah keseluruhan aliran kendali alih-alih memperkenalkan jalur baru ke sarang yang terisolasi. Jika Anda hanya memiliki satu pengembalian tunggal di akhir atau jika Anda menggunakan penjaga untuk keluar dari loop Anda tentu saja memiliki lebih banyak kode bersarang dan lebih banyak. Tetapi Anda mendapatkan compiler dan IDE yang mendukung kemampuan refactoring.

oopexpert
sumber
Hal yang sama berlaku untuk variabel. Yang merupakan alternatif untuk menggunakan kontrol-aliran-konstruksi seperti pengembalian awal.
Deduplicator
Variabel sebagian besar tidak akan menghalangi Anda untuk memecah kode Anda menjadi beberapa bagian dengan cara bahwa aliran kontrol yang ada dipertahankan. Coba "ekstrak metode". IDE hanya dapat melakukan kontrol aliran preserivng refactorings karena mereka tidak dapat memperoleh semantik dari apa yang telah Anda tulis.
oopexpert
5

Pertimbangkan fakta bahwa banyak pernyataan pengembalian setara dengan memiliki GOTO untuk pernyataan pengembalian tunggal. Ini adalah kasus yang sama dengan pernyataan break. Karena itu, beberapa orang, seperti saya, menganggapnya GOTO untuk semua maksud dan tujuan.

Namun, saya tidak menganggap jenis GOTO ini berbahaya dan tidak akan ragu untuk menggunakan GOTO yang sebenarnya dalam kode saya jika saya menemukan alasan yang bagus untuk itu.

Aturan umum saya adalah bahwa GOTO hanya untuk kontrol aliran. Mereka tidak boleh digunakan untuk perulangan apa pun, dan Anda tidak boleh GOTO 'ke atas' atau 'ke belakang'. (yang merupakan cara istirahat / pengembalian bekerja)

Seperti yang telah disebutkan orang lain, berikut ini adalah harus dibaca Pernyataan GOTO Dianggap Berbahaya
Namun, perlu diingat bahwa ini ditulis pada tahun 1970 ketika GOTO terlalu banyak digunakan. Tidak setiap GOTO berbahaya dan saya tidak akan mengecilkan penggunaannya selama Anda tidak menggunakannya daripada konstruksi normal, melainkan dalam kasus aneh bahwa menggunakan konstruksi normal akan sangat merepotkan.

Saya menemukan bahwa menggunakannya dalam kasus kesalahan di mana Anda perlu melarikan diri dari suatu daerah karena kegagalan yang seharusnya tidak pernah terjadi dalam kasus normal yang berguna di kali. Tetapi Anda juga harus mempertimbangkan untuk menempatkan kode ini ke dalam fungsi yang terpisah sehingga Anda bisa kembali lebih awal daripada menggunakan GOTO ... tapi terkadang itu juga merepotkan.

user606723
sumber
6
Semua konstruksi terstruktur yang menggantikan foto diimplementasikan dalam istilah goto. Misalnya loop, "jika" dan "case". Ini tidak membuat mereka buruk - malah sebaliknya. Juga, itu adalah "maksud dan tujuan".
Anthony
Sentuh, tapi ini tidak berbeda dengan maksud saya ... Itu hanya membuat penjelasan saya sedikit salah. Baiklah.
user606723
GOTO harus selalu baik-baik saja selama (1) target berada dalam metode atau fungsi yang sama dan (2) arahnya diteruskan dalam kode (lewati beberapa kode) dan (3) target tidak berada di dalam struktur bersarang lain (mis. GOTO dari tengah-tengah kasus-kasus ke tengah-kasus lain). Jika Anda mengikuti aturan ini, semua penyalahgunaan GOTO memiliki bau kode yang sangat kuat baik secara visual maupun logis.
Mikko Rantalainen
3

Kompleksitas Siklomatik

Saya telah melihat SonarCube menggunakan pernyataan pengembalian berganda untuk menentukan kompleksitas siklomatik. Jadi semakin banyak pernyataan pengembalian, semakin tinggi kompleksitas siklomatiknya

Return Type Change

Pengembalian berganda berarti kita perlu mengubah di beberapa tempat dalam fungsi saat kita memutuskan untuk mengubah jenis pengembalian kita.

Beberapa Keluar

Lebih sulit untuk di-debug karena logika perlu dipelajari dengan cermat bersama dengan pernyataan kondisional untuk memahami apa yang menyebabkan nilai yang dikembalikan.

Solusi Refactored

Solusi untuk beberapa pernyataan pengembalian adalah menggantinya dengan polimorfisme yang memiliki pengembalian tunggal setelah menyelesaikan objek implementasi yang diperlukan.

Tukang sortir
sumber
3
Pindah dari beberapa pengembalian ke pengaturan nilai kembali di banyak tempat tidak menghilangkan kompleksitas siklomatik, itu hanya menyatukan lokasi keluar. Semua masalah yang dapat ditunjukkan oleh kompleksitas siklomatik dalam konteks yang diberikan tetap ada. "Lebih sulit untuk di-debug karena logika perlu dipelajari dengan cermat bersama dengan pernyataan kondisional untuk memahami apa yang menyebabkan nilai yang dikembalikan" Sekali lagi, logikanya tidak berubah dengan menyatukan pengembalian. Jika Anda harus mempelajari kode dengan hati-hati untuk memahami cara kerjanya, kode itu perlu direaktor ulang, berhenti sepenuhnya.
WillD