Adakah hal yang memiliki terlalu banyak fungsi / metode pribadi?

63

Saya mengerti pentingnya kode yang terdokumentasi dengan baik. Tetapi saya juga memahami pentingnya kode dokumentasi diri . Semakin mudah membaca fungsi tertentu secara visual, semakin cepat kita dapat melanjutkan selama pemeliharaan perangkat lunak.

Dengan itu, saya suka memisahkan fungsi besar menjadi yang lebih kecil. Tapi saya melakukannya ke titik di mana kelas dapat memiliki lebih dari lima dari mereka hanya untuk melayani satu metode publik. Sekarang gandakan lima metode pribadi dengan lima metode publik, dan Anda mendapatkan sekitar dua puluh lima metode tersembunyi yang mungkin akan dipanggil hanya satu kali oleh metode publik tersebut.

Tentu, sekarang lebih mudah untuk membaca metode-metode publik itu, tetapi saya tidak bisa tidak berpikir bahwa memiliki terlalu banyak fungsi adalah praktik yang buruk.

[Sunting]

Orang-orang bertanya kepada saya mengapa saya pikir memiliki terlalu banyak fungsi adalah praktik yang buruk.

Jawaban sederhana: itu adalah firasat.

Kepercayaan saya, sedikit pun, tidak didukung oleh pengalaman rekayasa perangkat lunak selama berjam-jam. Hanya ketidakpastian yang memberi saya "blok penulis", tetapi untuk seorang programmer.

Di masa lalu, saya hanya memprogram proyek pribadi. Baru-baru ini saya pindah ke proyek berbasis tim. Sekarang, saya ingin memastikan bahwa orang lain dapat membaca dan memahami kode saya.

Saya tidak yakin apa yang akan meningkatkan keterbacaan. Di satu sisi, saya berpikir untuk memisahkan satu fungsi besar menjadi yang lebih kecil dengan nama yang dapat dipahami. Tetapi ada sisi lain dari diri saya yang mengatakan bahwa itu hanya mubazir.

Jadi, saya meminta ini untuk menerangi diri saya sendiri untuk memilih jalan yang benar.

[Sunting]

Di bawah ini, saya menyertakan dua versi tentang bagaimana saya bisa menyelesaikan masalah saya. Yang pertama menyelesaikannya dengan tidak memisahkan potongan kode besar. Yang kedua melakukan hal-hal yang terpisah.

Versi pertama:

public static int Main()
{
    // Displays the menu.
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Versi kedua:

public static int Main()
{
    DisplayMenu();
}

private static void DisplayMenu()
{
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Dalam contoh di atas, yang terakhir memanggil fungsi yang hanya akan digunakan sekali selama seluruh runtime program.

Catatan: kode di atas digeneralisasi, tetapi sifatnya sama dengan masalah saya.

Sekarang, inilah pertanyaan saya: yang mana? Apakah saya memilih yang pertama, atau yang kedua?

Sal
sumber
1
"Memiliki terlalu banyak fungsi adalah praktik yang buruk."? Mengapa? Harap perbarui pertanyaan Anda untuk menjelaskan mengapa ini tampak buruk bagi Anda.
S.Lott
Saya akan merekomendasikan membaca gagasan 'keterpaduan kelas' - Bob Martin membahas ini dalam 'Kode Bersih'.
JᴀʏMᴇᴇ
Menambahkan ke jawaban lain, pisah dan beri nama, tetapi mari kita semua ingat bahwa, ketika kelas tumbuh, variabel anggotanya menjadi lebih dan lebih seperti variabel global. Salah satu cara untuk memperbaiki masalah (terlepas dari solusi yang lebih baik seperti refactoring ;-)) adalah dengan memisahkan metode-metode pribadi kecil menjadi fungsi yang berdiri sendiri jika memungkinkan (dalam hal itu mereka tidak memerlukan akses ke banyak negara). Beberapa bahasa memungkinkan fungsi di luar kelas, yang lain memiliki kelas statis. Dengan cara ini, Anda bisa memahami fungsi itu secara terpisah.
Pablo H
Jika ada yang bisa memberi tahu saya mengapa "jawaban ini tidak berguna", saya akan berterima kasih. Seseorang selalu bisa belajar. :-)
Pablo H
@PabloH Jawaban yang Anda berikan memberikan pengamatan dan wawasan yang baik untuk OP, namun sebenarnya tidak menjawab pertanyaan yang diajukan. Saya memindahkannya ke kolom komentar.
maple_shaft

