“Sadece bir dönüş” kavramı nereden geldi?


1055

Sık sık söylemek programcılar konuşmak " aynı yöntemle birden fazla dönüş ifadeleri koymayın. " Bana, benim hemen hepsi bu nedenleri anlatmak için onlara sorduğunuzda " kodlama standardı. Öyle diyor " "veya Kafa karıştırıcı. " Bana bir geri dönüş ifadesi olan çözümler gösterdiklerinde, kod bana daha çirkin görünüyor. Örneğin:

if (condition)
   return 42;
else
   return 97;

" Bu çirkin, yerel bir değişken kullanman gerekiyor! "

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

Bu% 50 kod kısıtlaması, programın daha kolay anlaşılmasını nasıl sağlar? Şahsen daha zor buluyorum, çünkü devlet alanı daha kolay önlenebilecek başka bir değişkenle arttı.

Tabii ki normalde sadece şunu yazardım:

return (condition) ? 42 : 97;

Ancak birçok programcı şartlı işletmeciden kaçınır ve uzun biçimi tercih eder.

Bu “sadece bir dönüş” nosyonu nereden geldi? Bu konvansiyonun ortaya çıkmasının tarihsel bir nedeni var mı?


2
Bu, Guard Clause refactoring ile bir şekilde bağlantılı. stackoverflow.com/a/8493256/679340 Guard Clause, yöntemlerin başına iade ekleyecektir. Ve bence kodu çok daha temiz hale getiriyor.
Piotr Perak

3
Yapılandırılmış programlama kavramından geldi. Bazıları, yalnızca bir dönüşe sahip olmanın, kodu hemen dönmeden veya kolayca hata ayıklamadan önce bir şeyi yapmak için kolayca değiştirmenize izin verdiğini iddia edebilir.
martinkunev

3
Bence bu örnek, bir şekilde veya diğerinde güçlü bir fikre sahip olamayacağım kadar basit bir durum. tek giriş-tek çıkış ideali, 15 iade ifadesi ve hiç geri dönmeyen iki dal gibi çılgın durumlardan bizi uzak tutmak için daha fazla şey!
mendota

2
Bu şimdiye kadar okuduğum en kötü yazılardan biri. Yazar, OOP'sinin saflığı hakkında gerçekten bir şeyi nasıl elde edeceğini düşünmekten daha fazla zaman harcamaktadır. İfade ve değerlendirme ağaçlarının değeri vardır, ancak bunun yerine normal bir işlev yazabileceğiniz zaman olmaz.
DeadMG

3
Durumu tamamen ortadan kaldırmalısın. Cevap 42.
cambunctious

Yanıtlar:


1119

Çoğu programlama dili, FORTRAN veya COBOL'de yapıldığında "Tek Giriş, Tek Çıkış" yazılmıştır. Yaygın olarak yanlış yorumlandı, çünkü modern diller Dijkstra'nın uyardığı uygulamaları desteklemiyor.

"Tek Giriş", "işlevler için alternatif giriş noktaları oluşturma" anlamına geliyordu. Assembly dilinde, elbette, her talimatta bir fonksiyon girmek mümkündür. FORTRAN ENTRYifadesiyle işlevler için birden fazla girişi destekledi :

      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)

"Tek Çıkış" bir işlev yalnızca dönmelidir anlamına geliyordu için açıklamalar derhal çağrı aşağıdadır: tek bir yerde. O did değil bir işlev yalnızca dönmelidir anlamına gelen tek bir yerde. Ne zaman Yapısal Programlama yazılmıştır bir işlev farklı bir konuma geri dönerek bir hata belirtmek için, bu yaygın bir uygulama oldu. FORTRAN bunu "alternatif getiri" ile destekledi:

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'

Her iki teknik de yüksek oranda hata eğilimli idi. Alternatif girişlerin kullanımı genellikle bazı değişkenleri başlatılmamış olarak bıraktı. Alternatif geri dönüşlerin kullanılması, bir GOTO ifadesinin tüm sorunlarına sahipti, ek olarak dallanma koşulunun dalın bitişiğinde değil, alt rutinde bir yerde olması ek bir komplikasyona sahipti.


