Haruskah fungsi hanya memiliki satu pernyataan pengembalian?


780

Apakah ada alasan bagus mengapa lebih baik hanya memiliki satu pernyataan pengembalian dalam suatu fungsi?

Atau apakah boleh kembali dari suatu fungsi segera setelah itu secara logis benar untuk melakukannya, artinya mungkin ada banyak pernyataan pengembalian dalam fungsi?


25
Saya tidak setuju bahwa pertanyaannya adalah agnostik bahasa. Dengan beberapa bahasa, memiliki beberapa pengembalian lebih alami dan nyaman daripada yang lain. Saya akan lebih cenderung mengeluh tentang pengembalian awal dalam fungsi C daripada di C ++ yang menggunakan RAII.
Adrian McCarthy

3
Ini terkait erat dan memiliki jawaban yang sangat baik: programmers.stackexchange.com/questions/118703/…
Tim Schmelter

agnostik bahasa? Jelaskan kepada seseorang yang menggunakan bahasa fungsional bahwa ia harus menggunakan satu pengembalian per fungsi: p
Boiethios

Jawaban:


741

Saya sering memiliki beberapa pernyataan di awal metode untuk kembali untuk situasi "mudah". Sebagai contoh, ini:

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

... dapat dibuat lebih mudah dibaca (IMHO) seperti ini:

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

Jadi ya, saya pikir boleh saja memiliki beberapa "titik keluar" dari fungsi / metode.


83
Sepakat. Meskipun memiliki beberapa titik keluar bisa lepas kendali, saya pasti berpikir itu lebih baik daripada meletakkan seluruh fungsi Anda di blok IF. Gunakan return sesering mungkin agar kode Anda dapat dibaca.
Joshua Carmody

172
Ini yang dikenal sebagai "pernyataan penjaga" adalah Fowler's Refactoring.
Lars Westergren

12
Ketika fungsi disimpan relatif singkat, tidak sulit untuk mengikuti struktur fungsi dengan titik balik di dekat tengah.
KJAWolf

21
Blok besar pernyataan if-else, masing-masing dengan pengembalian? Itu bukan apa-apa. Hal seperti itu biasanya mudah di refactored. (setidaknya variasi keluar tunggal yang lebih umum dengan variabel hasil adalah, jadi saya ragu variasi keluar multi akan lebih sulit.) Jika Anda ingin sakit kepala nyata, lihat kombinasi if-else dan while loop (dikontrol oleh lokal booleans), di mana variabel hasil ditetapkan, mengarah ke titik keluar akhir di akhir metode. Itu adalah satu-satunya ide keluar yang menjadi gila, dan ya saya berbicara dari pengalaman yang sebenarnya harus menghadapinya.
Marcus Andrén

7
'Bayangkan: Anda perlu melakukan metode "IncreaseStuffCallCounter" di akhir fungsi' DoStuff '. Apa yang akan Anda lakukan dalam kasus ini? :) '-DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
Jim Balter

355

Tidak ada yang menyebutkan atau mengutip Kode Lengkap jadi saya akan melakukannya.

17.1 kembali

Minimalkan jumlah pengembalian di setiap rutin . Lebih sulit untuk memahami suatu rutinitas jika, jika membacanya di bagian bawah, Anda tidak menyadari kemungkinan bahwa ia kembali ke suatu tempat di atas.

Gunakan pengembalian ketika meningkatkan keterbacaan . Dalam rutinitas tertentu, setelah Anda tahu jawabannya, Anda ingin segera mengembalikannya ke rutinitas panggilan. Jika rutin didefinisikan sedemikian rupa sehingga tidak memerlukan pembersihan, tidak segera kembali berarti Anda harus menulis lebih banyak kode.


64
+1 untuk nuansa "perkecil" tetapi tidak melarang pengembalian berulang.
Raedwald

13
"sulit untuk dipahami" sangat subyektif, terutama ketika penulis tidak memberikan bukti empiris untuk mendukung klaim umum ... memiliki variabel tunggal yang harus ditetapkan di banyak lokasi bersyarat dalam kode untuk pernyataan pengembalian akhir sama-sama dikenakan untuk "tidak menyadari kemungkinan bahwa variabel ditugaskan di suatu tempat di atas int dia berfungsi!
Heston T. Holtmann

26
Secara pribadi, saya lebih suka kembali lebih awal jika memungkinkan. Mengapa? Nah, ketika Anda melihat kata kunci pengembalian untuk kasus yang diberikan, Anda langsung tahu "saya selesai" - Anda tidak harus terus membaca untuk mencari tahu apa, jika ada, yang terjadi setelahnya.
Mark Simpson

12
@ HestonT.Holtmann: Hal yang membuat Code Complete unik di antara buku-buku pemrograman adalah bahwa saran tersebut didukung oleh bukti empiris.
Adrian McCarthy

9
Ini mungkin jawaban yang diterima, karena menyebutkan bahwa memiliki beberapa poin pengembalian tidak selalu baik, namun terkadang perlu.
Rafid

229

Saya akan mengatakan itu akan sangat tidak bijaksana untuk memutuskan secara sewenang-wenang terhadap beberapa titik keluar karena saya telah menemukan teknik yang berguna dalam praktek berulang kali , bahkan saya sering refactored kode yang ada ke beberapa titik keluar untuk kejelasan. Kita dapat membandingkan dua pendekatan sebagai berikut: -

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

Bandingkan ini dengan kode di mana beberapa exit point yang diijinkan: -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Saya pikir yang terakhir ini jauh lebih jelas. Sejauh yang saya tahu kritik beberapa titik keluar adalah sudut pandang yang agak kuno hari ini.


12
Kejelasan ada di mata yang melihatnya - saya melihat suatu fungsi dan mencari awal tengah dan akhir. Ketika fungsi kecil itu baik-baik saja - tetapi ketika Anda mencoba untuk mencari tahu mengapa ada sesuatu yang rusak dan "Istirahat Kode" ternyata tidak sepele, Anda dapat menghabiskan waktu mencari alasan mengapa ret
Murph

7
Pertama-tama, ini adalah contoh yang dibuat-buat. Kedua, Di mana: string ret; "buka versi kedua? Ketiga, Ret tidak mengandung informasi yang berguna. Keempat, mengapa begitu banyak logika dalam satu fungsi / metode? Kelima, mengapa tidak membuat DidValuesPass (ketik res) lalu RestOfCode () terpisah subfungsi?
Rick Minerich

