Yalnızca diğer işlevleri çağıran işlevler. Bu iyi bir uygulama mı?


13

Şu anda (hepsi farklı biçimlendirme gerektiren) birçok farklı bölümlere sahip raporlar bir dizi üzerinde çalışıyorum ve kodumu yapılandırmak için en iyi yolu bulmaya çalışıyorum. Geçmişte yaptığımız benzer raporlar, rapor için tüm veri manipülasyonunu ve biçimlendirmesini yapan çok büyük (200+ satır) işlevlere sahip olur, böylece iş akışı şöyle görünür:

DataTable reportTable = new DataTable();
void RunReport()
{
    reportTable = DataClass.getReportData();
    largeReportProcessingFunction();
    outputReportToUser();
}

Bu büyük işlevleri daha küçük parçalara ayırmak istiyorum, ancak korkarım ki onlarca tekrar kullanılamayan fonksiyona ve benzer bir "burada her şeyi yap" fonksiyonuna sahip olacağım. tüm bu daha küçük işlevleri çağırın, şöyle:

void largeReportProcessingFunction()
{
    processSection1HeaderData();
    calculateSection1HeaderAverages();
    formatSection1HeaderDisplay();
    processSection1SummaryTableData();
    calculateSection1SummaryTableTotalRow();
    formatSection1SummaryTableDisplay();
    processSection1FooterData();
    getSection1FooterSummaryTotals();
    formatSection1FooterDisplay();

    processSection2HeaderData();
    calculateSection1HeaderAverages();
    formatSection1HeaderDisplay();
    calculateSection1HeaderAverages();
    ...
}

Veya bir adım daha ileri gidersek:

void largeReportProcessingFunction()
{
    callAllSection1Functions();

    callAllSection2Functions();

    callAllSection3Functions();
    ...        
}

Bu gerçekten daha iyi bir çözüm mü? Örgütsel bir bakış açısından, sanırım öyle (yani her şey başka türlü olabileceğinden çok daha organize), ancak kod okunabilirliğine kadar emin değilim (sadece diğer işlevleri çağıran fonksiyonların potansiyel olarak büyük zincirleri).

Düşünceler?


Günün sonunda ... daha okunabilir mi? Evet, bu yüzden daha iyi bir çözüm.
sylvanaar

Yanıtlar:


18

Bu genellikle işlevsel ayrışma olarak adlandırılır ve doğru şekilde yapılırsa genellikle yapılması iyi bir şeydir.

Ayrıca, bir işlevin uygulanması tek bir soyutlama düzeyinde olmalıdır. Eğer alırsanız largeReportProcessingFunction, rolü hangi sırayla hangi farklı işleme adımlarının atılacağını tanımlamaktır. Bu adımların her birinin uygulanması aşağıdaki soyutlama katmanındadır ve largeReportProcessingFunctiondoğrudan buna bağlı olmamalıdır.

Ancak bunun kötü bir adlandırma seçeneği olduğunu lütfen unutmayın:

void largeReportProcessingFunction() {
    callAllSection1Functions();
    callAllSection2Functions();
    callAllSection3Functions();
    ...        
}

Gördüğünüz gibi callAllSection1Functions, aslında bir soyutlama sağlamayan bir isim, çünkü aslında ne yaptığını değil, nasıl yaptığını söylemiyor . Bunun processSection1yerine veya bağlamda gerçekten anlamlı olan her şey çağrılmalıdır .


6
Bu süreçte prensipte hemfikirim, ancak "processSection1" in anlamlı bir isim olduğunu göremiyorum. Pratik olarak "callAllSection1Functions" ile eşdeğerdir.
Eric King

2
@EricKing: callAllSection1Functionsuygulama ile ilgili ayrıntıları sızdırıyor. Dış perspektiften bakıldığında, processSection1çalışmasını "tüm bölüm1 işlevlerine" karşı koyar mı, yoksa doğrudan mı yaparsa veya gerçek işi yapacak bir tek boynuzlu at çağırmak için pixie-toz kullanıp kullanmadığı önemli değildir. Yapılan bir çağrı sonrasında gerçekten önemli olduğunu Tümü, o processSection1, section1 düşünülebilir işlenmiş . Ben katılıyorum işleme genel adıdır, ancak (her zaman için çaba olmalı) yüksek uyum varsa ve daha sonra bağlam genellikle bunu belirsizliği giderecek yeterli dar olmasına.
back2dos