38
Ve spagetti kodunu unutma . Alt yordamların bir dönüş yerine bir GOTO kullanarak çıkıp işlev çağrısı parametrelerini bırakıp, yığın üzerinde adres bırakmalarını bilmiyordu. Tek çıkış, en azından tüm kod yollarını bir RETURN deyimine dönüştürmenin bir yolu olarak tanıtıldı.
TMN

2
@TMN: İlk günlerde, çoğu makinenin donanım yığını yoktu. Özyineleme genellikle desteklenmedi. Alt program argümanları ve dönüş adresi, alt program koduna bitişik sabit konumlarda saklandı. Dönüş sadece dolaylı bir adımdı.
kevin cl

5
@kevin: Evet, ama sana göre bu artık ne icat edildiği anlamına gelmiyor. (BTW, Fred’in “Tek Çıkış” ın şu anki yorumu için tercih edildiğini sorduğuna gerçekten makul bir şekilde eminim .) Ayrıca, C, o constzamandan beri birçok kullanıcının doğumundan önce olduğu için sermaye sabitlerine gerek kalmadı C'de bile. Fakat Java tüm bu eski C alışkanlıklarını korudu .
sbi

3
Öyleyse, istisnalar Tek Çıkış'ın bu yorumunu ihlal ediyor mu? (Ya da daha ilkel kuzenleri,? setjmp/longjmp)
Mason Wheeler

2
Operasyon tek geri dönüşün şu anki yorumunu sorsa bile, bu cevap en köklü tarihi olan cevaptır. Dilinizin VB'nin uygunsuzluğuyla eşleşmesini istemediğiniz sürece (.NET değil) kural olarak tek bir dönüş kullanmanın hiçbir anlamı yoktur . Sadece kısa devre dışı bir mantık mantığı kullanmayı da unutmayın.
acelent

912

Bu Tekli Giriş, Tekli Çıkış (SESE) kavramı, C ve montaj gibi açık kaynak yönetimi olan dillerden gelir . C kodunda bunun gibi kodlar sızıntı yapacak:

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