25
@Rick 1. Tidak dibuat-buat dalam pengalaman saya, ini sebenarnya adalah pola yang saya temui berkali-kali, 2. Ini ditugaskan dalam 'sisa kode', mungkin itu tidak jelas. 3. Um? Itu contoh? 4. Yah saya kira itu dibikin dalam hal ini, tapi mungkin untuk membenarkan ini banyak, 5. bisa melakukan ...
LJS

5
@Rick intinya adalah bahwa seringkali lebih mudah untuk mengembalikan lebih awal daripada membungkus kode dalam pernyataan if yang besar. Itu muncul banyak dalam pengalaman saya, bahkan dengan refactoring yang tepat.
ljs

5
Maksud dari tidak memiliki beberapa pernyataan pengembalian adalah membuat proses debug lebih mudah, sedetik saja untuk keterbacaan. Satu breakpoint di akhir memungkinkan Anda untuk melihat nilai keluar, tanpa pengecualian. Adapun keterbacaan 2 fungsi yang ditampilkan tidak berperilaku sama. Pengembalian default adalah string kosong jika! R.Lassed tapi yang "lebih mudah dibaca" mengubah ini untuk mengembalikan nol. Penulis salah membaca bahwa sebelumnya ada default setelah hanya beberapa baris. Bahkan dalam contoh sepele mudah untuk memiliki pengembalian default tidak jelas, sesuatu pengembalian tunggal pada akhirnya membantu menegakkan.
Mitch

191

Saat ini saya sedang mengerjakan basis kode di mana dua orang yang mengerjakannya secara buta berlangganan teori "satu titik keluar" dan saya dapat memberitahu Anda bahwa dari pengalaman, ini adalah praktik mengerikan yang mengerikan. Itu membuat kode sangat sulit untuk dipelihara dan saya akan tunjukkan alasannya.

Dengan teori "single point of exit", Anda pasti akan berakhir dengan kode yang terlihat seperti ini:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

Ini tidak hanya membuat kode sangat sulit untuk diikuti, tetapi sekarang katakan nanti Anda harus kembali dan menambahkan operasi di antara 1 dan 2. Anda harus membuat indentasi tentang seluruh fungsi panik, dan semoga sukses memastikan semua jika / kondisi lain dan kawat gigi dicocokkan dengan benar.

Metode ini membuat pemeliharaan kode sangat sulit dan rawan kesalahan.


5
@Murph: Anda tidak memiliki cara untuk mengetahui bahwa tidak ada hal lain terjadi setelah setiap kondisi tanpa membaca masing-masing. Biasanya saya akan mengatakan topik semacam ini subjektif, tapi ini jelas salah. Dengan pengembalian setiap kesalahan, Anda selesai, Anda tahu persis apa yang terjadi.
GEOCHET

6
@ Murph: Saya telah melihat kode semacam ini digunakan, disalahgunakan dan digunakan secara berlebihan. Contohnya cukup sederhana, karena tidak ada benar jika / di dalam. Semua yang perlu dipecahkan oleh kode semacam ini adalah satu "yang lain" dilupakan. AFAIK, kode ini sangat membutuhkan pengecualian.
paercebal

15
Anda dapat mengubahnya menjadi ini, menjaga "kemurnian" nya: if (! SUCCEEDED (Operation1 ())) {} else error = OPERATION1FAILED; if (error! = S_OK) {if (SUCCEEDED (Operation2 ())) {} else error = OPERATION2FAILED; } if (error! = S_OK) {if (SUCCEEDED (Operation3 ())) {} else error = OPERATION3FAILED; } // dll.
Joe Pineda

6
Kode ini tidak hanya memiliki satu titik keluar: masing-masing pernyataan "error =" ada di jalur keluar. Ini bukan hanya tentang fungsi yang keluar, ini tentang keluar dari blok atau urutan apa pun.
Jay Bazuzi

6
Saya tidak setuju bahwa pengembalian tunggal "pasti" menghasilkan sarang yang dalam. Contoh Anda dapat ditulis sebagai fungsi linier sederhana dengan pengembalian tunggal (tanpa gotos). Dan jika Anda tidak atau tidak bisa bergantung secara eksklusif pada RAII untuk manajemen sumber daya, maka pengembalian awal berakhir dengan menyebabkan kebocoran atau duplikat kode. Yang paling penting, pengembalian awal membuatnya tidak praktis untuk menegaskan pasca-kondisi.
Adrian McCarthy

72

Pemrograman terstruktur mengatakan Anda seharusnya hanya memiliki satu pernyataan pengembalian per fungsi. Ini untuk membatasi kerumitan. Banyak orang seperti Martin Fowler berpendapat bahwa lebih mudah untuk menulis fungsi dengan banyak pernyataan pengembalian. Dia menyajikan argumen ini dalam buku refactoring klasik yang ditulisnya. Ini bekerja dengan baik jika Anda mengikuti sarannya yang lain dan menulis fungsi-fungsi kecil. Saya setuju dengan sudut pandang ini dan hanya puritan pemrograman terstruktur ketat mematuhi pernyataan kembali tunggal per fungsi.


44
Pemrograman terstruktur tidak mengatakan apa-apa. Beberapa (tetapi tidak semua) orang yang menggambarkan diri mereka sebagai pendukung pemrograman terstruktur mengatakan itu.
Berkeliaran

15
"Ini bekerja dengan baik jika kamu mengikuti sarannya yang lain dan menulis fungsi-fungsi kecil." Inilah poin terpenting. Fungsi kecil jarang membutuhkan banyak poin kembali.

6
@wnoise +1 untuk komentar, sangat benar. Semua "pemrograman terstruktur" mengatakan tidak menggunakan GOTO.
paxos1977

1
@ceretullis: kecuali itu perlu. Tentu saja itu tidak penting, tetapi dapat berguna dalam C. Kernel linux menggunakannya, dan untuk alasan yang baik. GOTO dianggap Berbahaya berbicara tentang penggunaan GOTOuntuk memindahkan aliran kontrol bahkan ketika fungsi ada. Itu tidak pernah mengatakan "tidak pernah menggunakan GOTO".
Esteban Küber

1
"semua tentang mengabaikan 'struktur' kode." - Tidak, justru sebaliknya. "Masuk akal untuk mengatakan mereka harus dihindari" - tidak, tidak.
Jim Balter

62

Seperti yang dicatat oleh Kent Beck ketika mendiskusikan klausa penjaga dalam Pola Implementasi yang membuat rutin memiliki satu pintu masuk dan keluar ...

