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 PurchaseOrder
kelas, yang memiliki mesin keadaan terbatas yang mengendalikan perilakunya dan kumpulan PurchaseOrderItem
, salah satu argumen di sini di tempat kerja adalah bahwa kita harus menggunakan PurchaseOrder
kelas 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 PurchaseOrder
kelas tersebut harus memiliki issuePO
, approvePO
dan cancelPO
metode.
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?
sumber
PurchaseOrder
, Anda hanya bisa nama metode Andaissue
,approve
dancancel
. ThePO
akhiran menambahkan hanya nilai negatif.Jawaban:
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.
sumber
Menurut pendapat saya ukuran kelas tidak masalah asalkan variabel, fungsi dan metode relevan dengan kelas yang bersangkutan.
sumber
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
car
terlalu besar, itu mungkin merupakan indikasi bahwa itu perlu dipecah menjadi kelasengine
, kelasdoor
, dan kelaswindshield
, dll.sumber
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:
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 tersebutcar
. 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
, tetapicar::open_roof()
hanya akan tersedia untuk convertible, dancar::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
sumber
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.
sumber
Mari kita mundur:
Sebenarnya, ini adalah landasan pemrograman, yang OO hanyalah satu paradigma.
Saya akan membiarkan Antoine de Saint-Exupéry menjawab pertanyaan Anda (karena saya malas;)):
Semakin sederhana kelasnya, semakin yakin Anda akan hal itu benar, semakin mudah untuk mengujinya sepenuhnya .
sumber
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
car
danperson
begitu lazim, itu akan seperti memasukkan kode untuk membuat sambungan listrik, atau bahkan memutar starter, ke dalamperson
kelas. 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).
sumber
Ketika definisi kelas memiliki beberapa ratus metode, itu terlalu besar.
sumber
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.
sumber
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.
sumber
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.
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.
sumber
Cukup besar untuk memuat semua logika yang diperlukan untuk melakukan pekerjaannya.
Cukup kecil untuk dipertahankan dan mengikuti Prinsip Tanggung Jawab Tunggal .
sumber