2
Demek istediğim, "processXXX" olarak adlandırılan yöntemler neredeyse her zaman korkunç, işe yaramaz isimler. Tüm yöntemler şeyleri 'işler', yani 'processXXX' isimlendirmek 'doSomethingWithXXX' veya 'manipulateXXX' ve benzerlerini isimlendirmek kadar yararlıdır. 'ProcessXXX' adlı yöntemler için neredeyse her zaman daha iyi bir ad vardır. Pratik bir bakış açısından, 'callAllSection1Functions' kadar faydalıdır (yararsızdır).
Eric King

4

C # kullandığınızı söylediniz, bu yüzden nesne odaklı bir dil kullandığınızdan yararlanarak yapılandırılmış bir sınıf hiyerarşisi oluşturabileceğinizi merak ediyorum. Örneğin, raporu bölümlere ayırmak mantıklıysa, bir Sectionsınıfa sahip olun ve müşteriye özel gereksinimler için genişletin.

Etkileşimli olmayan bir GUI oluşturuyormuş gibi düşünmek mantıklı olabilir. Yaratabileceğiniz nesneleri farklı GUI bileşenleri gibi düşünün. Kendinize temel bir raporun nasıl görüneceğini sorun. Karmaşık olana ne dersiniz? Uzatma noktaları nerede olmalı?


3

Değişir. Oluşturduğunuz tüm işlevler gerçekten tek bir iş birimiyse, o zaman sorun değil, eğer arama işlevinin sadece 1-15, 16-30, 31-45 hatları ise kötüdür. Uzun işlevler, bir şey yaparlarsa kötü değildir, raporunuz için yirminin tümünü kontrol eden bir doğrulama işlevine sahip 20 filtreniz varsa uzun olacaktır. Bu sadece iyi, filtre veya filtre gruplarıyla parçalamak, gerçek bir fayda için ekstra karmaşıklık katıyor.

Çok sayıda özel işleve sahip olmak, otomatik birim testini daha da zorlaştırır, bu nedenle bazıları bu nedenle bundan kaçınmaya çalışacaktır, ancak bu, teste uyum sağlamak için daha büyük ve daha karmaşık bir tasarım oluşturma eğiliminde olduğu için bence oldukça zayıf bir akıl yürütmedir.


3
Tecrübelerime göre, uzun fonksiyonları vardır kötülük.
Bernard

Örneğin OO dilinde olsaydı, bir temel filtre sınıfı yaratırdım ve 20 filtrenin her biri farklı türetme sınıflarında olurdu. Switch deyimi, doğru filtreyi almak için bir oluşturma çağrısı ile değiştirilir. Her fonksiyon bir şey yapar ve hepsi küçüktür.
DXM

3

C # kullandığınızdan, nesnelerde daha fazla düşünmeye çalışın. Görünüşe göre, sonraki işlevlerin bağlı olduğu "global" sınıf değişkenlerine sahip olarak prosedürel olarak kodlama yapıyorsunuz.

Ayrı fonksiyonlarda mantığınızı kırmak, kesinlikle tüm mantığın tek bir fonksiyonda bulunmasından daha iyidir. Bahse girerim ayrıca fonksiyonların üzerinde çalıştığı verileri ayırarak bunu birkaç nesneye ayırarak daha ileri gidebilirsiniz.

Rapor verilerini aktarabileceğiniz bir ReportBuilder sınıfına sahip olun. ReportBuilder'ı ayrı sınıflara ayırabiliyorsanız bunu da yapın.


2
Prosedürler mükemmel derecede iyi bir işlev görür.
DeadMG

1

Evet.

Birden fazla yerde kullanılması gereken bir kod segmentiniz varsa, kendi işlevi. Aksi takdirde, işlevsellikte bir değişiklik yapmanız gerekiyorsa, değişikliği birden çok yerde yapmanız gerekir. Bu sadece işi arttırmakla kalmaz, aynı zamanda kod bir yerde değiştiğinde ortaya çıkan hata riskini arttırır, başka bir yerde değil veya bir yerde değil, bir yerde tanıtılırken diğerinde değil.