"adalah untuk mencegah kebingungan yang mungkin terjadi ketika masuk dan keluar dari banyak lokasi dalam rutinitas yang sama. Masuk akal jika diterapkan pada FORTRAN atau program bahasa assembly yang ditulis dengan banyak data global di mana bahkan memahami pernyataan mana yang dieksekusi adalah kerja keras .. "Dengan metode kecil dan sebagian besar data lokal, itu tidak perlu konservatif."

Saya menemukan fungsi yang ditulis dengan klausa penjaga jauh lebih mudah diikuti daripada sekelompok if then elsepernyataan panjang bersarang .


Tentu saja, "sekelompok panjang pernyataan if-then-else" bukan satu-satunya alternatif untuk menjaga klausa.
Adrian McCarthy

@AdrianMcCarthy apakah Anda memiliki alternatif yang lebih baik? Itu akan lebih bermanfaat daripada sarkasme.
shinzou

@kuhaku: Saya tidak yakin saya akan menyebutnya sarkasme. Jawabannya menunjukkan bahwa ini adalah salah satu atau situasi: baik klausa penjaga atau tandan bersarang panjang jika-maka-lain. Banyak (sebagian besar?) Bahasa pemrograman menawarkan banyak cara untuk memfaktorkan logika semacam itu selain klausa penjaga.
Adrian McCarthy

61

Dalam fungsi yang tidak memiliki efek samping, tidak ada alasan bagus untuk memiliki lebih dari satu pengembalian dan Anda harus menuliskannya dengan gaya fungsional. Dalam metode dengan efek samping, hal-hal lebih berurutan (diindeks waktu), jadi Anda menulis dengan gaya imperatif, menggunakan pernyataan kembali sebagai perintah untuk berhenti mengeksekusi.

Dengan kata lain, jika memungkinkan, nikmatilah gaya ini

return a > 0 ?
  positively(a):
  negatively(a);

lebih dari ini

if (a > 0)
  return positively(a);
else
  return negatively(a);

Jika Anda menemukan diri Anda menulis beberapa lapisan kondisi bersarang, mungkin ada cara Anda dapat memperbaiki itu, menggunakan daftar predikat misalnya. Jika Anda menemukan bahwa if dan elses Anda terpisah secara sintaksis, Anda mungkin ingin memecahnya menjadi fungsi yang lebih kecil. Blok bersyarat yang membentang lebih dari satu layar penuh teks sulit dibaca.

Tidak ada aturan keras dan cepat yang berlaku untuk setiap bahasa. Sesuatu seperti memiliki pernyataan pengembalian tunggal tidak akan membuat kode Anda baik. Tetapi kode yang baik akan cenderung memungkinkan Anda untuk menulis fungsi Anda seperti itu.


6
+1 "Jika Anda menemukan bahwa if dan elses Anda terpisah secara sintaksis, Anda mungkin ingin memecahnya menjadi fungsi yang lebih kecil."
Andres Jaan Tack

4
+1, jika ini merupakan masalah, biasanya itu berarti Anda melakukan terlalu banyak dalam satu fungsi. Ini benar-benar membuat saya tertekan bahwa ini bukan jawaban dengan suara terbanyak
Matt Briggs

1
Pernyataan penjaga juga tidak memiliki efek samping, tetapi kebanyakan orang akan menganggapnya berguna. Jadi mungkin ada alasan untuk menghentikan eksekusi lebih awal bahkan jika tidak ada efek samping. Jawaban ini tidak sepenuhnya mengatasi masalah menurut pendapat saya.
Maarten Bodewes

@ MaartenBodewes-owlstead Lihat "Ketat dan Kemalasan"
Apocalisp

43

Saya telah melihatnya dalam standar pengkodean untuk C ++ yang merupakan hang-over dari C, seolah-olah Anda tidak memiliki RAII atau manajemen memori otomatis lainnya maka Anda harus membersihkan setiap pengembalian, yang berarti cut-and-paste dari clean-up atau goto (secara logis sama dengan 'akhirnya' dalam bahasa yang dikelola), yang keduanya dianggap sebagai bentuk yang buruk. Jika praktik Anda adalah menggunakan smart pointer dan koleksi di C ++ atau sistem memori otomatis lainnya, maka tidak ada alasan kuat untuk itu, dan itu menjadi soal keterbacaan, dan lebih dari penilaian panggilan.


Baik dikatakan, meskipun saya percaya bahwa lebih baik untuk menyalin penghapusan ketika mencoba untuk menulis kode yang sangat dioptimalkan (seperti perangkat lunak menguliti jerat 3d kompleks!)
Grant Peters

1
Apa yang membuat Anda percaya itu? Jika Anda memiliki kompiler dengan optimasi yang buruk di mana ada beberapa overhead dalam dereferencing auto_ptr, Anda dapat menggunakan pointer biasa secara paralel. Meskipun akan aneh untuk menulis kode 'dioptimalkan' dengan kompiler yang tidak mengoptimalkan di tempat pertama.
Pete Kirkham

Ini membuat pengecualian yang menarik untuk aturan: Jika bahasa pemrograman Anda tidak mengandung sesuatu yang secara otomatis dipanggil di akhir metode (seperti try... finallydi Jawa) dan Anda perlu melakukan pemeliharaan sumber daya yang dapat Anda lakukan dengan satu kembali pada akhir suatu metode. Sebelum Anda melakukan ini, Anda harus serius mempertimbangkan refactoring kode untuk menyingkirkan situasi.
Maarten Bodewes

@PeteKirkham mengapa goto untuk pembersihan buruk? ya goto bisa digunakan dengan buruk, tetapi penggunaan khusus ini tidak buruk.
q126t

1
@ q126y di C ++, tidak seperti RAII, gagal ketika pengecualian dilemparkan. Di C, ini adalah praktik yang benar-benar valid. Lihat stackoverflow.com/questions/379172/use-goto-or-not
Pete Kirkham

40

Saya bersandar pada gagasan bahwa pernyataan pengembalian di tengah fungsi itu buruk. Anda dapat menggunakan pengembalian untuk membangun beberapa klausa penjaga di bagian atas fungsi, dan tentu saja memberi tahu kompiler apa yang harus kembali di akhir fungsi tanpa masalah, tetapi pengembalian di tengah fungsi bisa mudah dilewatkan dan dapat membuat fungsi lebih sulit untuk ditafsirkan.


38

Apakah ada alasan bagus mengapa lebih baik hanya memiliki satu pernyataan pengembalian dalam suatu fungsi?