Bu tür dillerde, temel olarak üç seçeneğiniz vardır:

  • Temizleme kodunu çoğaltın.
    Ugh. Artıklık her zaman kötüdür.

  • gotoTemizleme koduna atlamak için a kullanın .
    Bu, temizleme kodunun işlevdeki son şey olmasını gerektirir. (Ve bu yüzden bazıları gotoonun yerini aldığını iddia ediyor . Ve gerçekten de - C.'de)

  • Yerel bir değişken tanıtın ve bunun içinden kontrol akışını değiştirin.
    Dezavantajı (düşünmek sözdizimi aracılığıyla manipüle o kontrol akışı break, return, if, whiledeğişkenlerin devlet aracılığıyla manipüle kontrol akışı (eğer algoritma baktığınızda bu değişkenlerin hiçbir devlet var çünkü) daha takip etmek çok daha kolaydır).

Montajda daha da tuhaf, çünkü o fonksiyonu çağırdığınızda bir fonksiyondaki herhangi bir adrese atlayabilirsiniz, bu da herhangi bir fonksiyon için neredeyse sınırsız sayıda giriş noktasına sahip olduğunuz anlamına gelir. (Bazen bu yararlıdır. Bu tür parçalar , C ++ 'da çoklu kalıtım senaryolarında işlevleri thisçağırmak için gerekli olan işaretçi ayarını uygulamak için derleyiciler için yaygın bir tekniktir virtual.)

Kaynakları manuel olarak yönetmeniz gerektiğinde, herhangi bir yere bir işleve girme veya çıkma seçeneklerinden yararlanmak, daha karmaşık kodlara ve dolayısıyla hatalara yol açar. Bu nedenle, daha temiz kodlar ve daha az hata elde etmek için SESE'yi geliştiren bir düşünce okulu ortaya çıktı.


Bununla birlikte, bir dil istisnalar içerdiğinde, (neredeyse) herhangi bir işlev herhangi bir noktada (neredeyse) herhangi bir noktada erken çıkar, bu nedenle erken dönüş için gerekli hükümleri yapmanız gerekir. (Sanırım finallybaşta Java bunun için kullanılır ve usinguygularken ( IDisposable, finallyaksi) C # C ++ yerine istihdam RAII .) Bunu yaptıktan sonra, olamaz nedeniyle erken üzere kendinize temizlemek için başarısız returnaçıklamada, peki muhtemelen SESE lehine en güçlü argüman ortadan kayboldu.

Bu okunabilirliği bırakır. Tabii ki, returnüzerine rastgele serpilen yarım düzine ifadeli 200 LoC işlevi iyi bir programlama stili değildir ve okunabilir kodlar yapmaz. Ancak böyle bir işlev, bu erken dönüşler olmadan da anlaşılması kolay olmaz.

Kaynakların manuel olarak yönetilemediği veya yönetilmemesi gereken dillerde, eski SESE sözleşmesine bağlı kalmanın değeri yoktur veya çok azdır. OTOH, yukarıda belirttiğim gibi, SESE sıklıkla kodu daha karmaşık hale getirir . (C hariç) günümüz dillerinin çoğuna tam olarak uymayan bir dinozor. Kodun anlaşılabilirliğine yardım etmek yerine, onu engeller.


Java programcıları neden buna bağlı? Bilmiyorum ama (dışardan) POV’mdan Java, C’den (mantıklı oldukları yer) birçok konvansiyon aldı ve onları OO dünyasına uyguladı (nerede işe yaramazlar ya da tamamen kötüler) Onları, ne pahasına olursa olsun. (Tüm değişkenlerinizi kapsamın başlangıcında tanımlama sözleşmesi gibi)

Programcılar irrasyonel sebeplerle her türlü garip gösterime sadık kalırlar. (Derinden iç içe geçmiş yapısal ifadeler - "ok uçları" - Pascal gibi dillerde bir zamanlar güzel kodlar olarak görüldü.) Buna mantıklı bir mantıksal akıl yürütmek, çoğunluğunu yerleşik yollarından sapmaya ikna etmekte başarısız görünüyor. Bu tür alışkanlıkları değiştirmenin en iyi yolu muhtemelen geleneksel olanı değil, en iyisini yapmayı öğretmektir. Siz, bir programlama öğretmeni olarak, elinizde tutun.:)


52
Sağ. Java'da, temizleme kodu finally, erken returnveya istisnalar ne olursa olsun, yürütüldüğü cümleciklere aittir .
dan04, kas

15
@ dan04, Java 7'de finallyçoğu zaman ihtiyaç duymaz .
R. Martinho Fernandes

93
@Steven: Tabii ki bunu gösterebilirsin! Aslında, kodu daha basit ve anlaşılması daha kolay hale getirmek için gösterilebilecek herhangi bir özelliğe sahip, karmaşık ve karmaşık kodlar gösterebilirsiniz. Her şey kötüye kullanılabilir. Mesele, kodun anlaşılması daha kolay ve SESE'nin pencereden atılmasını gerektirdiği zaman yazmaktır , öyle olsun ve farklı dillere uygulanan eski alışkanlıkları lanetleyin. Ancak, kodu okumayı kolaylaştıracağını düşünürsem değişkenlerle yürütmeyi kontrol etmekte tereddüt etmem. Sadece böyle bir kodu neredeyse yirmi yılda gördüğümü hatırlamıyorum.
sbi

21
@Karl: Gerçekten de, Java gibi GC dillerinin bir kaynağını temizlemekten kurtardığı, ancak diğerlerinden başarısız olduğu konusunda ciddi bir eksiklik. (C ++ kullanan tüm kaynaklar için bu sorunu çözer de ray .) Ama ben bile sadece bellek konuşmuyordum (sadece koymak malloc()ve free()bir örnek olarak Bir yoruma), ben genel olarak kaynakların bahsediyordu. Ayrıca GC'nin bu sorunları çözeceğini ima etmiyordum. (Kutunun dışında GC olmayan C ++ 'dan bahsettim.) Anladığım kadarıyla Java'da finallybu sorunu çözmek için kullanılır.
sbi

10
@sbi: Bir işlev için (prosedür, yöntem vb.), sayfa uzunluğundan fazla olmamaktan daha önemli, işlevin açık bir şekilde tanımlanmış bir sözleşmesi olması; Eğer net bir şey yapmıyorsa, çünkü keyfi bir sınırlamayı sağlamak için kesilmişse, bu Kötü. Programlama farklı, bazen birbirleriyle çelişen güçlerin birbiriyle oynaması ile ilgilidir.
Donal Fellows

81

Bir yandan, tek dönüş ifadeleri günlüğe kaydetmeyi ve günlüğe kaydetmeye dayanan hata ayıklama biçimlerini kolaylaştırır. Dönüş değerini tek bir noktadan yazdırmak için işlevi tek dönüşe dönüştürmek zorunda kaldığımı hatırlıyorum.

  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;
  }