Jawaban:

36

Sekarang gandakan lima metode pribadi dengan lima metode publik, dan Anda mendapatkan sekitar dua puluh lima metode tersembunyi yang mungkin akan dipanggil hanya satu kali oleh metode publik tersebut.

Inilah yang dikenal sebagai Enkapsulasi , yang menciptakan Abstraksi Kontrol di tingkat yang lebih tinggi. Ini hal yang baik.

Ini berarti bahwa siapa pun yang membaca kode Anda, ketika mereka sampai ke startTheEngine()metode dalam kode Anda, dapat mengabaikan semua rincian tingkat yang lebih rendah seperti openIgnitionModule(), turnDistributorMotor(), sendSparksToSparkPlugs(), injectFuelIntoCylinders(), activateStarterSolenoid(), dan semua lainnya yang kompleks, kecil, fungsi yang harus dijalankan untuk memfasilitasi fungsi yang jauh lebih besar, lebih abstrak startTheEngine().

Kecuali jika masalah yang Anda hadapi dalam kode Anda berhubungan langsung dengan salah satu komponen itu, pengelola kode dapat melanjutkan, mengabaikan fungsi kotak pasir yang dienkapsulasi.

Ini juga memiliki keuntungan tambahan untuk membuat kode Anda lebih mudah diuji. . Sebagai contoh, saya dapat menulis test case untuk turnAlternatorMotor(int revolutionRate)dan menguji fungsionalitasnya sepenuhnya independen dari sistem lain. Jika ada masalah dengan fungsi itu dan outputnya tidak seperti yang saya harapkan, maka saya tahu apa masalahnya.

Kode yang tidak dipecah menjadi komponen-komponen jauh lebih sulit untuk diuji. Tiba-tiba, pengelola Anda hanya melihat abstraksi saja daripada bisa menggali ke dalam komponen yang terukur.

Saran saya adalah tetap melakukan apa yang Anda lakukan karena skala kode Anda, mudah dipelihara, dan dapat digunakan dan diperbarui untuk tahun-tahun mendatang.

jmort253
sumber
3
Jadi Anda percaya itu 'enkapsulasi' yang baik untuk membuat logika tersedia untuk seluruh kelas yang hanya disebut di satu tempat?
Steven Jeuris
9
@ Seven Jeuris: Selama fungsi-fungsi itu dibuat pribadi, saya tidak melihat masalah dengan itu; pada kenyataannya, saya menemukan bahwa, sebagaimana dinyatakan dalam jawaban ini, lebih mudah dibaca. Jauh lebih mudah untuk memahami fungsi publik tingkat tinggi yang hanya memanggil serangkaian fungsi pribadi tingkat rendah (yang telah dinamai dengan tepat, tentu saja). Tentu saja, semua kode itu dapat dimasukkan ke dalam fungsi tingkat tinggi itu sendiri, tetapi kemudian akan membutuhkan waktu lebih lama untuk mendapatkan pemahaman tentang kode karena Anda harus melihat lebih banyak dari itu di satu dan di tempat yang sama.
gablin
2
@ gablin: fungsi pribadi masih dapat diakses dari dalam seluruh kelas. Dalam hal ini (tampaknya kontroversial) artikel yang saya membahas bagaimana 'pembacaan global hilang ketika memiliki fungsi terlalu banyak. Sebenarnya prinsip umum selain fungsi harus melakukan hanya satu hal, adalah Anda tidak boleh memiliki terlalu banyak. Anda hanya memperoleh 'keterbacaan lokal' dengan memisahkan hanya karena kekurangan, yang juga dapat dicapai dengan komentar sederhana dan 'paragraf kode'. Oke, kode harus mendokumentasikan diri sendiri, tetapi komentar tidak usang!
Steven Jeuris
1
@ Seven - Apa yang lebih mudah dipahami? myString.replaceAll(pattern, originalData);atau saya menyisipkan seluruh metode replaceAll dalam kode saya, termasuk array char backing yang mewakili objek string yang mendasari setiap dan setiap kali saya perlu menggunakan fungsionalitas String?
jmort253
7
@ Seven Jeuris: Anda benar juga. Jika suatu kelas memiliki terlalu banyak fungsi (publik atau pribadi), maka mungkin seluruh kelas berusaha melakukan terlalu banyak. Dalam kasus seperti itu, mungkin lebih baik untuk membaginya menjadi beberapa kelas yang lebih kecil, sama seperti Anda membagi fungsi yang terlalu besar menjadi beberapa fungsi yang lebih kecil.
gablin
22