Ya ada:

  • Titik keluar tunggal memberikan tempat yang sangat baik untuk menegaskan kondisi pasca Anda.
  • Mampu menempatkan breakpoint debugger pada satu pengembalian di akhir fungsi sering berguna.
  • Lebih sedikit pengembalian berarti lebih sedikit kompleksitas. Kode linear umumnya lebih mudah dipahami.
  • Jika mencoba menyederhanakan fungsi menjadi pengembalian tunggal menyebabkan kerumitan, maka itu insentif untuk melakukan refactor ke fungsi yang lebih kecil, lebih umum, dan lebih mudah dipahami.
  • Jika Anda menggunakan bahasa tanpa destruktor atau jika Anda tidak menggunakan RAII, maka pengembalian tunggal akan mengurangi jumlah tempat yang harus Anda bersihkan.
  • Beberapa bahasa memerlukan titik keluar tunggal (mis. Pascal dan Eiffel).

Pertanyaan itu sering diajukan sebagai dikotomi palsu antara pengembalian berganda atau pernyataan if. Hampir selalu ada solusi ketiga yang sangat linier (tidak ada sarang dalam) dengan hanya satu titik keluar.

Pembaruan : Rupanya pedoman MISRA juga mempromosikan jalan keluar tunggal .

Untuk lebih jelasnya, saya tidak mengatakan itu selalu salah untuk memiliki beberapa pengembalian. Tetapi dengan memberikan solusi yang setara, ada banyak alasan bagus untuk memilih yang dengan pengembalian tunggal.


2
alasan bagus lainnya, mungkin yang terbaik hari ini, untuk memiliki satu pernyataan pengembalian adalah logging. jika Anda ingin menambahkan logging ke metode, Anda bisa menempatkan pernyataan log tunggal yang menyampaikan apa metode kembali.
inor

Seberapa umum pernyataan FORTRAN ENTRY? Lihat docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html . Dan jika Anda berjiwa petualang, Anda bisa mencatat metode dengan AOP dan saran setelahnya
Eric Jablow

1
+1 2 poin pertama sudah cukup untuk meyakinkan saya. Itu dengan paragraf terakhir kedua. Saya tidak akan setuju dengan elemen logging untuk alasan yang sama seperti saya mencegah persyaratan bersarang mendalam karena mereka mendorong melanggar aturan tanggung jawab tunggal yang merupakan alasan utama polimorfisme diperkenalkan ke dalam OOP.
Francis Rodgers

Saya hanya ingin menambahkan bahwa dengan Kontrak C # dan Kontrak, masalah pascakondisi adalah masalah tidak, karena Anda masih dapat menggunakan Contract.Ensuresdengan beberapa titik pengembalian.
julealgon

1
@ q126y: Jika Anda menggunakan gotountuk mendapatkan kode pembersihan umum, maka Anda mungkin telah menyederhanakan fungsi sehingga ada satu returndi akhir kode pembersihan. Jadi Anda bisa mengatakan Anda telah menyelesaikan masalah dengan goto, tetapi saya akan mengatakan Anda menyelesaikannya dengan menyederhanakan menjadi satu return.
Adrian McCarthy

33

Memiliki satu titik keluar memang memberikan keuntungan dalam debugging, karena memungkinkan Anda untuk menetapkan breakpoint tunggal di akhir fungsi untuk melihat nilai apa yang sebenarnya akan dikembalikan.


6
BRAVO! Anda adalah satu - satunya orang yang menyebutkan alasan obyektif ini . Itu sebabnya saya lebih suka titik keluar tunggal dibandingkan beberapa titik keluar. Jika debugger saya dapat menetapkan titik istirahat pada titik keluar mana pun saya mungkin lebih suka beberapa titik keluar. Pendapat saya saat ini adalah orang-orang yang membuat kode beberapa titik keluar melakukannya untuk keuntungan mereka sendiri dengan mengorbankan orang lain yang harus menggunakan debugger pada kode mereka (dan ya saya sedang berbicara tentang Anda semua kontributor open-source yang menulis kode dengan beberapa titik keluar.)
MikeSchinkel

3
IYA. Saya menambahkan kode logging ke sistem yang sebentar-sebentar mengalami kesalahan dalam produksi (di mana saya tidak bisa melangkah maju). Jika kode sebelumnya menggunakan satu-keluar, itu akan jauh lebih mudah.
Michael Blackburn

3
Benar, dalam men-debug sangat membantu. Tetapi dalam prakteknya saya dalam banyak kasus dapat mengatur breakpoint dalam fungsi panggilan, tepat setelah panggilan - efektif untuk hasil yang sama. (Dan posisi itu ditemukan di tumpukan panggilan, tentu saja.) YMMV.
foo

Ini kecuali jika debugger Anda menyediakan fungsi step-out atau step-return (dan masing-masing dan setiap debugger melakukan sejauh yang saya tahu), yang menunjukkan nilai yang dikembalikan segera setelah dikembalikan. Mengubah nilai setelahnya mungkin agak rumit jika tidak ditugaskan ke variabel.
Maarten Bodewes

7
Saya belum pernah melihat debugger dalam waktu yang lama yang tidak akan memungkinkan Anda untuk menetapkan breakpoint pada "tutup" metode (akhir, kurung kurawal kanan, apa pun bahasa Anda) dan tekan breakpoint itu di mana pun, atau berapa banyak , mengembalikan statemet dalam metode. Juga, bahkan jika fungsi Anda hanya memiliki satu pengembalian, itu tidak berarti Anda tidak dapat keluar dari fungsi dengan pengecualian (eksplisit atau diwariskan). Jadi, saya pikir ini bukan poin yang valid.
Scott Gartner

19

Secara umum saya mencoba untuk hanya memiliki satu titik keluar dari suatu fungsi. Namun, ada kalanya melakukan hal itu pada akhirnya menciptakan fungsi tubuh yang lebih kompleks daripada yang diperlukan, dalam hal ini lebih baik memiliki beberapa titik keluar. Itu benar-benar harus menjadi "panggilan penilaian" berdasarkan kompleksitas yang dihasilkan, tetapi tujuannya harus sesedikit mungkin titik keluar tanpa mengorbankan kompleksitas dan pemahaman.


"Secara umum saya mencoba untuk hanya memiliki satu titik keluar dari suatu fungsi" - mengapa? "tujuannya harus sesedikit mungkin titik keluar" - mengapa? Dan mengapa 19 orang memilih jawaban ini?
Jim Balter

