Seberapa besar ok untuk Kelas?

29

Saya seorang pengembang lama (saya 49) tetapi agak baru untuk pengembangan berorientasi objek. Saya telah membaca tentang OO sejak Bertrand Meyer Eiffel, tetapi telah melakukan sedikit pemrograman OO.

Intinya adalah setiap buku tentang desain OO dimulai dengan contoh perahu, mobil atau benda umum apa pun yang sering kita gunakan, dan mereka mulai menambahkan atribut dan metode, dan menjelaskan bagaimana mereka memodelkan keadaan objek dan apa yang dapat dilakukan dengan saya t.

Jadi mereka biasanya melakukan sesuatu seperti "semakin baik model semakin baik itu mewakili objek dalam aplikasi dan semakin baik semuanya keluar".

Sejauh ini sangat baik, tetapi, di sisi lain, saya telah menemukan beberapa penulis yang memberikan resep seperti "kelas harus sesuai hanya dalam satu halaman" (saya akan menambahkan "pada ukuran monitor apa?" Sekarang kami mencoba untuk tidak untuk mencetak kode!).

Ambil contoh sebuah PurchaseOrderkelas, yang memiliki mesin keadaan terbatas yang mengendalikan perilakunya dan kumpulan PurchaseOrderItem, salah satu argumen di sini di tempat kerja adalah bahwa kita harus menggunakan PurchaseOrderkelas sederhana, dengan beberapa metode (sedikit lebih dari kelas data), dan memiliki sebuah PurchaseOrderFSM“kelas ahli” yang menangani mesin negara yang terbatas untuk PurchaseOrder.

Saya akan mengatakan bahwa jatuh dalam klasifikasi "Cemburu Fitur" atau "Keintiman Tidak Pantas" klasifikasi Jeff Atwood's Kode Bau posting di Coding Horror. Saya hanya menyebutnya akal sehat. Jika saya dapat mengeluarkan, menyetujui atau membatalkan pesanan pembelian saya yang sebenarnya, maka PurchaseOrderkelas tersebut harus memiliki issuePO, approvePOdan cancelPOmetode.

Bukankah itu sesuai dengan prinsip-prinsip lama “maksimalkan kohesi” dan “perkecil kopling” yang saya pahami sebagai landasan OO?

Selain itu, tidakkah hal itu membantu mempertahankan kelas?

Miguel Veloso
sumber
17
Tidak ada alasan untuk menyandikan nama ketik ke nama metode. Yaitu jika Anda memiliki PurchaseOrder, Anda hanya bisa nama metode Anda issue, approvedan cancel. The POakhiran menambahkan hanya nilai negatif.
dash-tom-bang
2
Selama Anda tinggal jauh dari metode lubang hitam , Anda harus baik-baik saja. :)
Mark C
1
Dengan resolusi layar saya saat ini, saya akan mengatakan 145 karakter besar.
DavRob60
Terima kasih semuanya atas komentar Anda, mereka benar-benar membantu saya dengan masalah ini. Jika situs ini mengizinkan, saya juga akan memilih jawaban TMN dan lazarus, tetapi hanya memperbolehkannya, jadi ia pergi ke Craig. Salam Hormat.
Miguel Veloso

Jawaban:

27

Kelas harus menggunakan Prinsip Tanggung Jawab Tunggal. Kelas paling besar yang saya lihat lakukan untuk banyak hal yang mengapa mereka terlalu besar. Lihatlah setiap metode dan kode yang memutuskan apakah itu harus di kelas ini atau terpisah, kode duplikat adalah petunjuk. Anda mungkin memiliki metode issuePO tetapi apakah itu berisi 10 baris kode akses data misalnya? Kode itu mungkin seharusnya tidak ada di sana.

Craig
sumber
1
Craig, saya tidak menyadari Prinsip Responsabilitas Tunggal, dan baru saja membacanya. Hal pertama yang terlintas di benak saya adalah, apa ruang lingkup Responsabilitas? untuk PO saya akan mengatakan mengelola negaranya, dan itulah alasan untuk issuePO, menyetujuiPO dan membatalkan metodePO, tetapi juga termasuk berinteraksi dengan kelas PurchaseOrderItem yang menangani status item PO. Jadi saya tidak jelas apakah pendekatan ini konsisten dengan SRP. apa yang akan Anda sampaikan?
Miguel Veloso
1
+1 untuk jawaban yang sangat bagus dan ringkas. Secara realistis, tidak ada ukuran pasti untuk seberapa besar kelas seharusnya. Menerapkan SRP memastikan bahwa kelas hanya sebesar yang dibutuhkan .
S.Robins
1
@MiguelVeloso - Menyetujui PO mungkin memiliki logika dan aturan yang berbeda yang terkait dengannya daripada menerbitkan PO. Jika demikian, masuk akal untuk memisahkan tanggung jawab menjadi POApprover dan POIssuer. Idenya adalah bahwa jika aturan berubah untuk satu, mengapa Anda ingin mengacaukan dengan kelas yang berisi yang lain.
Matius Flynn
12