Öte yandan, function()bu çağrıları yeniden değerlendirebilir _function()ve sonucu kaydedebilirsiniz.


31
Ayrıca, hata ayıklamayı kolaylaştırdığını da eklerdim, çünkü fonksiyondaki tüm çıkışları * yakalamak için yalnızca bir kesme noktası ayarlamanız gerekir. Bazı IDE'lerin, aynı şeyi yapmak için fonksiyonun yakın kümesine bir kırılma noktası koymanıza izin verdiğine inanıyorum. (* çıkış
yapmazsanız

3
Benzer bir nedenden ötürü, yeni işlevlerinizin her dönüşten önce eklenmesi gerekmediğinden, işlevi genişletmeyi (eklemeyi) kolaylaştırır. Örneğin, işlev çağrısı sonucu bir günlüğü güncellemeniz gerektiğini söyleyin.
JeffSahol

63
Dürüst olmak gerekirse, bu kodu saklıyor olsaydım , uygun yerlerde s _function()ile makul bir şekilde tanımlanmış olmayı ve tüm kayıtları tek bir çıkışa sığdırmak için eğrilmiş mantığa sahip bir tekli olandan daha fazla günlüğe kaydetme işleyen bir sarıcıyı tercih ederim. -point sadece bu noktadan önce ek bir ifade ekleyebilirim. returnfunction()function()
ruakh

11
Bazı hata ayıklayıcılarda (MSVS) son kapanış
ayracına

6
baskı! = hata ayıklama. Bu hiç tartışma değil.
Piotr Perak

53

"Tek Giriş, Tek Çıkış" 1970'lerin başlarında Yapılandırılmış Programlama devrimi ile ortaya çıktı ve Edsger W. Dijkstra'nın " GOTO Bildirimi Zarar Gördüğünü Gördü" yazısına yazdı . Yapılandırılmış programlamanın arkasındaki kavramlar, Ole Johan-Dahl, Edsger W. Dijkstra ve Charles Anthony Richard Hoare tarafından yazılmış “Yapısal Programlama” adlı klasik kitapta ayrıntılı olarak ortaya kondu.

"GOTO'nun Zarar Gördüğü Beyanı" bugün bile okunmak zorunda. “Yapısal Programlama” tarihli, ancak yine de çok, çok faydalıdır ve örneğin Steve McConnell'den çok daha fazla olan herhangi bir geliştiricinin “Okunması Gerekenler” listesinin başında yer almalıdır. (Dahl'ın bölümü, C ++ ve tüm nesne yönelimli programlamanın teknik temeli olan Simula 67'deki sınıfların temelini çizer.)


6
GOTO ağır kullanıldığı zaman makale C öncesi günlerde yazılmıştır. Düşman değiller, ama bu cevap kesinlikle doğru. Bir fonksiyonun sonunda olmayan bir return ifadesi etkin bir şekilde başlamıştır.
user606723 9:11

