Saat ini saya sedang mengerjakan serangkaian laporan yang memiliki banyak bagian berbeda (semua memerlukan pemformatan berbeda), dan saya mencoba mencari cara terbaik untuk menyusun kode saya. Laporan serupa yang kami lakukan di masa lalu memiliki fungsi yang sangat besar (200+ baris) yang melakukan semua manipulasi dan pemformatan data untuk laporan, sehingga alur kerjanya terlihat seperti ini:
DataTable reportTable = new DataTable();
void RunReport()
{
reportTable = DataClass.getReportData();
largeReportProcessingFunction();
outputReportToUser();
}
Saya ingin dapat memecah fungsi-fungsi besar ini menjadi potongan-potongan yang lebih kecil, tetapi saya takut bahwa pada akhirnya saya akan memiliki puluhan fungsi yang tidak dapat digunakan kembali, dan fungsi "do everything here" yang serupa yang tugasnya hanya untuk panggil semua fungsi yang lebih kecil ini, seperti:
void largeReportProcessingFunction()
{
processSection1HeaderData();
calculateSection1HeaderAverages();
formatSection1HeaderDisplay();
processSection1SummaryTableData();
calculateSection1SummaryTableTotalRow();
formatSection1SummaryTableDisplay();
processSection1FooterData();
getSection1FooterSummaryTotals();
formatSection1FooterDisplay();
processSection2HeaderData();
calculateSection1HeaderAverages();
formatSection1HeaderDisplay();
calculateSection1HeaderAverages();
...
}
Atau, jika kita melangkah lebih jauh:
void largeReportProcessingFunction()
{
callAllSection1Functions();
callAllSection2Functions();
callAllSection3Functions();
...
}
Apakah ini benar-benar solusi yang lebih baik? Dari sudut pandang organisasi saya kira itu (yaitu semuanya jauh lebih terorganisir daripada yang seharusnya), tetapi sejauh pembacaan kode saya tidak yakin (rantai fungsi yang berpotensi besar yang hanya memanggil fungsi lain).
Pikiran?
sumber
Jawaban:
Ini biasanya disebut sebagai dekomposisi fungsional dan umumnya merupakan hal yang baik untuk dilakukan, jika dilakukan dengan benar.
Juga, implementasi suatu fungsi harus dalam satu level abstraksi. Jika Anda mengambil
largeReportProcessingFunction
, maka perannya adalah untuk menentukan langkah pemrosesan yang berbeda yang harus diambil dalam urutan yang mana. Implementasi dari masing-masing langkah tersebut adalah pada lapisan abstraksi di bawah ini danlargeReportProcessingFunction
tidak harus bergantung padanya secara langsung.Harap perhatikan bahwa ini adalah pilihan penamaan yang buruk:
Anda lihat
callAllSection1Functions
adalah nama yang tidak benar-benar memberikan abstraksi, karena itu tidak benar-benar mengatakan apa yang dilakukannya, melainkan bagaimana melakukannya. Itu harus disebutprocessSection1
sebaliknya atau apa pun yang sebenarnya bermakna dalam konteks.sumber
callAllSection1Functions
detail kebocoran tentang implementasi. Dari perspektif luar, tidak masalah apakahprocessSection1
menolak pekerjaannya untuk "semua fungsi section1" atau apakah itu melakukannya secara langsung atau apakah itu menggunakan debu peri untuk memanggil unicorn yang akan melakukan pekerjaan yang sebenarnya. Yang penting adalah, bahwa setelah panggilan keprocessSection1
, bagian1 dapat dianggap diproses . Saya setuju bahwa pemrosesan adalah nama generik, tetapi jika Anda memiliki kohesi yang tinggi (yang harus selalu Anda perjuangkan) maka konteks Anda biasanya cukup sempit untuk membuatnya ambigu.Anda bilang Anda menggunakan C #, jadi saya ingin tahu apakah Anda bisa membangun hierarki kelas terstruktur mengambil keuntungan dari fakta bahwa Anda menggunakan bahasa berorientasi objek? Misalnya, jika masuk akal untuk memecah laporan menjadi beberapa bagian, memiliki
Section
kelas dan memperluasnya untuk persyaratan spesifik klien.Mungkin masuk akal untuk memikirkannya seolah-olah Anda sedang membuat GUI non-interaktif. Pikirkan tentang objek yang mungkin Anda buat seolah-olah mereka komponen GUI yang berbeda. Tanyakan pada diri Anda seperti apa laporan dasar itu? Bagaimana dengan yang rumit? Di mana titik ekstensi seharusnya?
sumber
Tergantung. Jika semua fungsi yang Anda buat benar-benar satu unit kerja maka tidak apa-apa, jika mereka hanya baris 1-15, 16-30, 31-45 dari fungsi panggilan mereka yang buruk. Fungsi panjang bukan kejahatan, jika mereka melakukan satu hal, jika Anda memiliki 20 filter untuk laporan Anda yang memiliki fungsi validasi yang memeriksa semua dua puluh akan lama. Tidak apa-apa, memecahnya dengan filter atau grup filter hanya menambah kompleksitas tambahan tanpa manfaat nyata.
Memiliki banyak fungsi pribadi juga membuat pengujian unit otomatis menjadi lebih sulit, sehingga beberapa akan mencoba menghindarinya karena alasan itu, tetapi menurut saya ini adalah alasan yang buruk karena cenderung membuat desain yang lebih besar dan lebih rumit untuk mengakomodasi pengujian.
sumber
Karena Anda menggunakan C #, coba dan pikirkan lebih dalam objek. Sepertinya Anda masih mengkode secara prosedural dengan memiliki variabel kelas "global" yang bergantung pada fungsi selanjutnya.
Melepaskan logika Anda dalam fungsi-fungsi yang terpisah jelas lebih baik daripada memiliki semua logika dalam satu fungsi. Saya berani bertaruh Anda bisa melangkah lebih jauh dengan memisahkan data yang berfungsi berfungsi dengan memecah ini dalam beberapa objek.
Pertimbangkan untuk memiliki kelas ReportBuilder tempat Anda dapat menyampaikan data laporan. Jika Anda dapat membagi ReportBuilder ke dalam kelas yang terpisah, lakukan ini juga.
sumber
Iya.
Jika Anda memiliki segmen kode yang perlu digunakan di beberapa tempat, fungsi sendiri. Kalau tidak, jika Anda harus membuat perubahan pada fungsionalitas Anda perlu membuat perubahan di banyak tempat. Ini tidak hanya meningkatkan pekerjaan tetapi meningkatkan risiko bug muncul ketika kode diubah di satu tempat tetapi tidak di tempat lain tetapi di mana tetapi diperkenalkan di satu tempat tetapi tidak di tempat lain.
Ini juga membantu membuat metode lebih mudah dibaca jika nama metode deskriptif digunakan.
sumber
Fakta bahwa Anda menggunakan penomoran untuk membedakan fungsi menunjukkan bahwa ada masalah mendasar dengan duplikasi atau kelas melakukan terlalu banyak. Memecah fungsi besar menjadi banyak fungsi yang lebih kecil bisa menjadi langkah yang berguna dalam refactoring keluar duplikasi, tetapi seharusnya tidak menjadi yang terakhir. Misalnya, Anda membuat dua fungsi:
Apakah tidak ada kesamaan dalam kode yang digunakan untuk memproses tajuk bagian? Bisakah Anda mengekstrak fungsi umum untuk keduanya dengan parameterisasi perbedaan?
sumber
Memecah fungsi menjadi subfungsi logis sangat membantu, bahkan jika subfungsi tidak digunakan kembali - memecahnya menjadi blok pendek, mudah dipahami. Tapi itu harus dilakukan oleh unit logis, bukan hanya n # baris. Bagaimana Anda harus memecahnya akan tergantung pada kode Anda, tema umum adalah loop, kondisi, operasi pada objek yang sama; pada dasarnya segala sesuatu yang dapat Anda gambarkan sebagai tugas tersendiri.
Jadi, ya, ini gaya yang bagus - ini mungkin terdengar aneh, tetapi mengingat penggambaran Anda yang terbatas, saya akan merekomendasikan agar Anda memikirkan dengan hati-hati tentang penggunaan kembali yang PERLUASAN. Pastikan penggunaannya benar-benar identik, dan bukan hanya kebetulan yang sama. Mencoba untuk menangani semua kasus di blok yang sama sering kali Anda berakhir dengan fungsi canggung yang besar di tempat pertama.
sumber
Saya pikir ini sebagian besar pilihan pribadi. Jika fungsi Anda akan digunakan kembali (dan apakah Anda yakin Anda tidak bisa membuatnya lebih generik dan dapat digunakan kembali?) Saya pasti akan mengatakan membagi mereka. Saya juga mendengar bahwa ini adalah prinsip yang baik bahwa tidak ada fungsi atau metode yang harus memiliki lebih dari 2-3 layar kode di dalamnya. Jadi, jika fungsi besar Anda terus berjalan, itu mungkin membuat kode Anda lebih mudah dibaca untuk dibagi.
Anda tidak menyebutkan teknologi apa yang Anda gunakan untuk mengembangkan laporan ini. Sepertinya Anda mungkin menggunakan C #. Apakah itu benar? Jika demikian, Anda mungkin juga mempertimbangkan arahan #region sebagai alternatif jika Anda tidak ingin membagi fungsi Anda menjadi yang lebih kecil.
sumber