Iya dan tidak. Prinsip dasarnya adalah: suatu metode harus melakukan satu hal dan satu hal saja

Benar untuk "memecah" metode Anda menjadi metode yang lebih kecil. Kemudian lagi, metode-metode itu seharusnya cukup umum untuk tidak hanya melayani metode "besar" itu tetapi dapat digunakan kembali di tempat lain. Dalam beberapa kasus suatu metode akan mengikuti struktur pohon sederhana, seperti Metode Inisialisasi yang memanggil InitializePlugins, InitializeInterface dll.

Jika Anda memiliki metode / kelas yang sangat besar itu biasanya merupakan pertanda bahwa ia melakukan banyak hal dan Anda perlu melakukan beberapa refactoring untuk memecah "gumpalan". Perphaps menyembunyikan beberapa kompleksitas di kelas lain di bawah abstraksi, dan gunakan injeksi dependensi

Homde
sumber
1
Sangat menyenangkan untuk membaca tidak semua orang mengikuti aturan 'satu hal' secara membabi buta! ; p Jawaban yang bagus!
Steven Jeuris
16
Saya sering memecah metode besar menjadi metode yang lebih kecil meskipun mereka hanya akan melayani metode yang lebih besar. Alasan untuk membaginya adalah karena menjadi lebih mudah untuk ditangani dan dipahami, daripada hanya memiliki satu gumpalan kode yang besar (yang mungkin atau mungkin tidak dikomentari sendiri dan dikomentari dengan baik).
gablin
1
Itu ok juga dalam beberapa kasus di mana Anda benar-benar tidak dapat menghindari metode besar. Misalnya, jika Anda memiliki metode Inisialisasi, Anda mungkin memanggilnya InitializeSystems, InitializePlugins, dll. Anda harus selalu mengecek diri sendiri bahwa refactoring tidak diperlukan
Homde
1
Kuncinya tentang setiap metode (atau fungsi) adalah bahwa ia harus memiliki antarmuka konseptual yang jelas, itu "kontrak". Jika Anda tidak dapat menjabarkannya, itu akan menjadi masalah dan Anda salah paham. (Mendokumentasikan kontrak secara eksplisit bisa menjadi hal yang baik - atau menggunakan alat untuk menegakkannya, gaya Eiffel - tetapi itu tidak sepenting memiliki di sana di tempat pertama.)
Donal Fellows
3
@ gablin: manfaat lain untuk dipertimbangkan: ketika saya memecah hal-hal menjadi fungsi yang lebih kecil, jangan berpikir "mereka hanya melayani satu fungsi lainnya", berpikir "mereka hanya melayani satu fungsi lainnya untuk saat ini ". Ada beberapa kesempatan di mana mempertimbangkan kembali metode besar telah menyelamatkan saya banyak waktu ketika menulis metode lain di masa depan karena fungsi yang lebih kecil lebih umum dan dapat digunakan kembali.
GS hingga
17

