İç yapıları varsa, uzun işlevler kabul edilebilir mi?


23

İç içe geçmiş işlevler (Python ve D gibi) destekli dillerdeki karmaşık algoritmalarla uğraşırken, genellikle büyük işlevler yazarım (algoritma karmaşık olduğu için) ancak karmaşık kodu yapılandırmak için iç içe geçmiş işlevler kullanarak hafifletirim. İç içe geçmiş işlevler kullanılarak dahili olarak iyi yapılandırılmış olsalar bile, dev (100+ satır) işlevler hala kötü olarak mı değerlendiriliyor?

Düzenleme: Python veya D ile aşina olmayanlar için, bu dillerde iç içe geçmiş işlevler de dış işlev kapsamına erişime izin verir. D'de bu erişim dış kapsamdaki değişkenlerin mutasyonuna izin verir. Python'da sadece okumayı sağlar. D'de, iç içe geçmiş bir işlevdeki dış etki alanına erişimi açıkça bildirerek devre dışı bırakabilirsiniz static.


5
Düzenli olarak 400'den fazla çizgi fonksiyonu / yöntemi yazıyorum. Bu 500 dava anahtarı bildirimini bir yere koymalıyım :)
Callum Rogers

7
@Callum Rogers: Python'da, 500 case switch ifadesi kullanmak yerine, bir arama sözlüğü / haritalaması kullanırsınız. Bence 500 ya da öylesine değiş tokuş-davası ifadesinden daha üstün olduklarına inanıyorum. 500 satırlık sözlük tanımlarına hala ihtiyacınız var (alternatif olarak, Python'da bunları dinamik olarak sıralamak için yansıma kullanabilirsiniz), ancak sözlük tanımı kod değil, veridir; ve büyük veri tanımına sahip olmakla ilgili olarak büyük işlev tanımından çok daha az nitelik vardır. Ek olarak, veriler koddan daha sağlamdır.
Yalan Ryan

Çok fazla LOC işlevine sahip olduğumu gördüğüm her seferinde cringe yapıyorum, modülerliğin amacının ne olduğunu merak etmemi sağlıyor.
Aditya P,

Eski ve tanıdık Pascal dili, aynı zamanda dış kapsamına erişime izin verir ve GNU C'deki iç içe geçmiş fonksiyon uzantısı da artar (Bu diller yalnızca aşağıya doğru açılan alanlara izin verir, ancak: kapsamı taşıyan işlevler yalnızca olabilir; tamamen sözcüksel kapanma desteğine ihtiyaç duyan ve geri gönderilen ve geri gönderilmeyen).
Kaz

Uzun olma eğiliminde olan fonksiyonlara güzel bir örnek, reaktif programlama yaparken yazdığınız birleştiricilerdir. Kolayca otuz çizgiye vurabiliyorlardı, ancak kapamaları kaybettiğiniz için ikiye ayrılırsanız boyutu iki katına çıkardı.
Craig Gidney

Yanıtlar:


21

Her zaman kuralı unutmayın, bir işlev bir şeyi yapar ve iyi yapar! Bunu yapabilirseniz, iç içe geçmiş işlevlerden kaçının.

Okunabilirliği ve testi engeller.


9
Bu kuraldan her zaman nefret ettim, çünkü yüksek bir soyutlama seviyesinde "bir şeyi" yapan bir fonksiyon, daha düşük bir seviyede görüntülendiğinde "birçok şeyi" yapabilir. Yanlış uygulanırsa, "bir şey" kuralı aşırı derecede hassas API'lere neden olabilir.
dsimcha

1
@ dsimcha: Hangi kural yanlış uygulanamaz ve sorunlara yol açamaz? Birisi "kurallar" / platformlar uygularken, yararlı oldukları noktayı geçtikten sonra, bir başkasını ortaya çıkarın: tüm genellemeler yanlıştır.

2
@Roger: Meşru bir şikayet, IMHO dışında kural, aşırı derecede ince taneli, incelenmiş API'leri haklı çıkarmak için pratikte çoğu zaman yanlış uygulanır. Bunun, basit, yaygın kullanım durumlarında kullanmak için API'nin zorlaşması ve daha ayrıntılı hale gelmesi nedeniyle somut maliyetleri vardır.
dsimcha

2
“Evet, kural yanlış uygulandı, ancak kural çok fazla yanlış uygulandı!”? : P

1
Benim işlevim bir şeyi yapar ve o şeyi çok iyi yapar: yani, 27 perdeyi işgal eder. :)
Kaz

12