Ayrıca, açıklayıcı yöntem adları kullanılırsa yöntemin daha okunabilir olmasına yardımcı olur.


1

İşlevleri ayırt etmek için numaralandırmayı kullanmanız, çoğaltma veya çok fazla şey yapan bir sınıfla ilgili temel problemler olduğunu gösterir. Büyük bir işlevi daha küçük işlevlere bölmek, çoğaltmayı yeniden düzenlemede yararlı bir adım olabilir, ancak sonuncusu olmamalıdır. Örneğin, iki işlevi oluşturursunuz:

processSection1HeaderData();
processSection2HeaderData();

Bölüm başlıklarını işlemek için kullanılan kodda herhangi bir benzerlik yok mu? Farklılıkları parametrelendirerek her ikisi için ortak bir işlev çıkarabilir misiniz?


Bu aslında üretim kodu değildir, bu nedenle işlev adları sadece örnektir (üretimde işlev adları daha açıklayıcıdır). Genel olarak, hayır, farklı bölümler arasında benzerlikler yoktur (benzerlikler olduğunda, kodu mümkün olduğunca tekrar kullanmaya çalışıyorum).
Eric

1

Bir işlevi mantıksal alt işlevlere bölmek, alt işlevler yeniden kullanılmasa bile yararlıdır - kısa, kolay anlaşılan bloklara ayırır. Ancak mantıksal birimler tarafından yapılmalıdır, sadece satır sayısı değil. Bunu nasıl bozmanız gerektiği kodunuza bağlı olacaktır, ortak temalar aynı nesnede döngüler, koşullar, işlemler olacaktır; temelde ayrı bir görev olarak tanımlayabileceğiniz her şey.

Yani, evet, iyi bir stil - bu garip gelebilir, ancak açıkladığınız sınırlı yeniden kullanımınız göz önüne alındığında, BAŞLANGIÇ yeniden kullanımı hakkında dikkatlice düşünmenizi tavsiye ederim. Kullanımın tamamen aynı olduğundan ve tesadüfen aynı olmadığından emin olun. Tüm vakaları aynı blokta ele almayı denemek, çoğu zaman en büyük yerde büyük bir bungalov işlevine nasıl ulaştığınızdır.


0

Bunun çoğunlukla kişisel tercih olduğunu düşünüyorum. Eğer işlevleriniz yeniden kullanılacaksa (ve onları daha genel ve tekrar kullanılabilir hale getiremediğinizden emin misiniz?) Kesinlikle onları ayırın diyebilirim. Ayrıca, hiçbir işlev veya yöntemin içinde 2-3'ten fazla kod ekranına sahip olmamasının iyi bir prensip olduğunu duydum. Bu nedenle, büyük işleviniz devam ederse, kodunuzu bölmek için daha okunabilir hale getirebilir.

Bu raporları geliştirmek için hangi teknolojiyi kullandığınızdan bahsetmiyorsunuz. Görünüşe göre C # kullanıyor olabilirsiniz. Bu doğru mu? Öyleyse, işlevinizi daha küçük olanlara bölmek istemiyorsanız #region yönergesini alternatif olarak da düşünebilirsiniz .


Evet, C # ile çalışıyorum. Bazı işlevleri yeniden kullanabilmem mümkün olabilir, ancak bu raporların birçoğu için farklı bölümlerin çok farklı verileri ve düzenleri vardır (müşteri gereksinimi).
Eric

1
Öneri bölgeleri hariç +1. Bölgeleri kullanmam.
Bernard

@Bernard - Belirli bir nedeni var mı? Askerin sadece işlevi bölmeyi zaten reddetmişse #regions kullanmayı düşüneceğini varsayıyorum.
Joshua Carmody

2
Bölgeler kodu gizleme eğilimindedir ve kötüye kullanılabilir (yani iç içe bölgeler). Bir kod dosyasında bir kerede tüm kodu görmeyi seviyorum. Bir dosyadaki kodu görsel olarak ayırmam gerekirse, eğik çizgi kullanırım.
Bernard

2
Bölgeler genellikle kodun yeniden düzenlenmesi gereken bir işarettir
kodlayıcı
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.