Saya pikir secara umum lebih baik berbuat salah pada fungsi yang terlalu banyak daripada terlalu sedikit. Dua pengecualian untuk aturan yang saya lihat dalam praktik adalah untuk prinsip KERING dan YAGNI . Jika Anda memiliki banyak fungsi yang hampir identik, Anda harus menggabungkannya dan menggunakan parameter untuk menghindari pengulangan. Anda juga dapat memiliki terlalu banyak fungsi jika Anda membuatnya "berjaga-jaga" dan tidak digunakan. Saya sama sekali tidak melihat ada yang salah dengan memiliki fungsi yang hanya digunakan sekali jika menambah keterbacaan dan pemeliharaan.

Karl Bielefeldt
sumber
10

Jawaban jmort253 yang bagus mengilhami saya untuk menindaklanjuti dengan jawaban "ya, tapi" ...

Memiliki banyak metode pribadi kecil bukanlah hal yang buruk, tetapi perhatikan perasaan picik yang membuat Anda memposting pertanyaan di sini :). Jika Anda sampai pada titik di mana Anda menulis tes yang difokuskan hanya pada metode pribadi (baik dengan memanggil mereka secara langsung, atau dengan membuat skenario sehingga seseorang dipanggil dengan cara tertentu supaya Anda dapat menguji hasilnya) maka Anda harus mengambil langkah mundur.

Apa yang mungkin Anda miliki adalah kelas baru dengan antarmuka publiknya sendiri yang lebih fokus dan berusaha melarikan diri. Pada titik itu Anda mungkin ingin berpikir tentang mengekstraksi kelas untuk merangkum bagian dari fungsionalitas kelas yang ada yang saat ini hidup dalam metode pribadi. Sekarang kelas asli Anda dapat menggunakan kelas baru Anda sebagai dependensi, dan Anda dapat menulis lebih banyak tes terfokus terhadap kelas itu secara langsung.

Pete Hodgson
sumber
Saya pikir ini adalah jawaban yang bagus dan merupakan arah bahwa kode pasti bisa pergi. Praktik terbaik mengatakan untuk menggunakan pengubah akses paling ketat yang memberi Anda fungsionalitas yang Anda butuhkan. Jika metode tidak digunakan di luar kelas, pribadi paling masuk akal. Jika lebih masuk akal untuk membuat metode publik atau memindahkannya ke kelas lain sehingga mereka dapat digunakan di tempat lain, maka dengan segala cara, itu bisa dilakukan juga. +1
jmort253
8

Hampir setiap proyek OO yang pernah saya ikuti menderita karena kelas yang terlalu besar, dan metode yang terlalu lama.

Ketika saya merasakan perlunya komentar yang menjelaskan bagian kode selanjutnya, maka saya mengekstrak bagian itu menjadi metode yang terpisah. Dalam jangka panjang, ini membuat kode lebih pendek. Metode-metode kecil itu dapat digunakan kembali lebih dari yang Anda harapkan pada awalnya. Anda mulai melihat peluang untuk mengumpulkan banyak dari mereka ke dalam kelas yang terpisah. Segera Anda memikirkan masalah Anda di tingkat yang lebih tinggi, dan seluruh upaya berjalan jauh lebih cepat. Fitur-fitur baru lebih mudah untuk ditulis, karena Anda dapat membangun kelas-kelas kecil yang Anda buat dari metode kecil itu.

kevin cline
sumber
7

Kelas dengan 5 metode publik dan 25 metode pribadi sepertinya tidak terlalu besar bagi saya. Pastikan kelas Anda memiliki tanggung jawab yang jelas dan jangan terlalu khawatir tentang jumlah metode.