Bazıları kısa işlevlerin uzun işlevlerden daha hataya eğilimli olabileceğini savunuyorlar .

Kart ve Cam (1990), tasarım karmaşıklığının gerçekten iki yönü içerdiğini belirtmektedir: her bir bileşen içindeki karmaşıklık ve bileşenler arasındaki ilişkilerin karmaşıklığı.

Şahsen, iyi yorumlanmış düz çizgi kodun takip edilmesinin daha kolay olduğunu (özellikle ilk yazan kişi sen değilken) başka yerde hiç kullanılmamış birden fazla fonksiyona bölündüğünden daha kolay olduğunu buldum. Ama bu gerçekten duruma bağlı.

Bence ana paket, bir kod bloğunu böldüğünüzde, bir başkası için bir çeşit karmaşıklık ticareti yaptığınızdır. Muhtemelen ortada bir yerlerde tatlı bir yer vardır.


"Temiz Kod" u okudun mu? Bunu uzunca tartışıyor. amazon.com/Clean-Code-Handbook-Yazılım-Craftsmanship/dp/…
Martin Wickman 26:10

9

İdeal olarak, tüm işlev kaydırma yapmak zorunda kalmadan görüntülenebilir olmalıdır. Bazen bu mümkün değildir. Ama parçalara ayırabilirseniz, kod okumayı çok daha kolay hale getirecektir.

Sayfa Yukarı / Aşağıya bastığımda veya kodun farklı bir bölümüne geçtiğimde önceki sayfadan yalnızca 7 +/- 2 şeyi hatırlayabildiğimi biliyorum. Ve ne yazık ki, bu kodlardan bazıları yeni kodu okurken kullanılacak.

Her zaman kısa süreli belleğimi bir bilgisayarın kayıtları gibi düşünmeyi severim (CISC, RISC değil). Tüm fonksiyonun aynı sayfada olması durumunda, programın başka bir bölümünden gerekli bilgileri almak için önbelleğe gidebilirsiniz. Tüm işlev bir sayfaya sığmazsa, her işlemden sonra her zaman herhangi bir belleği diske basmaya eşdeğer olur.


3
Sadece bir matris yazıcıda yazdırmanız gerekir - bkz. Aynı anda 10 metre kod :)

Veya daha yüksek çözünürlüklü bir monitör almak
frogstarr78

Ve dikey yönde kullanın.
Calmarius

4

Neden normal harici işlevler yerine iç içe geçmiş işlevler kullanıyorsunuz?

Dıştaki işlevler yalnızca eski, büyük işlevinizde yalnızca bir kez kullanılmış olsa bile, tüm karışıklığı okumayı kolaylaştırır:

DoThing(int x){
    x += ;1
    int y = FirstThing(x);
    x = SecondThing(x, y);
    while(spoo > fleem){
        x = ThirdThing(x+y);
    }
}

FirstThing(int x){...}
SecondThing(int x, int y){...}
ThirdThing(int z){...}

2
İki olası neden: isim alanını karıştırmak ve benim "sözcüksel yakınlık" dediğim şey. Bunlar dilinize bağlı olarak iyi veya kötü sebepler olabilir.
Frank Shearar 28:10

Ancak iç içe geçmiş işlevler daha az verimli olabilir çünkü aşağı doğru hunileri ve muhtemelen tamamen şişirilmiş sözcük kapanışlarını desteklemeleri gerekir. Dil zayıf bir şekilde optimize edilmişse, alt işlevlere geçmek üzere dinamik bir ortam oluşturmak için fazladan iş yapabilir.
Kaz

3

Kitabın önümde şu anda önümde yok (alıntı yapmak), ama Kurallara göre, araştırmaya göre, fonksiyon uzunluğu için "sweetspot" 25-50 satır civarındaydı.

Uzun fonksiyonlara sahip olmanın tamam olduğu zamanlar vardır:

  • Fonksiyonun siklomatik karmaşıklığı düşük olduğunda. Geliştiricileriniz, eğer aynı anda ekranda değilse, eğer bir if ifadesi ve bunun için başka bir ifadeyi içeren bir fonksiyona bakmak zorunda kaldıklarında biraz sinirlenebilirler.

Uzun işlevlere sahip olmanın uygun olmadığı zamanlar:

  • Çok iç içe koşullu bir işleve sahipsin. Kod okuyucularına bir iyilik yapın, işlevi parçalayarak okunabilirliği artırın. Bir işlev, okuyucuya "Bu bir şeyi yapan bir kod bloğu" olduğunu gösterir. Ayrıca, fonksiyonun uzunluğunun çok fazla bir şey yaptığını gösterip göstermediğini ve başka bir sınıfa yönlendirilmesi gerektiğini de kendinize sorun.