@ JimBalter Pada akhirnya, itu bermuara pada preferensi pribadi. Semakin banyak titik keluar biasanya mengarah ke metode yang lebih kompleks (meskipun tidak selalu) dan membuatnya lebih sulit untuk dipahami seseorang.
Scott Dorman

"itu bermuara pada preferensi pribadi." - Dengan kata lain, Anda tidak dapat menawarkan alasan. "Lebih banyak titik keluar biasanya mengarah ke metode yang lebih kompleks (meskipun tidak selalu)" - Tidak, sebenarnya, mereka tidak melakukannya. Dengan dua fungsi yang secara logis setara, satu dengan klausa penjaga dan satu dengan keluar tunggal, yang terakhir akan memiliki kompleksitas siklomatik yang lebih tinggi, yang banyak penelitian menunjukkan hasil dalam kode yang lebih rentan kesalahan dan sulit dipahami. Anda akan mendapat manfaat dengan membaca tanggapan lain di sini.
Jim Balter

14

Tidak, karena kita tidak hidup di tahun 1970-an lagi . Jika fungsi Anda cukup lama sehingga banyak pengembalian merupakan masalah, itu terlalu lama.

(Terlepas dari kenyataan bahwa setiap fungsi multi-line dalam bahasa dengan pengecualian akan memiliki beberapa titik keluar.)


14

Preferensi saya adalah keluar satu kali kecuali itu benar-benar menyulitkan. Saya telah menemukan bahwa dalam beberapa kasus, beberapa titik yang ada dapat menutupi masalah desain lain yang lebih signifikan:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

Saat melihat kode ini, saya akan segera bertanya:

  • Apakah 'foo' tidak pernah dibatalkan?
  • Jika demikian, berapa banyak klien 'DoStuff' yang pernah memanggil fungsi dengan 'foo' nol?

Tergantung pada jawaban atas pertanyaan-pertanyaan ini, mungkin itu

  1. cek tidak ada gunanya karena itu tidak pernah benar (mis. itu harus menjadi penegasan)
  2. pemeriksaan ini sangat jarang benar dan jadi mungkin lebih baik untuk mengubah fungsi-fungsi pemanggil khusus karena mereka mungkin harus mengambil tindakan lain pula.

Dalam kedua kasus di atas kode mungkin dapat dikerjakan ulang dengan pernyataan untuk memastikan bahwa 'foo' tidak pernah nol dan penelepon yang relevan berubah.

Ada dua alasan lain (khusus saya pikir untuk kode C ++) di mana ada beberapa sebenarnya dapat memiliki pengaruh negatif . Mereka adalah ukuran kode, dan optimisasi kompiler.

Objek non-POD C ++ dalam lingkup pada saat keluar dari suatu fungsi akan memanggil destruktornya. Di mana ada beberapa pernyataan pengembalian, mungkin ada kasus bahwa ada objek yang berbeda dalam ruang lingkup dan daftar destruktor untuk memanggil akan berbeda. Oleh karena itu kompiler perlu membuat kode untuk setiap pernyataan kembali:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

Jika ukuran kode merupakan masalah - maka ini mungkin sesuatu yang layak dihindari.