31
Bu makale aynı zamanda , başka bir fonksiyondaki rastgele bir noktaya gelmek, herhangi bir prosedür, fonksiyon, çağrı yığını, vb. Kavramını atlamak gibi gotokelimenin tam anlamıyla herhangi bir yere gidebildiği günlerde de yazılmıştır goto. C 'ler setjmp/ longjmpfarkında olduğum tek istisnai durumdur ve hatta her iki taraftan da işbirliğini gerektirir. (İstisnaların hemen hemen aynı şeyi yaptıklarını düşünürsek, orada "istisnai" kelimesini kullandığım yarı-ironik ...) Temel olarak, makale uzun zamandır ölü olan bir uygulamayı önermiyor.
cHao

5
"Zararlı olarak kabul Goto Bildirimi" son paragrafında From: "[2] Guiseppe Jacopini açıklamaya gitmek (mantıksal), gereksiz kanıtladık görünüyor. Egzersiz keyfi bir akış diyagramı çevirmek için bir jump- içine mekanik olarak az ya da çok Bununla birlikte daha az bir tanesi tavsiye edilmez . O zaman ortaya çıkan akış diyagramının orijinal olandan daha şeffaf olması beklenemez. "
hugomg

10
Bunun soru ile ne alakası var? Evet, Dijkstra'nın çalışması sonunda SESE dillerine neden oldu, peki ne? Babbage'nin işi de öyle. Ve belki de bir fonksiyonda birden fazla çıkış noktasına sahip olmakla ilgili bir şey söylediğini düşünüyorsanız, kağıdı tekrar okumalısınız . Çünkü öyle değil.
jalf

10
@John, soruyu aslında cevaplamadan cevaplamaya çalışıyor görünüyorsun. Güzel bir okuma listesi, ancak bu makalenin ve kitabın, sorucunun endişesi hakkında söyleyecek bir şeyleri olmadığını iddia etmek için hiçbir şeyden alıntı yapmadınız veya hiçbir şey ifade etmediniz. Gerçekten de, yorumların dışında , soru ile ilgili önemli bir şey söylemediniz . Bu cevabı genişletmeyi düşünün.
Shog9

35

Fowler'ı bağlamak her zaman kolaydır.

SESE'ye karşı çıkan ana örneklerden biri, koruyucu maddelerdir:

Yuvalanmış Koşulları Koruma Cümleleriyle Değiştirme

Tüm özel durumlar için Guard Cümleleri kullanın.

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();
};  

Daha fazla bilgi için Yeniden Düzenleme sayfasının 250. sayfasına bakın ...


11
Başka bir kötü örnek: başkasıyla kolayca çözülebilir.
Jack,

1
Örneğiniz adil değil, peki ya bu: double getPayAmount () {double ret = normalPayAmount (); if (_isDead) ret = deadAmount (); if (_isSeparated) ret = separaAmount (); if (_isRetired) ret = retiredAmount (); geri dönüş ret; };
Charbel

6
@ Charbel Bu aynı şey değil. Eğer _isSeparatedve _isRetiredikisi de doğru olabilir (ve bu yüzden mümkün olmazdı?) Yanlış miktarda döner.
HVD

2
@Konchog " iç içe koşullandırmalar, güvenlik şartlarından daha iyi bir uygulama süresi sağlayacaktır " Bu, büyük ölçüde bir alıntıya ihtiyaç duyar. Güvenilir bir şekilde doğru olduğuna dair şüphelerim var. Bu durumda, örneğin, erken dönüşler, üretilen kod açısından mantıksal kısa devreden ne kadar farklıdır? Önemli olsa bile, farkın sonsuz küçük bir şeritten daha fazla olacağı bir durum hayal edemiyorum. Yani kodu daha az okunabilir hale getirerek erken optimizasyon uyguluyorsunuz, sadece biraz daha hızlı koda neyin yol açtığını düşündüğünüz bazı kanıtlanmamış teorik noktaları yerine getirmek için. Bunu burada yapmıyoruz
underscore_d

1
@underscore_d, haklısın. derleyiciye çok bağlıdır, ancak daha fazla yer kaplar. İki sözde düzeneğe bakın ve güvenlik maddelerinin neden üst düzey dillerden geldiğini görmek kolaydır. "A" testi (1); branch_fail sonu; Test (2); branch_fail sonu; testi (3); branch_fail sonu; {KOD} son: dönüş; "B" testi (1); branch_good next1; dönüş; sonraki1: test (2); branch_good next2; dönüş; sonraki2: test (3); branch_good next3; dönüş; next3: {KOD} dönüş;
Konchog