Menurut pendapat saya ukuran kelas tidak masalah asalkan variabel, fungsi dan metode relevan dengan kelas yang bersangkutan.

Mike42
sumber
1
Di sisi lain, ukuran besar menunjukkan bahwa metode tersebut mungkin tidak relevan dengan kelas yang bersangkutan.
Billy ONeal
5
@ Billy: Saya akan mengatakan bahwa kelas besar adalah bau kode, tetapi mereka tidak secara otomatis buruk.
David Thornley
@ David: Itu sebabnya ada kata "mungkin" di sana - ini penting :)
Billy ONeal
Saya harus tidak setuju ... Saya bekerja pada sebuah proyek yang memiliki konvensi penamaan yang cukup konsisten dan segala sesuatunya ditata dengan baik ... tapi saya masih memiliki kelas yang terdiri dari 50k baris kode. Ini terlalu besar :)
Jay
2
Jawaban ini menunjukkan dengan tepat bagaimana "jalan menuju neraka" berjalan - jika seseorang tidak membatasi tanggung jawab suatu kelas, akan ada banyak variabel, fungsi, dan metode yang relevan dengan kelas - dan ini mengarah dengan mudah memiliki terlalu banyak dari mereka.
Doc Brown
7

Masalah terbesar dengan kelas-kelas besar adalah ketika datang untuk menguji kelas-kelas itu, atau interaksinya dengan kelas-kelas lain. Ukuran kelas besar dengan sendirinya bukan masalah, tetapi seperti semua bau, itu menunjukkan potensi masalah . Secara umum, ketika kelas besar, itu merupakan indikasi bahwa kelas melakukan lebih dari satu kelas.

Untuk analogi mobil Anda, jika kelas carterlalu besar, itu mungkin merupakan indikasi bahwa itu perlu dipecah menjadi kelas engine, kelas door, dan kelas windshield, dll.

Billy ONeal
sumber
8
Dan jangan lupa Mobil harus mengimplementasikan antarmuka Kendaraan. :-)
Chris
7

Saya tidak berpikir ada definisi spesifik dari kelas yang terlalu besar. Namun, saya akan menggunakan pedoman berikut untuk menentukan apakah saya harus memperbaiki kelas saya atau mendefinisikan kembali ruang lingkup kelas:

  1. Apakah ada banyak variabel yang independen satu sama lain? (Toleransi pribadi saya sekitar 10 ~ 20 dalam banyak kasus)
  2. Apakah ada banyak metode yang dirancang untuk kasus sudut? (Toleransi pribadi saya adalah ... well, itu sangat tergantung pada suasana hati saya, saya kira)

Berkenaan dengan 1, bayangkan bahwa Anda menentukan ukuran kaca depan Anda, ukuran roda, model bola lampu ekor, dan 100 detail lainnya di kelas Anda car. Meskipun mereka semua "relevan" dengan mobil, sangat sulit untuk melacak perilaku kelas tersebut car. Dan ketika suatu hari kolega Anda secara tidak sengaja (atau, dalam beberapa kasus, dengan sengaja) mengubah ukuran kaca depan berdasarkan pada model bohlam ekor, Anda perlu selamanya mengetahui mengapa kaca depan tidak lagi masuk ke dalam mobil Anda.

Berkenaan dengan 2, bayangkan bahwa Anda mencoba untuk mendefinisikan semua perilaku yang mungkin dimiliki oleh sebuah mobil di kelas car, tetapi car::open_roof()hanya akan tersedia untuk convertible, dan car::open_rear_door()tidak berlaku untuk mobil-mobil 2 pintu itu. Meskipun convertible dan mobil 2 pintu adalah "mobil" menurut definisi, menerapkannya di kelascar menyulitkan kelas. Kelas menjadi lebih sulit untuk digunakan dan lebih sulit untuk dipertahankan.

Selain 2 pedoman ini, saya akan menyarankan definisi yang jelas tentang ruang lingkup kelas. Setelah Anda mendefinisikan ruang lingkup, terapkan kelas secara ketat berdasarkan pada definisi. Sebagian besar waktu, orang mendapatkan kelas "terlalu besar" karena definisi ruang lingkup yang buruk, atau karena fitur sewenang-wenang yang ditambahkan ke kelas

YYC
sumber
7

Katakan apa yang Anda butuhkan dalam 300 baris

Catatan: aturan praktis ini menganggap Anda mengikuti pendekatan 'satu kelas per file' yang merupakan ide perawatan yang bagus