Yang mengatakan, metode-metode pribadi harus fokus pada satu aspek spesifik dari seluruh tugas, tetapi tidak dalam cara "doSomethingPart1", "doSomethingPart2". Anda tidak akan memperoleh apa-apa jika metode pribadi itu hanya potongan cerita panjang yang dibagi secara sewenang-wenang, masing-masing menjadi tidak berarti di luar konteks keseluruhan.

pengguna281377
sumber
+1 untuk "Anda tidak akan memperoleh apa-apa jika metode pribadi itu hanya potongan-potongan cerita panjang yang dibagi secara sewenang-wenang, masing-masing menjadi tidak berarti di luar seluruh konteks."
Mahdi
4

Kutipan dari Clean Code R. Martin (p. 176):

Bahkan konsep yang mendasar seperti penghapusan duplikasi, ekspresi kode, dan SRP dapat diambil terlalu jauh. Dalam upaya membuat kelas dan metode kita kecil, kita mungkin membuat terlalu banyak kelas dan metode kecil. Jadi aturan ini [meminimalkan jumlah kelas dan metode] menyarankan agar kita juga menjaga agar fungsi dan kelas kita tetap rendah. Hitungan kelas dan metode yang tinggi kadang-kadang merupakan hasil dari dogmatisme sia-sia.

pengguna2418306
sumber
3

Beberapa opsi:

  1. Tidak apa-apa. Cukup beri nama metode pribadi dan beri nama metode publik Anda. Dan sebutkan metode publik Anda dengan baik.

  2. Abstraksi beberapa metode penolong ini ke dalam kelas penolong atau modul penolong. Dan pastikan kelas / modul penolong ini dinamai dengan baik dan merupakan abstraksi yang solid di dalam dan dari diri mereka sendiri.

yfeldblum
sumber
1

Saya tidak berpikir ada masalah "terlalu banyak metode pribadi" jika rasanya seperti itu mungkin merupakan gejala arsitektur kode yang buruk.

Inilah yang saya pikirkan tentang hal itu ... Jika arsitekturnya dirancang dengan baik, umumnya jelas di mana kode yang melakukan X seharusnya. Dapat diterima jika ada beberapa pengecualian untuk ini - tetapi ini harus benar-benar luar biasa jika tidak refactor arsitektur untuk menangani ini sebagai standar.

Jika masalahnya adalah saat membuka kelas Anda, itu penuh dengan metode pribadi yang memecah potongan yang lebih besar menjadi potongan kode yang lebih kecil, maka mungkin ini adalah gejala arsitektur yang hilang. Alih-alih kelas dan arsitektur, area kode ini telah diimplementasikan secara prosedural dalam satu kelas.

Menemukan keseimbangan di sini tergantung pada proyek dan persyaratannya. Argumen kontra untuk ini adalah mengatakan bahwa seorang programmer junior dapat mengetuk solusi dalam 50 baris kode yang mengambil arsitek 50 kelas.

Michael Shaw
sumber
0

Adakah hal yang memiliki terlalu banyak fungsi / metode pribadi?

Iya.

Dalam Python konsep "pribadi" (seperti yang digunakan oleh C ++, Java dan C #) tidak benar-benar ada.

Ada konvensi penamaan "semacam pribadi", tetapi hanya itu.

Pribadi dapat menyebabkan kode sulit diuji. Itu juga dapat mematahkan "Prinsip Terbuka-Tertutup" dengan membuat kode hanya ditutup.

Akibatnya, untuk orang-orang yang telah menggunakan Python, fungsi pribadi tidak memiliki nilai. Jadikan semuanya publik dan dilakukan dengan itu.

S.Lott
sumber
1
Bagaimana itu bisa melanggar prinsip tertutup terbuka? Kode terbuka untuk ekstensi dan ditutup untuk modifikasi terlepas dari apakah metode publik telah dipecah menjadi metode pribadi yang lebih kecil.
byxor