11

Bir süre önce bu konuyla ilgili bir blog yazısı yazdım.

Sonuç olarak, bu kuralın çöp toplama veya istisna işlemesi olmayan dillerin yaşından gelmesidir. Bu kuralın modern dillerde daha iyi kodlara yol açtığını gösteren resmi bir çalışma yoktur. Bu, daha kısa veya daha okunaklı kodlara yol açacağı zaman bunu görmezden gelmekten çekinmeyin. Bu konuda ısrar eden Java adamları modası geçmiş, anlamsız bir kuralı izleyerek kör ve tartışmasız davranıyorlar.

Bu soru Stackoverflow'ta da sorulmuştur.


Hey, artık bu bağlantıya ulaşamıyorum. Hala erişilebilen bir yerde barındırılan bir sürümünüz var mı?
Nic Hartley,

Merhaba, QPT, iyi yer. Blog gönderisini geri getirdim ve yukarıdaki URL’yi güncelledim. Şimdi bağlantı kurmalı!
Anthony

Bundan daha fazlası var. SESE kullanarak hassas yürütme zamanlamasını yönetmek çok daha kolaydır. İçiçe kondisyonlamalar çoğu zaman yine de bir anahtarla yeniden uyarlanabilir. Bu sadece bir geri dönüş değeri olup olmadığına bağlı değil.
Konchog

Bunu destekleyecek resmi bir çalışma olmadığını iddia edecekseniz, buna karşı çıkan birine bağlantı vermeniz yerinde olacaktır.
Mehrdad

Mehrdad, Eğer onu destekleyen resmi bir çalışma varsa, göster. Bu kadar. Aleyhte deliller konusunda ısrar etmek ispat yükünü değiştiriyor.
Anthony,

7

Bir dönüş, yeniden düzenlemeyi kolaylaştırır. "Döndürme yöntemi", bir dönüş, ara veya devam içeren bir for döngüsünün iç gövdesine gerçekleştirmeyi deneyin. Kontrol akışınızı kırdığınız için bu başarısız olacaktır.

Mesele şu ki: Kimse mükemmel kod yazıyormuş gibi davranmıyor. Bu yüzden kod düzenli olarak "iyileştirilmesi" ve genişletilmesi için yeniden düzenleme altındadır. Bu yüzden hedefim, kodumu mümkün olduğunca dostça yeniden düzenlemeye devam etmektir.

Sık sık, kontrol akışı kesicileri içeriyorsa ve yalnızca çok az işlevsellik eklemek istersem, işlevleri tamamen yeniden biçimlendirmek zorunda olduğum sorunuyla karşılaşıyorum. İzole yuvalara yeni yollar eklemek yerine tüm kontrol akışını değiştirdiğiniz için bu çok yanlıştır. Sonunda yalnızca bir dönüşünüz varsa veya bir döngüden çıkmak için koruma kullanıyorsanız, elbette daha fazla yuvalama ve daha fazla kodunuz olur. Ancak derleyici ve IDE destekli yeniden düzenleme yetenekleri kazanıyorsunuz.


Aynısı değişkenler için de geçerlidir. Erken dönüş gibi kontrol-akış yapıları kullanmanın alternatifidir.
Deduplicator

Değişkenler çoğunlukla, mevcut kontrol akışını koruyacak şekilde kodunuzu parçalara ayırmanızı engellemez. "Özü yöntemi" deneyin. IDE'ler, yalnızca kontrol akışını önceden ayarlayan yeniden yapılandırmaları gerçekleştirebilir, çünkü yazdıklarından anlambilim elde edemezler.
oopexpert

5

Birden fazla return ifadesinin GOTO'ların tek bir return ifadesine sahip olmasına eşdeğer olduğunu düşünün. Bu, break ifadeleriyle aynı durumdur. Bu nedenle, bazıları benim gibi, GOTO’yu tüm amaç ve amaçlara yönelik olarak kabul eder.