Sonuç olarak, bakım yapılabilirlik listenizdeki en yüksek önceliklerden biri olmalıdır. Başka bir dev kodunuza bakamıyorsa ve kodun 5 saniyeden kısa sürede ne yaptığını "anlayamazsa", ur kodu ne yaptığını söylemeye yetecek kadar "meta veri" sağlamıyor demektir. Diğer geliştiriciler, 100'den fazla kod satırı okumak yerine, seçtiğiniz IDE'deki nesne tarayıcısına bakarak sınıfınızın ne yaptığını söyleyebilmelidir.

Küçük fonksiyonlar aşağıdaki avantajlara sahiptir:

  • Taşınabilirlik: İşlevselliği hareket ettirmek çok daha kolaydır (ya sınıf içinde farklı bir sisteme yeniden düzenleme yaparken)
  • Hata ayıklama: Yığınlamaya baktığınızda, 100'den ziyade 25 kod satırlı bir işleve bakıyorsanız bir hatayı tam olarak belirlemek çok daha hızlıdır.
  • Okunabilirlik - İşlevin adı tüm kod bloğunun ne yaptığını gösterir. Takımınızdaki bir ekip, eğer çalışmazsa o bloğu okumak istemeyebilir. Ayrıca, çoğu modern IDE'de başka bir aygıt, işlev adlarını bir nesne tarayıcısında okuyarak sınıfınızın ne yaptığını daha iyi anlayabilir.
  • Gezinme - Çoğu IDE, işlevlerin adını aramanıza izin verir. Ayrıca, çoğu modern IDE bir işlevin kaynağını başka bir pencerede görüntüleme kabiliyetine sahiptir, bu da diğer aygıtlara uzun işleve bakmalarını sağlamak yerine 2 ekranda (çoklu monitörlerde) bakma şansı verir.

Liste devam ediyor.....


2

Cevap buna bağlı, ancak bunu muhtemelen bir sınıfa dönüştürmelisiniz.


Bir OOPL'da yazmıyorsanız, bu durumda belki de değil :)
Frank Shearar 28:10

dsimcha, D ve Python kullandıklarından bahsetti.
frogstarr78

Sınıflar, sözcük kapanması gibi şeyleri hiç duymamış insanlar için çok zorlayıcıdır.
Kaz

2

Çoğu iç içe işlevden hoşlanmıyorum. Lambdalar bu kategoriye girer ancak 30-40 karakterden fazla olmadıkça genellikle beni işaretlemez.

Temel sebep, içsel anlamsal özyinelemeyle yerel olarak yoğun bir işleve dönüşmesi, beynimi etrafa sarmamın zor olduğu anlamına geliyor ve bazı şeyleri kod alanını karıştırmayan bir yardımcı fonksiyona itmek daha kolay .

Bir işlevin işini yapması gerektiğini düşünüyorum. Başka Şeyler Yapmak diğer fonksiyonların yaptığı şeydir. Yani, 200 çizgi bir işleve sahipseniz, Şeyini Yapıyorsanız ve hepsi akıyorsa, bu A-OK.


Bazen, harici bir işleve (yerel işlev olarak uygun erişime sahip olabilir) çok fazla bağlam geçirmeniz gerekir, işlevin nasıl çalıştığından daha fazla karmaşıklığı vardır.
Kaz

1

Kabul edilebilir mi? Bu gerçekten sadece senin cevaplayabileceğin bir soru. İşlev ihtiyaç duyduğu şeyi yapar mı? Bakım yapılabilir mi? Takımınızın diğer üyeleri için 'kabul edilebilir' mi? Eğer öyleyse, o zaman gerçekten önemli olan budur.

Düzenleme: İç içe geçmiş işlevler hakkında bir şey görmedim. Şahsen ben onları kullanmazdım. Bunun yerine düzenli fonksiyonlar kullanırdım.


1

Uzun bir "düz çizgi" işlevi, her zaman belirli bir sırayla gerçekleşen uzun adım dizisini belirtmenin açık bir yolu olabilir.

Bununla birlikte, diğerlerinin de belirttiği gibi, bu form, genel akışın daha az belirgin olduğu yerel karmaşıklık uzantılarına sahip olma eğilimindedir. Bu yerel karmaşıklığı iç içe geçmiş bir işleve yerleştirmek (yani: uzun işlev içinde başka bir yerde, belki üstte ya da altta tanımlanmış) yerleştirmek ana hat akışına açıklığı geri getirebilir.