Ini bukan aturan mutlak, tetapi jika saya melihat lebih dari 200 baris kode dalam satu kelas saya curiga. 300 baris dan bel peringatan menyala. Ketika saya membuka kode orang lain dan menemukan 2000 baris, saya tahu ini waktu refactoring bahkan tanpa membaca halaman pertama.

Sebagian besar ini karena pemeliharaan. Jika objek Anda sangat kompleks sehingga Anda tidak dapat mengekspresikan perilakunya dalam 300 baris, itu akan sangat sulit bagi orang lain untuk mengerti.

Saya menemukan bahwa saya membengkokkan aturan ini dalam Model -> ViewModel -> Lihat dunia tempat saya membuat 'objek perilaku' untuk halaman UI karena sering membutuhkan lebih dari 300 baris untuk menggambarkan serangkaian perilaku lengkap pada sebuah halaman. Namun saya masih baru dengan model pemrograman ini dan menemukan cara yang baik untuk refactoring / menggunakan kembali perilaku antara halaman / viewmodels yang secara bertahap mengurangi ukuran ViewModels saya.

Jay Beavers
sumber
Pedoman pribadi saya juga sekitar 300 baris. +1
Marcie
Saya pikir OP sedang mencari heuristik, dan ini bagus.
neontapir
Terima kasih, penting untuk menggunakan angka yang tepat sebagai pedoman.
Will Sheppard
6

Mari kita mundur:

Bukankah itu sesuai dengan prinsip-prinsip lama “maksimalkan kohesi” dan “perkecil kopling” yang saya pahami sebagai landasan OO?

Sebenarnya, ini adalah landasan pemrograman, yang OO hanyalah satu paradigma.

Saya akan membiarkan Antoine de Saint-Exupéry menjawab pertanyaan Anda (karena saya malas;)):

Kesempurnaan tercapai, bukan ketika tidak ada lagi yang ditambahkan, tetapi ketika tidak ada lagi yang tersisa untuk diambil.

Semakin sederhana kelasnya, semakin yakin Anda akan hal itu benar, semakin mudah untuk mengujinya sepenuhnya .

Matthieu M.
sumber
3

Saya dengan OP pada klasifikasi Fitur Envy. Tidak ada yang mengganggu saya lebih dari memiliki banyak kelas dengan banyak metode yang bekerja pada internal kelas lain. OO menyarankan agar Anda memodelkan data dan operasi pada data itu. Itu tidak berarti bahwa Anda menempatkan operasi di tempat yang berbeda dari data! Itu mencoba untuk mendapatkan Anda untuk memasukkan data dan metode-metode bersama-sama .

Dengan model cardan personbegitu lazim, itu akan seperti memasukkan kode untuk membuat sambungan listrik, atau bahkan memutar starter, ke dalam personkelas. Saya berharap ini jelas salah bagi programmer berpengalaman.

Saya tidak benar-benar memiliki ukuran kelas atau metode yang ideal, tetapi saya lebih memilih untuk menjaga metode dan fungsi saya tetap pada satu layar sehingga saya dapat melihat keseluruhannya di satu tempat. (Saya telah mengkonfigurasi Vim untuk membuka jendela 60-line, yang kebetulan berada tepat di sekitar jumlah baris pada halaman yang dicetak.) Ada tempat-tempat di mana ini tidak masuk akal, tetapi bekerja untuk saya. Saya suka mengikuti pedoman yang sama untuk definisi kelas, dan mencoba untuk menjaga file saya di bawah beberapa "halaman". Lebih dari 300, 400 baris mulai terasa berat (sebagian besar karena kelas sudah mulai mengambil terlalu banyak tanggung jawab langsung).

dash-tom-bang
sumber
+1 - aturan praktis yang baik tetapi tidak bersikap otoriter tentangnya. Namun saya akan mengatakan bahwa hanya karena suatu kelas dibubarkan tidak berarti bahwa kelas-kelas yang berbeda harus menyentuh anggota pribadi masing-masing.
Billy ONeal
Saya tidak berpikir bahwa kelas yang berbeda harus pernah menyentuh bagian pribadi masing-masing. Sementara akses 'teman' nyaman untuk menjalankan sesuatu, (bagi saya) itu adalah batu loncatan untuk desain yang lebih baik. Secara umum saya menghindari pengakses juga, karena tergantung pada internal kelas lain adalah bau kode lain .
dash-tom-bang
1

Ketika definisi kelas memiliki beberapa ratus metode, itu terlalu besar.

C Johnson
sumber
1

Saya sepenuhnya setuju dengan penggunaan prinsip tanggung jawab tunggal untuk kelas tetapi jika itu diikuti dan menghasilkan kelas besar maka jadilah itu. Fokus sebenarnya haruslah bahwa metode dan properti individual tidak terlalu besar, kompleksitas siklomatik harus menjadi panduan di sana dan mengikuti yang dapat menghasilkan banyak metode pribadi yang akan meningkatkan ukuran kelas keseluruhan tetapi akan membuatnya mudah dibaca dan diuji ke tingkat granular. Jika kode tetap dapat dibaca maka ukuran tidak relevan.