Ancak, bu GOTO türlerinin zararlı olduğunu düşünmüyorum ve bunun için iyi bir neden bulursam kodumda gerçek bir GOTO kullanmaktan çekinmeyeceğim.

Genel kuralım, GOTO'nun yalnızca akış kontrolü için olmasıdır. Asla herhangi bir döngü için kullanılmamalıdırlar ve asla 'yukarı' ya da 'geriye' GOTO olmamalıdır. (bu, molaların / iadelerin nasıl çalıştığıdır)

Diğerlerinin de belirttiği gibi, aşağıdakiler bir zorunluluktur: GOTO'nun Zarar Gördüğü GOTO Bildirimi'ni okumak zorunludur.
Ancak, bunun 1970’lerde GOTO’nun fazla kullanıldığı durumlarda yazıldığını unutmayın. Her GOTO zararlı değildir ve normal yapılar yerine kullanmadığınız sürece kullanımlarını caydırmayacağım, fakat normal durumlarda normal yapılar kullanmanın son derece elverişli olamayacağı bir durumda.

Onları, normal durumlarda asla kullanılmaması gereken bir arıza nedeniyle bir alandan kaçmanız gereken hata durumlarında kullanmanın yeterli olduğunu düşünüyorum. Ancak bu kodu ayrı bir işleve koymayı da düşünmelisiniz, böylece bir GOTO kullanmak yerine sadece erken dönebilirsiniz ... ama bu bazen de sakıncalıdır.


6
Gotoların yerini alan tüm yapılandırılmış yapılar, goto açısından uygulanır. Örneğin "if" ve "case" gibi döngüler. Bu onları kötü yapmaz - aslında tam tersi. Ayrıca, "niyet ve amaçlar" dır.
Anthony,

Touche, ama bu benim açımdan farklı değil ... Bu sadece açıklamamı biraz yanlış yapıyor. Oh iyi.
user606723 10:11

GOTO, (1) hedef aynı yöntem veya işlev içinde olduğu ve (2) yön kodda ileri (sürece bazı kodu atla) ve (3) hedef başka bir yuvalanmış yapı içinde olmadığı sürece (örn.) İfanın ortasından diğerinin ortasına GOTO). Bu kuralları izlerseniz, GOTO'nun tüm suiistimalleri hem görsel hem de mantıksal olarak çok güçlü bir kod kokusuna sahiptir.
Mikko Rantalainen

3

Cyclomatic karmaşıklık

SonarCube'ün döngüsel karmaşıklığı belirlemek için çoklu geri dönüş ifadesi kullandığını gördüm. Böylece geri dönüş ifadeleri daha fazla, döngüsel karmaşıklık artar.

İade Tipi Değişimi

Çoklu iadeler, geri dönüş tipimizi değiştirmeye karar verdiğimizde, fonksiyonun çoklu yerlerinde değişmemiz gerektiği anlamına gelir.

Birden Çok Çıkış

Mantığın döndürülen değere neyin neden olduğunu anlamak için koşullu ifadelerle birlikte dikkatlice çalışılması gerektiğinden, hata ayıklamak daha zordur.

Refaktörlü Çözüm

Birden fazla geri dönüş ifadesine çözüm, gerekli uygulama nesnesini çözdükten sonra bunları tek biçimli bir polimorfizmle değiştirmektir.


3
Birden fazla iadeden dönüş değerini birden fazla yerde ayarlamaya geçmek, döngüsel karmaşıklığı ortadan kaldırmaz, yalnızca çıkış konumunu birleştirir. Döngüsel karmaşıklığın belirtilen bağlamda gösterebileceği tüm sorunlar devam etmektedir. “Mantığın, döndürülen değere neyin neden olduğunu anlamak için koşullu ifadelerle birlikte dikkatlice incelenmesi gerektiğinden, hata ayıklamak daha zordur” Yine, mantık, dönüşü birleştirerek değişmez. Nasıl çalıştığını anlamak için kodu dikkatlice incelemeniz gerekiyorsa, yeniden yapılandırılması ve tam durması gerekir.
Will
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.