İkinci önemli husus, yalnızca uzun bir işlevin yerel bir uzantısında kullanılması amaçlanan değişkenlerin kapsamını kontrol etmektir. Bu tür bir hata derleme ya da çalışma zamanı hatası olarak görünmeyeceğinden, istemeden başka bir yere (örneğin, düzenleme döngülerinden sonra) referans verilen kodun bir bölümünde tanıtılan bir değişkenin bulunmaması için dikkatli olmak gerekir.

Bazı dillerde, bu sorun kolayca önlenir: Yerel bir kod dizisi kendi bloğuna, örneğin yeni girilen değişkenlerin yalnızca bu bloğa göründüğü "{...}" gibi sarılabilir. Python gibi bazı diller bu özellikten yoksundur; bu durumda yerel işlevler daha küçük kapsam bölgelerini zorlamak için yararlı olabilir.


1

Hayır, çok sayfalı işlevler istenmez ve kod incelemesini geçmemelidir. Ben de uzun fonksiyonlar yazıyordum, ancak Martin Fowler's Refactoring'i okuduktan sonra durdum. Uzun fonksiyonların doğru yazılması, anlaşılması zor ve test edilmesi güçtür. 50 satırdan bile daha kolay anlaşılamayacak ve daha küçük fonksiyonlara ayrılacaksa test edilemeyecek bir fonksiyon görmemiştim. Çok sayfalı bir fonksiyonda, kesinlikle kesinlikle dışlanması gereken tüm sınıflar vardır. Daha spesifik olmak zor. Belki de uzun fonksiyonlarınızdan birini Code Review'a göndermelisiniz ve birisi (belki ben) size nasıl geliştireceğinizi gösterebilir.


0

Python'da programlama yaparken, bir işlev yazdıktan ve kendime "Zen Python" a uyup uymadığını sorduktan sonra geri adım atmayı seviyorum (python tercümanınıza 'içe aktarın' yazın):

Güzel, çirkin olmaktan iyidir.
Açık, örtük olmaktan iyidir.
Basit, karmaşıktan daha iyidir.
Karmaşık karmaşıktan daha iyidir.
Düz iç içe daha iyidir.
Seyreklik yoğun olmaktan iyidir.
Okunabilirlik sayar.
Özel durumlar kuralları ihlal edecek kadar özel değil.
Her ne kadar pratiklik saflığı yenerse de.
Hatalar asla sessizce geçmemelidir.
Açıkça susturmadıkça.
Belirsizlik karşısında, tahmin etme isteğini reddetmek.
Bunu yapmanın bir yolu ve tercihen sadece bir tane olmalı.
Hollandalı olmadıkça bu yol ilk başta belli olmayabilir.
Şimdi hiç olmadığı kadar iyidir.
Her zaman asla doğrudan daha iyi olmamasına rağmenşimdi.
Uygulamanın açıklanması zorsa, bu kötü bir fikirdir.
Uygulamanın açıklanması kolay ise, iyi bir fikir olabilir.
İsim boşlukları harika bir fikirdir - hadi daha fazlasını yapalım!


1
Açıkça örtük olmaktan daha iyiyse, makine dilinde kodlamalıyız. Montajcı bile değil; Şube geciktirme yuvalarını talimatlarla otomatik olarak doldurma, göreli dallanma ofsetlerini hesaplama ve küreler arasındaki sembolik referansları çözme gibi kötü şeyler yapar. Dostum, bu aptal Python kullanıcıları ve küçük dinleri ...
Kaz

0

Onları ayrı bir modüle koyun.

Çözümünüzün çok fazla seçeneğiniz olmadığından, ihtiyaç duyduğunuzdan daha fazla şişirilmediğini farz edersiniz. İşlevleri zaten farklı alt işlevlerde böldünüz, bu nedenle soru nereye koymanız gerektiğidir:

  1. Bunları sadece bir "public" işlevine sahip bir modüle yerleştirin.
  2. Bunları yalnızca bir "public" (statik) işlevi olan bir sınıfa yerleştirin.
  3. Onları bir işleve yerleştirin (açıkladığınız gibi).

Şimdi hem ikinci hem de üçüncü alternatifler bir anlamda iç içe geçiyor, ancak ikinci alternatif bazı programcılar tarafından kötü görünmüyor. İkinci alternatifi ekarte etmezseniz, üçüncü ekarte etmek için çok fazla sebep göremiyorum.

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.