Lazarus
sumber
1

Mengeluarkan pesanan pembelian seringkali merupakan proses yang rumit yang melibatkan lebih dari sekedar pesanan pembelian. Dalam hal ini, menjaga logika bisnis di kelas yang terpisah dan membuat pesanan pembelian lebih sederhana mungkin adalah hal yang benar untuk dilakukan. Namun secara umum, Anda benar - Anda tidak ingin kelas "struktur data". Misalnya, metode "POManager" kelas bisnis Anda issue()mungkin harus memanggil POissue() . PO kemudian dapat mengatur statusnya menjadi "Diterbitkan" dan catat tanggal penerbitannya, sementara POManager kemudian dapat mengirim pemberitahuan ke akun yang dibayarkan untuk memberi tahu mereka mengharapkan adanya faktur, dan pemberitahuan lain untuk inventaris sehingga mereka tahu ada sesuatu yang masuk dan tanggal yang diharapkan.

Siapa pun yang mendorong "aturan" untuk ukuran metode / kelas jelas belum menghabiskan cukup waktu di dunia nyata. Seperti yang disebutkan orang lain, ini sering merupakan indikator refactoring, tetapi kadang-kadang (terutama dalam pekerjaan jenis "pemrosesan data") Anda benar-benar perlu menulis kelas dengan metode tunggal yang membentang lebih dari 500 baris. Jangan menutup telepon.

TMN
sumber
Saya kira POManager akan menjadi "pengontrol" dan PurchaseOrder akan menjadi "model" dalam pola MVC, bukan?
Miguel Veloso
0

Saya telah menemukan beberapa penulis yang memberikan resep seperti "kelas harus sesuai hanya dalam satu halaman"

Dalam hal ukuran fisik suatu kelas, melihat kelas besar merupakan indikasi dari bau kode, tetapi tidak selalu berarti bahwa selalu ada bau. (Biasanya mereka memang seperti itu) Seperti yang dikatakan oleh para perompak di Pirates of the Caribbean, inilah yang lebih Anda sebut "pedoman" daripada aturan yang sebenarnya. Saya telah mencoba dengan ketat mengikuti "tidak lebih dari seratus LOC di kelas" satu kali dan hasilnya adalah BANYAK rekayasa berlebihan yang tidak perlu.

Jadi mereka biasanya melakukan sesuatu seperti "semakin baik model semakin baik itu mewakili objek dalam aplikasi dan semakin baik semuanya keluar".

Saya biasanya memulai desain saya dengan ini. Apa yang dilakukan kelas ini di dunia nyata? Bagaimana kelas saya harus mencerminkan objek ini sebagaimana dinyatakan dalam persyaratannya? Kami menambahkan sedikit status di sini, bidang di sana, metode untuk bekerja di bidang itu, tambahkan beberapa lagi dan voila! Kami punya kelas pekerja. Sekarang isi tanda tangan dengan implementasi yang tepat dan kami baik untuk pergi, atau begitulah menurut Anda. Sekarang kita memiliki kelas besar yang bagus dengan semua fungsi yang kita butuhkan. Tetapi masalah dengan ini adalah, tidak memperhitungkan cara dunia nyata bekerja. Dan maksud saya itu tidak memperhitungkan, "Ganti".

Alasan mengapa kelas lebih disukai dibagi menjadi bagian-bagian yang lebih kecil adalah bahwa sulit untuk mengubah kelas besar nanti tanpa mempengaruhi logika yang ada atau memperkenalkan bug baru atau dua bug secara tidak sengaja atau hanya membuat semuanya sulit untuk digunakan kembali. Di sinilah refactoring, pola desain, SOLID, dll datang untuk bermain dan hasil akhirnya biasanya kelas kecil bekerja pada subkelas yang lebih kecil, lebih granular lainnya.

Juga, dalam contoh Anda, menambahkan IssuePO, ApprovePO, dan Cancel PO ke dalam kelas PurchaseOrder tampaknya tidak masuk akal. Pesanan Pembelian tidak menerbitkan, menyetujui, atau membatalkan sendiri. Jika PO harus memiliki metode, maka metode harus bekerja pada negaranya bukan pada kelas secara keseluruhan. Mereka benar-benar hanya kelas data / catatan yang bekerja pada kelas lain.

Jonn
sumber
0

Cukup besar untuk memuat semua logika yang diperlukan untuk melakukan pekerjaannya.

Cukup kecil untuk dipertahankan dan mengikuti Prinsip Tanggung Jawab Tunggal .

RMalke
sumber