Masalah lainnya berkaitan dengan "Optimalisasi Nilai Pengembalian Bernama" (alias Copy Elision, ISO C ++ '03 12.8 / 15). C ++ memungkinkan implementasi untuk melewati pemanggilan copy constructor jika dapat:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

Hanya dengan mengambil kode apa adanya, objek 'a1' dikonstruksikan dalam 'foo' dan kemudian salinan konstruksinya akan dipanggil untuk membangun 'a2'. Namun, copy elision memungkinkan kompiler untuk membangun 'a1' di tempat yang sama pada stack sebagai 'a2'. Karena itu tidak perlu "menyalin" objek ketika fungsi kembali.

Beberapa titik keluar mempersulit pekerjaan kompiler dalam mencoba mendeteksi ini, dan setidaknya untuk versi VC ++ yang relatif baru, optimasi tidak terjadi di mana fungsi tubuh memiliki beberapa pengembalian. Lihat Optimasi Nilai Pengembalian Bernama dalam Visual C ++ 2005 untuk detail lebih lanjut.


1
Jika Anda mengambil semua kecuali karakter terakhir dari contoh C ++ Anda, kode untuk menghancurkan B dan kemudian C dan B, masih harus dihasilkan ketika ruang lingkup pernyataan if berakhir, sehingga Anda benar-benar mendapatkan apa-apa dengan tidak memiliki banyak pengembalian .
Eclipse

4
+1 Dan waaaaay di bagian bawah daftar kami memiliki alasan NYATA praktik pengkodean ini ada - NRVO. Namun, ini adalah optimasi mikro; dan, seperti semua praktik optimasi mikro, mungkin dimulai oleh "pakar" berusia 50 tahun yang terbiasa memprogram pada PDP-8 300 kHz, dan tidak memahami pentingnya kode yang bersih dan terstruktur. Secara umum, ikuti saran Chris S dan gunakan beberapa pernyataan pengembalian kapan pun masuk akal.
BlueRaja - Danny Pflughoeft

Meskipun saya tidak setuju dengan preferensi Anda (dalam pikiran saya, saran Penegasan Anda juga merupakan titik balik, seperti halnya throw new ArgumentNullException()dalam C # dalam kasus ini), saya benar-benar menyukai pertimbangan Anda yang lain, mereka semua valid untuk saya, dan bisa kritis dalam beberapa konteks niche.
julealgon

Ini penuh dengan sedotan. Pertanyaan mengapa foosedang diuji tidak ada hubungannya dengan subjek, yaitu apakah harus melakukan if (foo == NULL) return; dowork; atauif (foo != NULL) { dowork; }
Jim Balter

11

Memiliki satu titik keluar mengurangi Kompleksitas Siklomatik dan oleh karena itu, secara teori , mengurangi kemungkinan Anda akan memasukkan bug ke dalam kode Anda ketika Anda mengubahnya. Namun praktiknya, cenderung menunjukkan bahwa pendekatan yang lebih pragmatis diperlukan. Karena itu saya cenderung bertujuan untuk memiliki satu titik keluar, tetapi mengizinkan kode saya untuk memiliki beberapa jika itu lebih mudah dibaca.


Sangat mendalam. Meskipun, saya merasa bahwa sampai seorang programmer tahu kapan harus menggunakan beberapa titik keluar, mereka harus dibatasi menjadi satu.
Rick Minerich

5
Tidak juga. Kompleksitas siklus "jika (...) kembali; ... kembali;" sama dengan "if (...) {...} return;". Mereka berdua memiliki dua jalan melalui mereka.
Steve Emmerson

11

Saya memaksakan diri untuk menggunakan hanya satu returnpernyataan, karena akan menghasilkan kode bau. Biarkan saya jelaskan:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(Kondisinya arbriter ...)

Semakin banyak kondisi, semakin besar fungsinya, semakin sulit untuk dibaca. Jadi jika Anda terbiasa dengan bau kode, Anda akan menyadarinya, dan ingin memperbaiki kode tersebut. Dua solusi yang mungkin adalah:

  • Pengembalian ganda
  • Refactoring ke dalam fungsi yang terpisah

Pengembalian Berganda

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

Fungsi yang terpisah

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

Memang, ini lebih panjang dan sedikit berantakan, tetapi dalam proses refactoring fungsi dengan cara ini, kami sudah

  • menciptakan sejumlah fungsi yang dapat digunakan kembali,
  • membuat fungsi lebih mudah dibaca manusia, dan
  • fokus fungsi adalah pada mengapa nilainya benar.

5
-1: Contoh buruk. Anda telah menghilangkan penanganan pesan kesalahan. Jika itu tidak diperlukan, isCorrect dapat diekspresikan seperti return xx && yy && zz; di mana xx, yy dan z adalah ekspresi isEqual, isDouble dan isThird.
kauppi

10

Saya akan mengatakan Anda harus memiliki sebanyak yang diperlukan, atau yang membuat kode lebih bersih (seperti klausa penjaga ).

Saya pribadi tidak pernah mendengar / melihat "praktik terbaik" yang mengatakan bahwa Anda hanya boleh memiliki satu pernyataan pengembalian.

Untuk sebagian besar, saya cenderung untuk keluar dari fungsi sesegera mungkin berdasarkan jalur logika (klausa penjaga adalah contoh yang sangat baik dari ini).


10

Saya percaya bahwa banyak pengembalian biasanya baik (dalam kode yang saya tulis dalam C #). Gaya pengembalian tunggal adalah peninggalan dari C. Tapi Anda mungkin tidak mengkodekan dalam C.

Tidak ada hukum yang mewajibkan hanya satu titik keluar untuk metode dalam semua bahasa pemrograman . Beberapa orang bersikeras pada keunggulan gaya ini, dan kadang-kadang mereka mengangkatnya ke "aturan" atau "hukum" tetapi kepercayaan ini tidak didukung oleh bukti atau penelitian.

Lebih dari satu gaya pengembalian mungkin kebiasaan buruk dalam kode C, di mana sumber daya harus secara eksplisit dialokasikan, tetapi bahasa seperti Java, C #, Python atau JavaScript yang memiliki konstruksi seperti pengumpulan dan try..finallyblok sampah otomatis (danusing blok dalam C # ), dan argumen ini tidak berlaku - dalam bahasa-bahasa ini, sangat jarang membutuhkan alokasi sumber daya manual yang terpusat.

Ada kasus di mana pengembalian tunggal lebih mudah dibaca, dan kasus di mana tidak kembali. Lihat apakah itu mengurangi jumlah baris kode, membuat logika lebih jelas atau mengurangi jumlah kawat gigi dan inden atau variabel sementara.

Karena itu, gunakan pengembalian sebanyak mungkin sesuai dengan kepekaan artistik Anda, karena ini adalah masalah tata letak dan keterbacaan, bukan masalah teknis.

Saya telah membicarakan hal ini lebih jauh di blog saya .


10

Ada hal-hal baik untuk dikatakan tentang memiliki satu titik keluar, sama seperti ada hal-hal buruk untuk dikatakan tentang pemrograman "panah" yang tak terhindarkan yang dihasilkan.

Jika menggunakan beberapa titik keluar selama validasi input atau alokasi sumber daya, saya mencoba menempatkan semua 'kesalahan-keluar' dengan sangat jelas di bagian atas fungsi.

Baik artikel Pemrograman Spartan dari "SSDSLPedia" dan artikel titik keluar fungsi tunggal dari "Portland Pattern Repository's Wiki" memiliki beberapa argumen mendalam tentang hal ini. Juga, tentu saja, ada posting ini untuk dipertimbangkan.

Jika Anda benar-benar ingin satu titik keluar (dalam bahasa apa pun yang tidak diaktifkan) sebagai contoh untuk melepaskan sumber daya di satu tempat, saya menemukan aplikasi goto yang cermat menjadi baik; lihat misalnya ini contoh yang agak dibuat-buat (dikompresi untuk menyimpan layar real-estate):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

Secara pribadi saya, secara umum, tidak suka pemrograman panah lebih dari saya tidak suka beberapa titik keluar, meskipun keduanya berguna ketika diterapkan dengan benar. Yang terbaik, tentu saja, adalah menyusun program Anda untuk tidak membutuhkan keduanya. Memecah fungsi Anda menjadi beberapa bagian biasanya membantu :)

Meskipun ketika melakukan itu, saya menemukan saya akhirnya memiliki beberapa titik keluar seperti dalam contoh ini, di mana beberapa fungsi yang lebih besar telah dipecah menjadi beberapa fungsi yang lebih kecil:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

Bergantung pada proyek atau pedoman pengkodean, sebagian besar kode pelat-ketel dapat diganti dengan makro. Sebagai catatan, memecahnya dengan cara ini membuat fungsi g0, g1, g2 sangat mudah untuk diuji secara individual.

Jelas, dalam bahasa OO dan bahasa yang diaktifkan pengecualian, saya tidak akan menggunakan pernyataan if seperti itu (atau sama sekali, jika saya bisa lolos dengan sedikit usaha), dan kodenya akan jauh lebih sederhana. Dan tidak panah. Dan sebagian besar pengembalian non-final mungkin akan menjadi pengecualian.

Pendeknya;

  • Beberapa pengembalian lebih baik daripada banyak pengembalian
  • Lebih dari satu pengembalian lebih baik daripada panah besar, dan menjaga klausa biasanya ok.
  • Pengecualian bisa / harus mungkin menggantikan sebagian besar 'klausa penjagaan' bila memungkinkan.

Contoh macet untuk y <0, karena mencoba membebaskan pointer NULL ;-)
Erich Kitzmueller

2
opengroup.org/onlinepubs/009695399/functions/free.html "Jika ptr adalah pointer nol, tidak ada tindakan yang akan terjadi."
Henrik Gustafsson,

1
Tidak itu tidak akan crash, karena meneruskan NULL ke bebas adalah no-op yang ditentukan. Ini adalah kesalahpahaman yang sangat umum bahwa Anda harus menguji NULL terlebih dahulu.
hlovdal

Pola "panah" bukanlah alternatif yang tak terhindarkan. Ini adalah dikotomi yang salah.
Adrian McCarthy

9

Anda tahu pepatah - keindahan ada di mata yang melihatnya .

Beberapa orang bersumpah oleh NetBeans dan beberapa oleh IntelliJ IDEA , beberapa oleh Python dan beberapa oleh PHP .

Di beberapa toko Anda bisa kehilangan pekerjaan jika Anda bersikeras melakukan ini:

public void hello()
{
   if (....)
   {
      ....
   }
}

Pertanyaannya adalah semua tentang visibilitas dan pemeliharaan.

Saya kecanduan menggunakan aljabar boolean untuk mengurangi dan menyederhanakan logika dan penggunaan mesin negara. Namun, ada kolega masa lalu yang percaya bahwa mempekerjakan saya "teknik matematika" dalam pengkodean tidak cocok, karena itu tidak akan terlihat dan dipelihara. Dan itu akan menjadi praktik yang buruk. Maaf orang-orang, teknik yang saya terapkan sangat terlihat dan dapat dipelihara untuk saya - karena ketika saya kembali ke kode enam bulan kemudian, saya akan memahami kode dengan jelas daripada melihat kekacauan spaghetti pepatah.

Hai teman (seperti mantan klien sebelumnya) lakukan apa yang Anda inginkan selama Anda tahu cara memperbaikinya ketika saya membutuhkan Anda untuk memperbaikinya.

Saya ingat 20 tahun yang lalu, seorang rekan saya dipecat karena menggunakan apa yang hari ini disebut strategi pengembangan yang gesit . Dia punya rencana tambahan yang cermat. Tetapi manajernya meneriakinya, "Anda tidak dapat secara bertahap merilis fitur kepada pengguna! Anda harus tetap menggunakan air terjun ." Tanggapannya kepada manajer adalah bahwa pengembangan tambahan akan lebih tepat untuk kebutuhan pelanggan. Dia percaya dalam mengembangkan untuk kebutuhan pelanggan, tetapi manajer percaya pada pengkodean untuk "kebutuhan pelanggan".

Kami sering bersalah karena melanggar normalisasi data, batas MVP dan MVC . Kami sebaris bukannya membangun fungsi. Kami mengambil jalan pintas.

Secara pribadi, saya percaya bahwa PHP adalah praktik yang buruk, tetapi apa yang saya tahu. Semua argumen teoretis bermuara pada upaya memenuhi satu set aturan

kualitas = presisi, rawatan dan profitabilitas.

Semua aturan lain menghilang ke latar belakang. Dan tentu saja aturan ini tidak pernah pudar:

Kemalasan adalah keutamaan seorang programmer yang baik.


1
"Hai teman (seperti mantan klien sebelumnya) lakukan apa yang kamu inginkan selama kamu tahu bagaimana memperbaikinya ketika aku membutuhkanmu untuk memperbaikinya." Masalah: seringkali bukan Anda yang 'memperbaikinya'.
Dan Barron

Memberi +1 pada jawaban ini karena saya setuju dengan tujuan Anda tetapi belum tentu bagaimana Anda sampai di sana. Saya berpendapat bahwa ada tingkat pemahaman. yaitu pemahaman bahwa karyawan A memiliki pengalaman pemrograman setelah 5 tahun dan 5 tahun dengan sebuah perusahaan sangat berbeda dengan karyawan B, lulusan perguruan tinggi baru yang baru mulai dengan sebuah perusahaan. Maksud saya adalah bahwa jika karyawan A adalah satu-satunya orang yang dapat memahami kode, itu TIDAK dapat dipertahankan sehingga kita semua harus berusaha untuk menulis kode yang dapat dipahami karyawan B. Di sinilah seni sejati ada dalam perangkat lunak.
Francis Rodgers

9

Saya condong ke arah menggunakan klausa penjaga untuk kembali lebih awal dan sebaliknya keluar pada akhir metode. Aturan masuk dan keluar tunggal memiliki signifikansi historis dan sangat membantu ketika berhadapan dengan kode lawas yang berlari ke 10 halaman A4 untuk metode C ++ tunggal dengan banyak pengembalian (dan banyak cacat). Baru-baru ini, praktik baik yang diterima adalah menjaga metode tetap kecil yang membuat banyak jalan keluar lebih sedikit dari hambatan untuk memahami. Dalam contoh Kronoz berikut yang disalin dari atas, pertanyaannya adalah apa yang terjadi pada // Sisa kode ... ?:

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Saya menyadari bahwa contohnya agak dibuat-buat tetapi saya akan tergoda untuk mengubah loop foreach menjadi pernyataan LINQ yang kemudian dapat dianggap sebagai klausa penjaga. Sekali lagi, dalam contoh buat maksud dari kode ini tidak jelas dan someFunction () mungkin memiliki beberapa efek samping lain atau hasilnya dapat digunakan dalam // Istirahat kode ... .

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

Memberi fungsi refactored berikut:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

Tidakkah C ++ memiliki pengecualian? Lalu mengapa Anda kembali nullbukannya melemparkan pengecualian yang menunjukkan bahwa argumen tersebut tidak diterima?
Maarten Bodewes

1
Seperti yang saya tunjukkan, kode contoh disalin dari jawaban sebelumnya ( stackoverflow.com/a/36729/132599 ). Contoh asli mengembalikan nol dan refactoring untuk melempar pengecualian argumen tidak penting sampai saya mencoba untuk membuat atau ke pertanyaan asli. Sebagai praktik yang baik, maka ya saya biasanya (dalam C #) melempar ArgumentNullException dalam klausa penjaga daripada mengembalikan nilai nol.
David Clarke

7

Satu alasan bagus yang dapat saya pikirkan adalah untuk pemeliharaan kode: Anda memiliki satu titik keluar. Jika Anda ingin mengubah format hasil, ..., itu hanya jauh lebih mudah untuk diterapkan. Juga, untuk debugging, Anda bisa menempel breakpoint di sana :)

Karena itu, saya pernah harus bekerja di perpustakaan di mana standar pengkodean memberlakukan 'satu pernyataan pengembalian per fungsi', dan saya merasa cukup sulit. Saya menulis banyak kode perhitungan numerik, dan sering kali ada 'kasus khusus', sehingga kode tersebut menjadi cukup sulit untuk diikuti ...


Itu tidak benar-benar membuat perbedaan. Jika Anda mengubah jenis variabel lokal yang dikembalikan, maka Anda harus memperbaiki semua tugas ke variabel lokal. Dan mungkin lebih baik untuk mendefinisikan metode dengan tanda tangan yang berbeda, karena Anda harus memperbaiki semua panggilan metode juga.
Maarten Bodewes

@ MaartenBodewes-owlstead - Ini bisa membuat perbedaan. Untuk menyebutkan hanya dua contoh di mana Anda tidak harus memperbaiki semua tugas ke variabel lokal atau mengubah panggilan metode, fungsi mungkin mengembalikan tanggal sebagai string (variabel lokal akan menjadi tanggal aktual, diformat sebagai string hanya di saat terakhir), atau mungkin mengembalikan angka desimal dan Anda ingin mengubah jumlah tempat desimal.
nnnnnn

@nnnnnn OK, jika Anda ingin melakukan pemrosesan posting pada output ... Tapi saya hanya akan menghasilkan metode baru yang melakukan pemrosesan posting dan biarkan yang lama saja. Itu hanya sedikit lebih sulit untuk refactor, tetapi Anda harus memeriksa apakah panggilan lain kompatibel dengan format baru. Tapi itu adalah alasan yang sah.
Maarten Bodewes

7

Beberapa titik keluar baik untuk fungsi yang cukup kecil - yaitu, fungsi yang dapat dilihat pada satu layar secara keseluruhan. Jika fungsi yang panjang juga mencakup beberapa titik keluar, itu menandakan bahwa fungsi tersebut dapat dicincang lebih lanjut.

Yang mengatakan saya menghindari beberapa fungsi keluar kecuali benar-benar diperlukan . Saya telah merasakan sakit bug yang disebabkan oleh beberapa penyimpangan dalam garis yang tidak jelas dalam fungsi yang lebih kompleks.


6

Saya telah bekerja dengan standar pengkodean yang mengerikan yang memaksa satu jalur keluar pada Anda dan hasilnya hampir selalu spaghetti tidak terstruktur jika fungsinya sama sekali tidak sepele - Anda berakhir dengan banyak jeda dan terus yang hanya menghalangi.


Belum lagi harus mengatakan pada pikiran Anda untuk melewatkan ifpernyataan di depan setiap panggilan metode yang mengembalikan kesuksesan atau tidak :(
Maarten Bodewes

6

Satu titik keluar - semuanya setara - membuat kode lebih mudah dibaca. Tapi ada yang menarik: konstruksi populer

resulttype res;
if if if...
return res;

adalah palsu, "res =" tidak jauh lebih baik daripada "kembali". Ini memiliki pernyataan pengembalian tunggal, tetapi beberapa titik di mana fungsi sebenarnya berakhir.

Jika Anda memiliki fungsi dengan banyak pengembalian (atau "res =" s), seringkali merupakan ide bagus untuk memecahnya menjadi beberapa fungsi yang lebih kecil dengan titik keluar tunggal.


6

Kebijakan saya yang biasa adalah hanya memiliki satu pernyataan kembali di akhir fungsi kecuali kompleksitas kode sangat berkurang dengan menambahkan lebih banyak. Sebenarnya, saya lebih suka penggemar Eiffel, yang memberlakukan satu-satunya aturan pengembalian dengan tidak memiliki pernyataan pengembalian (hanya ada variabel 'hasil' yang dibuat secara otomatis untuk memasukkan hasil Anda).

Tentu saja ada kasus-kasus di mana kode dapat dibuat lebih jelas dengan beberapa pengembalian daripada versi yang jelas tanpa mereka. Orang bisa berpendapat bahwa diperlukan lebih banyak pengerjaan ulang jika Anda memiliki fungsi yang terlalu rumit untuk dapat dipahami tanpa banyak pernyataan pengembalian, tetapi kadang-kadang ada baiknya bersikap pragmatis tentang hal-hal seperti itu.


5

Jika Anda berakhir dengan lebih dari beberapa pengembalian mungkin ada sesuatu yang salah dengan kode Anda. Kalau tidak, saya akan setuju bahwa kadang-kadang senang bisa kembali dari beberapa tempat dalam subrutin, terutama ketika itu membuat kode lebih bersih.

Perl 6: Contoh Buruk

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

akan lebih baik ditulis seperti ini

Perl 6: Contoh Baik

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

Perhatikan ini hanyalah contoh cepat


Ok Kenapa ini ditolak? Ini tidak seperti itu bukan pendapat.
Brad Gilbert

5

Saya memilih Single return di akhir sebagai pedoman. Ini membantu penanganan pembersihan kode umum ... Misalnya, lihat kode berikut ...

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

Anda memberi suara untuk pengembalian tunggal - dalam kode C. Tetapi bagaimana jika Anda mengkode dalam bahasa yang memiliki koleksi Sampah dan mencoba .. akhirnya terblokir?
Anthony

4

Ini mungkin perspektif yang tidak biasa, tetapi saya pikir siapa pun yang percaya bahwa pernyataan pengembalian berganda lebih disukai tidak pernah harus menggunakan debugger pada mikroprosesor yang hanya mendukung 4 breakpoint perangkat keras. ;-)

Sementara masalah "kode panah" sepenuhnya benar, satu masalah yang tampaknya hilang ketika menggunakan beberapa pernyataan kembali adalah dalam situasi di mana Anda menggunakan debugger. Anda tidak memiliki posisi tangkap semua yang nyaman untuk meletakkan breakpoint untuk menjamin bahwa Anda akan melihat pintu keluar dan karenanya kondisi pengembalian.


5
Itu hanya semacam optimasi prematur yang berbeda. Anda tidak boleh mengoptimalkan untuk kasus khusus. Jika Anda mendapati diri Anda banyak men-debug bagian kode tertentu, ada yang lebih salah dengan jumlah titik keluar yang dimilikinya.
Wedge

Tergantung pada debugger Anda juga.
Maarten Bodewes

4

Semakin banyak pernyataan pengembalian yang Anda miliki dalam suatu fungsi, semakin tinggi kompleksitas dalam satu metode itu. Jika Anda bertanya-tanya apakah Anda memiliki terlalu banyak pernyataan pengembalian, Anda mungkin ingin bertanya pada diri sendiri apakah Anda memiliki terlalu banyak baris kode dalam fungsi itu.

Tapi, tidak, tidak ada yang salah dengan satu / banyak pernyataan pengembalian. Dalam beberapa bahasa, ini adalah praktik yang lebih baik (C ++) daripada yang lain (C).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.