Saya adalah pengembang junior di antara para manula dan saya banyak berjuang dalam memahami pemikiran, alasan mereka.
Saya membaca Domain-Driven Design (DDD) dan tidak dapat memahami mengapa kita perlu membuat banyak kelas. Jika kita mengikuti metode perancangan perangkat lunak kita berakhir dengan 20-30 kelas yang dapat diganti dengan paling banyak dua file dan 3-4 fungsi. Ya, ini bisa berantakan, tetapi jauh lebih mudah dikelola dan dibaca.
Kapan saja saya ingin melihat apa yang dilakukan oleh beberapa jenis EntityTransformationServiceImpl
, saya perlu mengikuti banyak kelas, antarmuka, pemanggilan fungsi, konstruktor, kreasi mereka dan sebagainya.
Matematika sederhana:
- 60 baris kode dummy vs 10 kelas X 10 (misalkan kita memiliki logika yang sangat berbeda) = 600 baris kode berantakan vs 100 kelas + beberapa lagi untuk membungkus dan mengelolanya; jangan lupa untuk menambahkan injeksi ketergantungan.
- Membaca 600 baris kode berantakan = satu hari
- 100 kelas = satu minggu, masih lupa yang mana melakukan apa, kapan
Semua orang mengatakan itu mudah dipertahankan, tetapi untuk apa? Setiap kali Anda menambahkan fungsionalitas baru, Anda menambahkan lima kelas lagi dengan pabrik, entitas, layanan, dan nilai-nilai. Saya merasa kode seperti ini bergerak jauh lebih lambat daripada kode yang berantakan.
Katakanlah, jika Anda menulis kode berantakan 50K LOC dalam satu bulan, hal DDD membutuhkan banyak ulasan dan perubahan (saya tidak keberatan dengan tes dalam kedua kasus). Satu tambahan sederhana bisa memakan waktu seminggu jika tidak lebih.
Dalam satu tahun, Anda menulis banyak kode berantakan dan bahkan dapat menulis ulang beberapa kali, tetapi dengan gaya DDD, Anda masih tidak memiliki cukup fitur untuk bersaing dengan kode berantakan.
Tolong jelaskan. Mengapa kita membutuhkan gaya DDD ini dan banyak pola?
UPD 1 : Saya menerima begitu banyak jawaban, bisakah kalian tolong tambahkan komentar di suatu tempat atau edit jawaban Anda dengan tautan untuk daftar bacaan (tidak yakin mulai dari mana, DDD, Pola Desain, UML, Kode Lengkap, Refactoring, Pragmatis,. (begitu banyak buku bagus), tentu saja dengan urutan, sehingga saya juga bisa mulai memahami dan menjadi senior seperti yang Anda lakukan.
sumber
Jawaban:
Ini adalah masalah optimisasi
Seorang insinyur yang baik memahami bahwa masalah optimasi tidak ada artinya tanpa target. Anda tidak bisa hanya mengoptimalkan, Anda harus mengoptimalkan untuk sesuatu. Misalnya, opsi kompiler Anda termasuk mengoptimalkan kecepatan dan mengoptimalkan ukuran kode; ini kadang-kadang tujuan yang berlawanan.
Saya ingin memberi tahu istri saya bahwa meja saya dioptimalkan untuk menambah. Itu hanya tumpukan, dan sangat mudah untuk menambahkan barang. Istri saya akan lebih suka jika saya dioptimalkan untuk pengambilan, yaitu mengatur barang saya sedikit sehingga saya dapat menemukan sesuatu. Ini membuatnya lebih sulit, tentu saja, untuk ditambahkan.
Perangkat lunaknya juga sama. Anda tentu dapat mengoptimalkan pembuatan produk - menghasilkan satu ton kode monolitik secepat mungkin, tanpa perlu khawatir mengaturnya. Seperti yang sudah Anda perhatikan, ini bisa sangat, sangat cepat. Alternatifnya adalah mengoptimalkan perawatan - membuat pembuatan sentuhan menjadi lebih sulit, tetapi membuat modifikasi lebih mudah atau kurang berisiko. Itulah tujuan dari kode terstruktur.
Saya menyarankan bahwa produk perangkat lunak yang sukses hanya akan dibuat sekali tetapi dimodifikasi berkali-kali. Insinyur yang berpengalaman telah melihat basis kode yang tidak terstruktur mengambil kehidupan mereka sendiri dan menjadi produk, tumbuh dalam ukuran dan kompleksitas, sampai bahkan perubahan kecil sangat sulit dilakukan tanpa memperkenalkan risiko besar. Jika kode terstruktur, risiko dapat diatasi. Itu sebabnya kami pergi ke semua masalah ini.
Kompleksitas berasal dari hubungan, bukan elemen
Saya perhatikan dalam analisis Anda, Anda melihat kuantitas - jumlah kode, jumlah kelas, dll. Meskipun ini agak menarik, dampak nyata berasal dari hubungan antar elemen, yang meledak secara kombinatorial. Misalnya, jika Anda memiliki 10 fungsi dan tidak tahu yang bergantung pada mana, Anda memiliki 90 kemungkinan hubungan (dependensi) yang harus Anda khawatirkan - masing-masing dari sepuluh fungsi mungkin bergantung pada salah satu dari sembilan fungsi lainnya, dan 9 x 10 = 90. Anda mungkin tidak tahu fungsi mana yang memodifikasi variabel mana atau bagaimana data dilewatkan, jadi pembuat kode memiliki banyak hal yang perlu dikhawatirkan saat menyelesaikan masalah tertentu. Sebaliknya, jika Anda memiliki 30 kelas tetapi mereka diatur dengan cerdik, mereka dapat memiliki sesedikit 29 hubungan, misalnya jika mereka berlapis atau diatur dalam tumpukan.
Bagaimana ini memengaruhi throughput tim Anda? Nah, ada lebih sedikit ketergantungan, masalahnya jauh lebih mudah ditelusuri; coders tidak harus menyulap jutaan hal di kepala mereka setiap kali mereka membuat perubahan. Jadi meminimalkan ketergantungan dapat menjadi dorongan besar bagi kemampuan Anda untuk beralasan tentang masalah secara kompeten. Itulah sebabnya kami membagi berbagai hal ke dalam kelas atau modul, dan variabel ruang lingkup sekencang mungkin, dan menggunakan prinsip SOLID .
sumber
EntityTransformationServiceImpl
, Anda dipaksa untuk mempelajari bagaimana semuanya bekerja sebelum Anda bisa memperbaikinya - tetapi jika misalnya ada yang salah dengan format danFormatter
kelas yang menangani itu, Anda hanya perlu mempelajari cara kerja bagian itu . Plus membaca kode Anda sendiri setelah ~ 3 bulan seperti membaca kode orang asing.) Saya merasa sebagian besar dari kita secara tidak sadar memikirkan hal ini, tetapi itu mungkin karena pengalaman. Tidak 100% yakin tentang ini.Yah, pertama-tama, keterbacaan dan pemeliharaan sering di mata yang melihatnya.
Apa yang bisa Anda baca mungkin bukan untuk tetangga Anda.
Maintainability sering bermuara pada kemampuan menemukan (betapa mudahnya suatu perilaku atau konsep ditemukan dalam basis kode) dan kemampuan menemukan adalah hal subjektif lainnya.
DDD
Salah satu cara DDD membantu tim pengembang adalah dengan menyarankan cara spesifik (namun masih subyektif) dalam mengatur konsep dan perilaku kode Anda. Konvensi ini membuatnya lebih mudah untuk menemukan hal-hal, dan karenanya lebih mudah untuk mempertahankan aplikasi.
Pengaturan ini secara obyektif tidak mudah untuk dipertahankan. Hal ini, bagaimanapun, terukur lebih mudah untuk mempertahankan ketika semua orang mengerti mereka beroperasi dalam konteks DDD.
Kelas
Kelas membantu dengan kemampuan pemeliharaan, keterbacaan, kemampuan menemukan, dll ... karena mereka adalah konvensi terkenal .
Dalam pengaturan Berorientasi Objek, Kelas biasanya digunakan untuk mengelompokkan perilaku yang terkait erat dan untuk merangkum keadaan yang perlu dikendalikan dengan hati-hati.
Saya tahu itu terdengar sangat abstrak, tetapi Anda dapat memikirkannya seperti ini:
Dengan Kelas, Anda tidak perlu tahu bagaimana kode di dalamnya bekerja. Anda hanya perlu tahu untuk apa kelas itu bertanggung jawab.
Kelas memungkinkan Anda untuk mempertimbangkan aplikasi Anda dalam hal interaksi antara komponen yang didefinisikan dengan baik .
Ini mengurangi beban kognitif saat beralasan tentang cara kerja aplikasi Anda. Daripada harus mengingat apa yang dicapai 600 baris kode , Anda dapat berpikir tentang bagaimana 30 komponen berinteraksi.
Dan, mengingat 30 komponen itu mungkin menjangkau 3 lapisan aplikasi Anda, Anda mungkin hanya perlu mempertimbangkan sekitar 10 komponen sekaligus.
Tampaknya itu cukup mudah dikelola.
Ringkasan
Pada dasarnya, apa yang Anda lihat para pengembang senior lakukan adalah ini:
Mereka memecah aplikasi menjadi alasan yang mudah tentang kelas.
Mereka kemudian mengatur ini menjadi alasan yang mudah tentang lapisan.
Mereka melakukan ini karena mereka tahu bahwa, seiring dengan pertumbuhan aplikasi, semakin sulit untuk memikirkannya secara keseluruhan. Memecahnya menjadi Layers dan Kelas berarti bahwa mereka tidak perlu alasan tentang seluruh aplikasi. Mereka hanya perlu beralasan tentang sebagian kecil dari itu.
sumber
AddressServiceRepositoryProxyInjectorResolver
saat Anda bisa menyelesaikan masalah dengan lebih elegan? Pola desain demi pola desain hanya mengarah pada kompleksitas dan kata kerja yang tidak perlu.Pertama, sebuah catatan: bagian penting dari DDD bukanlah pola , tetapi keselarasan upaya pengembangan dengan bisnis. Greg Young mengatakan bahwa bab-bab dalam buku biru berada di urutan yang salah .
Tetapi untuk pertanyaan spesifik Anda: cenderung ada lebih banyak kelas daripada yang Anda harapkan, (a) karena upaya sedang dilakukan untuk membedakan perilaku domain dari pipa ledeng dan (b) karena upaya ekstra sedang dilakukan untuk memastikan bahwa konsep dalam model domain diekspresikan secara eksplisit.
Terus terang, jika Anda memiliki dua konsep yang berbeda dalam domain, maka mereka harus berbeda dalam model bahkan jika mereka berbagi hal yang sama dalam representasi memori .
Akibatnya, Anda sedang membangun bahasa khusus domain yang menjelaskan model Anda dalam bahasa bisnis, sehingga pakar domain harus dapat melihatnya dan menemukan kesalahan.
Selain itu, Anda melihat sedikit lebih banyak perhatian diberikan pada pemisahan masalah; dan gagasan mengisolasi konsumen dari beberapa kemampuan dari detail implementasi. Lihat DL Parnas . Batas-batas yang jelas memberdayakan Anda untuk mengubah atau memperpanjang implementasi tanpa efek berdesir di seluruh solusi Anda.
Motivasinya di sini: untuk aplikasi yang merupakan bagian dari kompetensi inti bisnis (artinya tempat di mana ia memperoleh keunggulan kompetitif), Anda ingin dapat dengan mudah dan murah mengganti perilaku domain dengan variasi yang lebih baik. Akibatnya, Anda memiliki bagian dari program yang ingin Anda kembangkan dengan cepat (bagaimana status berevolusi dari waktu ke waktu), dan bagian lain yang ingin Anda ubah perlahan-lahan (bagaimana status disimpan); lapisan tambahan abstraksi membantu menghindari secara tidak sengaja menyatukan satu dengan yang lainnya.
Dalam keadilan: beberapa di antaranya juga Kerusakan Otak Berorientasi Objek. Pola-pola yang awalnya dijelaskan oleh Evans didasarkan pada proyek-proyek Jawa yang ia ikuti 15+ tahun yang lalu; keadaan dan perilaku sangat erat dalam gaya itu, yang mengarah pada komplikasi yang Anda mungkin ingin hindari; lihat Persepsi dan Tindakan oleh Stuart Halloway, atau Inlining Code oleh John Carmack.
sumber
Ada banyak poin bagus di jawaban lain, tetapi saya pikir mereka kehilangan atau tidak menekankan kesalahan konseptual penting yang Anda buat:
Anda membandingkan upaya untuk memahami program yang lengkap.
Ini bukan tugas yang realistis dengan sebagian besar program. Bahkan program sederhana terdiri dari begitu banyak kode sehingga tidak mungkin untuk mengelola semua itu di kepala pada waktu tertentu. Satu-satunya kesempatan Anda adalah menemukan bagian dari sebuah program yang relevan dengan tugas yang ada (memperbaiki bug, mengimplementasikan fitur baru) dan bekerja dengannya.
Jika program Anda terdiri dari fungsi / metode / kelas yang sangat besar, ini hampir mustahil. Anda harus memahami ratusan baris kode hanya untuk memutuskan apakah potongan kode ini relevan dengan masalah Anda. Dengan perkiraan yang Anda berikan menjadi mudah untuk menghabiskan satu minggu hanya untuk menemukan potongan kode yang perlu Anda kerjakan.
Bandingkan dengan basis kode dengan fungsi / metode / kelas kecil bernama dan diorganisasikan ke dalam paket / namespaces yang membuatnya jelas di mana menemukan / meletakkan sepotong logika tertentu. Ketika dilakukan dengan benar dalam banyak kasus, Anda dapat melompat langsung ke tempat yang tepat untuk menyelesaikan masalah Anda, atau setidaknya ke tempat dari mana menjalankan debugger Anda akan membawa Anda ke tempat yang tepat dalam beberapa lompatan.
Saya telah bekerja di kedua jenis sistem. Perbedaannya dapat dengan mudah menjadi dua urutan besarnya dalam kinerja untuk tugas yang sebanding dan ukuran sistem yang sebanding.
Efeknya terhadap aktivitas lain:
sumber
Karena pengujian kode lebih sulit daripada menulis kode
Banyak jawaban telah memberikan alasan yang bagus dari sudut pandang pengembang - bahwa pemeliharaan dapat dikurangi, dengan biaya membuat kode lebih berat untuk ditulis.
Namun, ada aspek lain yang perlu dipertimbangkan - pengujian hanya dapat dipastikan sebagai kode asli Anda.
Jika Anda menulis semuanya dalam monolith, satu-satunya tes efektif yang dapat Anda tulis adalah "diberi input ini, apakah outputnya benar?". Ini berarti setiap bug yang ditemukan, dicakup dalam "suatu tempat di tumpukan kode raksasa".
Tentu saja, Anda kemudian dapat memiliki pengembang duduk dengan debugger dan menemukan di mana masalah dimulai dan bekerja pada perbaikan. Ini membutuhkan banyak sumber daya, dan penggunaan waktu pengembang yang buruk. Bayangkan bug minor yang pernah Anda miliki, menghasilkan pengembang yang perlu men-debug seluruh program lagi.
Solusinya: Banyak tes yang lebih kecil yang menunjukkan masing-masing potensi kegagalan spesifik.
Tes kecil ini (misalnya Tes Unit), memiliki keuntungan memeriksa area spesifik basis kode dan membantu menemukan kesalahan dalam lingkup terbatas. Ini tidak hanya mempercepat debugging ketika sebuah tes gagal, tetapi juga berarti bahwa jika semua tes kecil Anda gagal - Anda dapat lebih mudah menemukan kegagalan dalam tes Anda yang lebih besar (yaitu jika itu tidak dalam fungsi yang diuji spesifik, itu harus dalam interaksi diantara mereka).
Seperti yang seharusnya jelas, untuk membuat tes yang lebih kecil berarti basis kode Anda perlu dipecah menjadi potongan-potongan kecil yang dapat diuji. Cara melakukannya, pada basis kode komersial besar, sering menghasilkan kode yang mirip dengan apa yang Anda kerjakan.
Sama seperti catatan tambahan: Ini bukan untuk mengatakan bahwa orang tidak akan mengambil hal-hal "terlalu jauh". Tetapi ada alasan yang sah untuk memisahkan basis kode menjadi bagian yang lebih kecil / kurang terhubung - jika dilakukan dengan bijaksana.
sumber
Banyak (sebagian besar ...) dari kita benar-benar tidak membutuhkannya . Para ahli teori dan programmer yang sangat maju dan berpengalaman menulis buku tentang teori dan metodologi sebagai hasil dari banyak penelitian dan pengalaman mendalam mereka - itu tidak berarti bahwa semua yang mereka tulis dapat diterapkan untuk setiap programmer dalam praktik sehari-hari mereka.
Sebagai pengembang junior, ada baiknya membaca buku seperti yang Anda sebutkan untuk memperluas perspektif Anda, dan membuat Anda sadar akan masalah tertentu. Ini juga akan menyelamatkan Anda dari rasa malu dan bingung ketika kolega senior Anda menggunakan terminologi yang tidak Anda kenal. Jika Anda menemukan sesuatu yang sangat sulit dan tampaknya tidak masuk akal atau tampak bermanfaat, jangan bunuh diri karena itu - simpan saja di kepala Anda bahwa ada konsep atau pendekatan seperti itu.
Dalam perkembangan Anda sehari-hari, kecuali jika Anda seorang akademisi, tugas Anda adalah menemukan solusi yang bisa diterapkan dan dapat dikelola. Jika ide-ide yang Anda temukan di buku tidak membantu Anda mencapai tujuan itu, maka jangan khawatir tentang itu sekarang, selama pekerjaan Anda dianggap memuaskan.
Mungkin ada saatnya ketika Anda akan menemukan bahwa Anda dapat menggunakan sebagian dari apa yang Anda baca tetapi tidak terlalu "mengerti" pada awalnya, atau mungkin tidak.
sumber
AccountServiceFacadeInjectResolver
(contoh nyata saya baru saja menemukan dalam suatu sistem di tempat kerja) - jawabannya kemungkinan besar tidak.Karena pertanyaan Anda mencakup banyak alasan, dengan banyak asumsi, saya akan memilih subjek pertanyaan Anda:
Kita tidak. Tidak ada aturan yang berlaku umum yang mengatakan bahwa harus ada banyak kelas dalam pola desain.
Ada dua panduan utama untuk memutuskan di mana menempatkan kode, dan cara memotong tugas Anda menjadi unit kode yang berbeda:
Mengapa keduanya harus penting?
Kedua aspek ini adalah "driver" dasar untuk setiap pilihan "di mana harus meletakkan apa" dalam bahasa pemrograman apa pun, dan paradigma apa pun (tidak hanya OO). Tidak semua orang secara eksplisit menyadarinya, dan butuh waktu, seringkali bertahun-tahun, untuk mendapatkan perasaan yang benar-benar tertanam dan otomatis tentang bagaimana perangkat lunak ini memengaruhi.
Jelas, kedua konsep itu tidak memberi tahu Anda apa yang sebenarnya harus dilakukan . Beberapa orang keliru dalam hal terlalu banyak, yang lain terlalu sedikit. Beberapa bahasa (memandang Anda di sini, Jawa) cenderung memihak banyak kelas karena sifatnya yang sangat statis dan pedantik dari bahasa itu sendiri (ini bukan pernyataan nilai, tetapi itu adalah apa adanya). Ini menjadi sangat nyata ketika Anda membandingkannya dengan bahasa yang lebih dinamis dan lebih ekspresif, misalnya Ruby.
Aspek lain adalah bahwa beberapa orang berlangganan pendekatan tangkas dari hanya menulis kode yang diperlukan saat ini , dan untuk melakukan refactor berat nanti, bila perlu. Dalam gaya pengembangan ini, Anda tidak akan membuat
interface
ketika Anda hanya memiliki satu kelas pelaksana. Anda cukup mengimplementasikan kelas konkret. Jika, nanti, Anda membutuhkan kelas dua, Anda akan refactor.Beberapa orang tidak bekerja seperti itu. Mereka membuat antarmuka (atau, lebih umum, kelas dasar abstrak) untuk apa pun yang pernah dapat digunakan secara lebih umum; ini menyebabkan ledakan kelas dengan cepat.
Sekali lagi, ada argumen untuk dan melawan, dan tidak masalah yang saya, atau Anda, sukai. Dalam hidup Anda sebagai pengembang perangkat lunak, Anda akan menghadapi semua hal yang ekstrem, mulai dari metode spaghetti yang panjang, hingga desain kelas yang tercerahkan, cukup besar, hingga skema kelas yang sangat meledak-ledak yang terlalu banyak direkayasa. Ketika Anda semakin berpengalaman, Anda akan tumbuh lebih banyak menjadi peran "arsitektur", dan dapat mulai memengaruhi ini ke arah yang Anda inginkan. Anda akan menemukan tengah emas untuk diri sendiri, dan Anda masih akan menemukan bahwa banyak orang akan tidak setuju dengan Anda, apa pun yang Anda lakukan.
Oleh karena itu, menjaga pikiran terbuka adalah hal yang paling penting, di sini, dan itu akan menjadi saran utama saya untuk Anda, melihat bahwa Anda tampaknya sangat kesakitan tentang masalah ini, menilai dari sisa pertanyaan Anda ...
sumber
Coders berpengalaman telah belajar:
sumber
Sejauh ini jawabannya, semuanya baik, dimulai dengan asumsi yang masuk akal bahwa si penanya kehilangan sesuatu, yang juga diakui si penanya. Mungkin juga penanya pada dasarnya benar, dan ada baiknya membahas bagaimana situasi ini bisa terjadi.
Pengalaman dan praktik sangat kuat, dan jika para senior mendapatkan pengalaman mereka dalam proyek-proyek besar dan kompleks di mana satu-satunya cara untuk menjaga hal-hal di bawah kendali adalah dengan banyak hal
EntityTransformationServiceImpl
, maka mereka menjadi cepat dan nyaman dengan pola desain dan kepatuhan yang dekat dengan DDD. Mereka akan jauh lebih efektif menggunakan pendekatan yang ringan, bahkan untuk program kecil. Sebagai yang aneh, Anda harus beradaptasi dan itu akan menjadi pengalaman belajar yang hebat.Meskipun beradaptasi, Anda harus menganggapnya sebagai pelajaran dalam keseimbangan antara mempelajari satu pendekatan secara mendalam, sampai pada titik Anda dapat membuatnya bekerja di mana saja, dibandingkan tetap fleksibel dan mengetahui alat apa yang tersedia tanpa harus menjadi ahli dengan salah satu dari mereka. Ada keuntungan untuk keduanya dan keduanya dibutuhkan di dunia.
sumber
Membuat lebih banyak kelas dan fungsi sesuai penggunaan akan menjadi pendekatan yang bagus untuk menyelesaikan masalah dengan cara tertentu yang membantu di masa depan untuk menyelesaikan masalah apa pun.
Banyak kelas membantu mengidentifikasi sebagai pekerjaan dasar mereka dan setiap kelas dapat dipanggil kapan saja.
Namun jika Anda memiliki banyak kelas dan fungsi dari nama yang dapat diperoleh, mereka mudah dipanggil dan dikelola. Ini disebut kode bersih.
sumber