Saya telah menghabiskan banyak waktu membaca berbagai buku tentang "desain yang baik", "pola desain", dll. Saya penggemar berat pendekatan SOLID dan setiap kali saya perlu menulis sepotong kode sederhana, saya memikirkan masa depan. Jadi, jika menerapkan fitur baru atau perbaikan bug hanya perlu menambahkan tiga baris kode seperti ini:
if(xxx) {
doSomething();
}
Itu tidak berarti saya akan melakukannya dengan cara ini. Jika saya merasa potongan kode ini kemungkinan akan menjadi lebih besar dalam waktu terdekat, saya akan memikirkan untuk menambahkan abstraksi, memindahkan fungsi ini di tempat lain dan seterusnya. Tujuan yang saya kejar adalah menjaga kompleksitas rata-rata sama seperti sebelum perubahan saya.
Saya percaya, bahwa dari sudut pandang kode, ini adalah ide yang cukup bagus - kode saya tidak pernah cukup lama, dan cukup mudah untuk memahami makna untuk entitas yang berbeda, seperti kelas, metode, dan hubungan antara kelas dan objek.
Masalahnya adalah, butuh terlalu banyak waktu, dan saya sering merasa akan lebih baik jika saya hanya mengimplementasikan fitur "sebagaimana adanya". Ini hanya tentang "tiga baris kode" vs. "antarmuka baru + dua kelas untuk mengimplementasikan antarmuka itu".
Dari sudut pandang produk (ketika kita berbicara tentang hasilnya ), hal-hal yang saya lakukan tidak masuk akal. Saya tahu bahwa jika kita akan bekerja pada versi berikutnya, memiliki kode yang baik sangat bagus. Tetapi di sisi lain, waktu yang Anda habiskan untuk membuat kode Anda "baik" mungkin telah dihabiskan untuk mengimplementasikan beberapa fitur yang bermanfaat.
Saya sering merasa sangat tidak puas dengan hasil saya - kode bagus yang hanya bisa melakukan A lebih buruk daripada kode buruk yang bisa melakukan A, B, C, dan D.
Apakah pendekatan ini cenderung menghasilkan keuntungan bersih positif untuk proyek perangkat lunak, atau hanya membuang-buang waktu?
sumber
Jawaban:
Bagi saya ini baunya seperti generalisasi spekulatif . Tanpa mengetahui (atau setidaknya cukup yakin) bahwa klien Anda akan membutuhkan fitur B, C dan D, Anda hanya perlu membuat desain Anda terlalu rumit. Kode yang lebih kompleks lebih sulit untuk dipahami dan dipelihara dalam jangka panjang. Kompleksitas ekstra hanya dibenarkan oleh fitur tambahan yang bermanfaat . Tapi kami biasanya sangat buruk dalam memprediksi masa depan. Sebagian besar fitur yang kami pikir mungkin dibutuhkan di masa depan tidak akan pernah diminta dalam kehidupan nyata.
Begitu:
Kode yang baik yang hanya dapat melakukan A (tetapi melakukan itu satu hal dengan sederhana dan bersih) adalah LEBIH BAIK daripada kode yang buruk yang bisa melakukan A, B, C, D (beberapa di antaranya mungkin diperlukan di masa depan).
sumber
Waktu anekdot:
Saya memiliki dua pengembang yang bekerja untuk saya yang condong ke arah rekayasa berlebihan dengan cara ini.
Bagi salah satu dari mereka, ini pada dasarnya menghentikan produktivitasnya, terutama ketika memulai proyek baru. Terutama terutama jika proyek itu, pada dasarnya, cukup sederhana. Akhirnya perangkat lunak yang berfungsi sekarang adalah yang kita butuhkan. Ini menjadi sangat buruk sehingga saya harus membiarkannya pergi.
Pengembang lain yang cenderung over-engineering menebusnya dengan menjadi sangat produktif. Dengan demikian, terlepas dari semua kode asing, ia masih mengirim lebih cepat daripada kebanyakan. Namun, sekarang dia sudah pindah, saya sering merasa jengkel pada jumlah pekerjaan tambahan yang diperlukan untuk menambah fungsionalitas karena Anda harus memodifikasi lapisan abstraksi (seluruhnya tidak perlu) dll.
Jadi moralnya adalah, over-engineering akan menghabiskan waktu ekstra yang bisa lebih baik dihabiskan untuk melakukan hal-hal yang bermanfaat. Dan bukan hanya waktu Anda sendiri, tetapi juga mereka yang harus bekerja dengan kode Anda.
Jadi jangan.
Anda ingin kode Anda sesederhana mungkin (dan tidak lebih sederhana). Menangani 'maybes' tidak membuat segala sesuatunya menjadi lebih sederhana, jika Anda salah menebak Anda akan membuat kode lebih kompleks tanpa menunjukkan hasil nyata.
sumber
Prinsip-prinsip SOLID dan KISS / YAGNI bertentangan satu sama lain. Seseorang akan memberi tahu Anda bahwa jika doSomething () tidak dapat dibenarkan sebagai bagian integral dari pekerjaan yang dilakukan kelas, itu harus dalam kelas yang berbeda yang secara longgar digabungkan dan disuntikkan. Yang lain akan memberi tahu Anda bahwa jika ini adalah satu tempat doSomething digunakan, bahkan mengekstraksinya menjadi metode mungkin berlebihan.
Inilah yang membuat programmer yang baik layak mendapatkan emas. Struktur "tepat" adalah kasus per kasus, membutuhkan pengetahuan tentang basis kode saat ini, jalur masa depan program, dan kebutuhan bisnis di belakang program.
Saya suka mengikuti metodologi tiga langkah sederhana ini.
Pada dasarnya, ini adalah bagaimana Anda memadukan KISS dengan SOLID. Ketika Anda pertama kali menulis satu baris kode, yang Anda tahu itu hanya satu kali; itu hanya harus bekerja dan tidak ada yang peduli bagaimana, jadi jangan suka. Kali kedua Anda meletakkan kursor di baris kode itu, Anda telah menyangkal hipotesis asli Anda; dalam meninjau kembali kode ini, Anda kemungkinan memperluas atau menghubungkannya dari tempat lain. Sekarang, Anda harus membersihkannya sedikit; ekstrak metode untuk tidbits yang umum digunakan, kurangi atau hapus kode copy / paste, tambahkan beberapa komentar, dll. Kali ketiga Anda kembali ke kode ini, sekarang menjadi persimpangan utama untuk jalur eksekusi program Anda. Sekarang Anda harus memperlakukannya sebagai bagian inti dari program Anda, dan menerapkan aturan SOLID jika memungkinkan.
Contoh: Anda perlu menulis satu baris teks sederhana ke konsol. Pertama kali ini terjadi, Console.WriteLine () baik-baik saja. Kemudian Anda kembali ke kode ini setelah persyaratan baru juga menentukan penulisan baris yang sama ke log keluaran. Dalam contoh sederhana ini, mungkin tidak ada banyak kode "salin / tempel" berulang-ulang (atau mungkin ada), tetapi Anda masih dapat melakukan beberapa pembersihan dasar, mungkin mengekstrak satu atau dua metode untuk menghindari inlining operasi IO ke dalam logika bisnis yang lebih dalam . Kemudian, Anda kembali ketika klien menginginkan output teks yang sama ke pipa bernama untuk server pemantauan. Sekarang, rutinitas keluaran ini adalah masalah besar; Anda menyiarkan teks yang sama tiga cara. Ini adalah contoh buku teks dari suatu algoritma yang akan mendapat manfaat dari pola Komposit; mengembangkan antarmuka IWriter sederhana dengan metode Write (), mengimplementasikan antarmuka itu untuk membuat kelas ConsoleWriter, FileWriter, dan NamedPipeWriter, dan sekali lagi untuk membuat kelas komposit "MultiWriter", lalu paparkan ketergantungan IWriter pada kelas Anda, atur komposit MultiWriter dengan tiga penulis aktual, dan sambungkan. Sekarang, ini cukup SOLID; dari titik ini seterusnya, setiap kali persyaratan menentukan bahwa output harus pergi ke mana pun yang baru, Anda cukup membuat IWriter baru dan tancapkan ke MultiWriter, tidak perlu menyentuh kode kerja yang ada.
sumber
1) Memiliki kode yang hanya melakukan apa yang seharusnya dilakukan.
2) Jika Anda merencanakan kode Anda untuk melakukan A, B, C dan D, pelanggan pada akhirnya akan meminta Anda untuk E.
Kode Anda harus melakukan apa yang seharusnya dilakukan, Anda tidak boleh berpikir sekarang tentang implementasi di masa depan, karena Anda tidak akan pernah berakhir mengubah kode Anda terus-menerus, dan yang lebih penting Anda akan mendesain ulang kode Anda secara berlebihan. Anda harus memperbaiki kode Anda segera setelah Anda merasa memerlukannya karena fitur yang ada, tidak berjuang untuk menyiapkannya untuk sesuatu yang belum akan dilakukan, kecuali tentu saja Anda merencanakannya sebagai bagian dari arsitektur proyek.
Saya sarankan Anda membaca buku yang bagus: Programmer Pragmatis . Itu akan membuka pikiran Anda dan mengajari Anda apa yang harus Anda lakukan dan apa yang tidak boleh Anda lakukan.
Juga Kode Lengkap adalah sumber daya yang besar penuh informasi berguna yang setiap pengembang (tidak hanya programmer) harus membaca.
sumber
Mungkin di sini masalahnya.
Pada tahap awal, Anda tidak tahu apa yang akan menjadi produk akhir. Atau jika Anda memilikinya, itu salah. Tentunya. Seperti bocah lelaki berusia 14 tahun yang bertanya beberapa hari yang lalu tentang Programmers.SE apakah dia harus, untuk karir masa depannya, memilih antara aplikasi web dan saya tidak ingat apa lagi: cukup jelas bahwa dalam beberapa tahun, hal-hal dia suka akan berubah, dia akan tertarik dengan domain lain, dll.
Jika, untuk menulis tiga baris kode, Anda membuat antarmuka baru dan dua kelas, Anda over-engineering. Anda akan mendapatkan kode yang sulit dipertahankan dan sulit dibaca , hanya karena untuk setiap baris kode yang bermanfaat, Anda memiliki dua baris kode yang tidak Anda perlukan. Tidak termasuk dokumentasi XML, tes unit, dll.
Pikirkan tentang hal ini: jika saya ingin tahu bagaimana suatu fitur diimplementasikan dalam kode Anda, apakah akan lebih mudah bagi saya untuk membaca dua puluh baris kode, atau akan lebih cepat dan lebih mudah harus membuka puluhan kelas semi-kosong dan antarmuka untuk mencari tahu mana yang menggunakan yang mana, bagaimana mereka terkait, dll?
Ingat: basis kode yang lebih besar berarti lebih banyak pemeliharaan. Jangan menulis lebih banyak kode ketika Anda bisa menghindarinya.
Pendekatan Anda juga berbahaya di sisi lain:
Jika Anda perlu menghapus fitur, bukankah lebih mudah untuk mencari tahu di mana metode dua puluh baris tertentu digunakan, daripada membuang waktu Anda untuk memahami dependensi antara puluhan kelas?
Saat men-debug, bukankah lebih mudah untuk memiliki jejak tumpukan kecil, atau apakah Anda lebih suka membaca lusinan baris untuk mencari tahu apa yang disebut dengan apa?
Sebagai kesimpulan, tampaknya mirip dengan optimasi prematur . Anda mencoba menyelesaikan masalah tanpa memastikan apakah ada masalah di tempat pertama, dan di mana itu. Saat bekerja pada versi 1 produk Anda, implementasikan fitur yang perlu Anda terapkan sekarang; jangan berpikir tentang sesuatu yang Anda harapkan untuk diimplementasikan dalam dua tahun dalam versi 14.
sumber
Menulis banyak kode yang akan (mungkin) tidak akan pernah digunakan adalah cara yang sangat baik untuk dikeluarkan dengan P45 . Anda tidak memiliki bola kristal dan tidak tahu ke arah akhir bahwa pengembangan akan memakan waktu sehingga menghabiskan fungsi-fungsi ini hanya membutuhkan biaya tanpa pengembalian.
sumber
Mencoba untuk memprediksi apa yang mungkin diperlukan dari kode di masa depan sering berakhir dengan over-engineering yang tidak perlu (kebiasaan yang saat ini saya coba goyang). Saya akan mengatakan hanya melakukan tiga baris. Ketika kebutuhan muncul (dan bukan sebelumnya), refactor. Dengan begitu kode Anda selalu melakukan apa yang dibutuhkan tanpa menjadi rumit dan mengembangkan struktur yang baik secara alami melalui refactoring.
sumber
Saya sering mengatakan bahwa pengkodean seperti sisi terang / sisi gelap the Force - sisi "terang" membutuhkan lebih banyak usaha tetapi menghasilkan hasil yang lebih besar. Sisi "gelap" cepat dan mudah dan memberi manfaat lebih besar segera tetapi merusak Anda. Begitu Anda mulai menyusuri jalan yang gelap, selamanya akan mendominasi takdir Anda.
Saya mengalami ini sepanjang waktu, di setiap pekerjaan yang pernah saya miliki, itu seperti kutukan yang tidak bisa saya hindari. Budaya perusahaan selalu merupakan jalan dari sisi gelap, dan peretasan cepat / perbaikan untuk mendorong keluar fitur baru, dan permohonan serta seruan saya untuk refactoring dan penulisan kode benar-benar jatuh di telinga tuli, jika itu tidak mengarah pada penghentian saya untuk " mencoba mengubah keadaan "(jangan bercanda, itu terjadi beberapa kali, karena saya ingin memperkenalkan pola desain dan menjauh dari peretasan cepat).
Kebenaran yang menyedihkan adalah bahwa seringkali sisi jalan yang bodoh / gelap adalah cara Anda harus menginjak, Anda hanya perlu memastikan untuk melangkah dengan ringan. Saya perlahan dan sedih menyadari bahwa programmer yang mengerti cara yang benar untuk menulis perangkat lunak, yaitu mengikuti SOLID, menggunakan pola desain, mematuhi SoC, dll. Jauh lebih jarang daripada peretas yang tidak mengerti yang akan menulis
if
pernyataan untuk memperbaiki bug, dan ketika lebih banyak bug muncul tambahkan saja ke pernyataan itu daripada pernah berpikir "Mungkin ada cara yang lebih baik untuk melakukan ini" dan refactoring kode agar dapat diperluas dengan benar.sumber
if
jauh lebih mudah untuk mempertahankan daripadaIAbstractBugFixer
dariIAbstractBugFixerFactory
. Kapan, dan, JIKA, Anda bisa menambahkan satu detikif
, maka saatnya untuk refactor. Pola desain dan SOLID sangat penting selama fase arsitektur, tetapi tidak ketika produk sudah berjalan dan ditulis dalam satu gaya yang disetujui oleh semua anggota tim.Menyadari apa yang mungkin terjadi (masa depan) TIDAK PERNAH buruk. Memikirkan apa yang mungkin menjadi cara yang lebih baik untuk melakukan sesuatu adalah bagian dari apa yang membuat Anda pandai dalam pekerjaan Anda. Bagian yang lebih sulit adalah menentukan apakah waktu yang dihabiskan: rasio hasil dibenarkan. Kita semua pernah melihat situasi di mana orang melakukan "mudah jika" untuk menghentikan pendarahan segera (dan / atau berteriak), dan ketika itu bertambah, Anda mendapatkan kode yang membingungkan. Banyak dari kita juga pernah mengalami abstraksi berlebihan yang merupakan misteri begitu pembuat kode asli bergerak, yang juga menghasilkan kode membingungkan.
Saya akan melihat situasi Anda dan mengajukan pertanyaan-pertanyaan ini:
Apakah kode ini penting untuk misi, dan apakah akan jauh lebih stabil jika saya membuat kode ulang? Dalam bedah berbicara, apakah ini menyelamatkan nyawa refactoring, atau hanya elektif dan kosmetik?
Apakah saya mempertimbangkan kode refactoring yang akan kami ganti dalam 6 bulan?
Apakah saya bersedia mengambil waktu sebanyak mungkin untuk mendokumentasikan desain dan alasan saya untuk pengembang di masa depan sebagaimana saya habiskan untuk melakukan refactoring?
Mengenai desain elegan saya untuk menambahkan fitur masa depan, apakah ini dalam kode yang pengguna minta perubahan setiap minggu, atau ini pertama kali saya menyentuhnya tahun ini?
Ada saat-saat YAGNI dan KISS menang, tetapi ada hari-hari di mana perubahan mendasar akan membuat Anda turun dari spiral ke keburukan. Selama Anda realistis dalam penilaian Anda tidak hanya tentang apa yang Anda inginkan, tetapi apa yang harus dilakukan orang lain untuk mempertahankan pekerjaan Anda, Anda akan lebih dapat menentukan situasi mana yang mana. Oh, dan jangan lupa menuliskan apa yang Anda lakukan, dan mengapa. Ini akan menyelamatkan mereka yang mengikuti Anda, tetapi juga diri Anda sendiri ketika Anda harus kembali lagi nanti.
sumber
Dalam edisi kedua 'Bahasa pemrograman C ++ Stroustrups', (saya tidak memiliki halaman yang tersedia) saya baca
dan saya mengikuti saran dengan baik. Tentu saja ada pengorbanan, dan Anda harus menemukan keseimbangan, tetapi fragmen pendek lebih baik diuji daripada kekacauan spaghetti besar.
Saya sering mengalami, bahwa ketika membedakan dari satu kasus ke dua kasus, jika Anda menganggap 2 sebagai kasus-n, Anda membuka pintu ke banyak kemungkinan baru, yang mungkin tidak Anda pikirkan.
Tetapi kemudian Anda harus mengajukan pertanyaan YAGNI: Apakah itu sepadan? Apakah ini benar-benar bermanfaat? Menjadi berpengalaman berarti, bahwa Anda akan jarang salah, dan sebagai pemula lebih sering salah.
Anda harus cukup kritis untuk mengenali suatu pola, dan mendeteksi, apakah kode Anda sulit untuk dipelihara, karena terlalu banyak abstraksi, atau sulit untuk dipelihara, karena semuanya diselesaikan di tempat.
Solusinya bukan ini atau itu, tetapi untuk memikirkannya. :)
sumber
"kode bagus yang hanya bisa melakukan A lebih buruk daripada kode buruk yang bisa melakukan A, B, C, dan D."
Ini mungkin masuk akal dalam pengembangan produk; Tetapi sebagian besar profesional TI bekerja di 'proyek' daripada pengembangan produk.
Dalam 'proyek TI', jika Anda memprogram komponen yang baik, itu akan berfungsi dengan lancar selama masa proyek - yang mungkin tidak berjalan lebih dari 5 atau 10 tahun pada saat itu, skenario bisnis mungkin sudah usang dan proyek baru / ERP produk mungkin telah menggantinya. Selama masa hidup 5/10 tahun ini, kecuali jika htere cacat dalam kode Anda, tidak ada yang akan melihat keberadaannya dan manfaat dari pemikiran terbaik Anda tidak diperhatikan! (Kecuali jika Anda pandai bertaruh terompet Anda sendiri!)
Tidak banyak yang mendapatkan kesempatan untuk memprogram 'Windows Ctl + Alt + Del' dan beberapa yang mendapatkan kesempatan itu mungkin tidak menyadari potensi masa depan dari kode mereka!
sumber
Banyak buku tentang pengembangan lean dan / atau gesit akan membantu memperkuat pendekatan ini: lakukan apa yang perlu saat ini. Jika Anda tahu Anda sedang membangun kerangka kerja, maka tambahkan abstraksi. Kalau tidak, jangan tambahkan kompleksitas sampai Anda membutuhkannya. Saya merekomendasikan Pengembangan Perangkat Lunak Lean , yang akan memperkenalkan banyak konsep lain yang dapat membuat perbedaan substantif dalam produktivitas.
sumber
Sangat lucu bagaimana orang berbicara tentang cara yang benar / salah dalam melakukan sesuatu. Pada saat yang sama tugas pemrograman masih sangat sulit, dan tidak memberikan solusi yang baik untuk menulis sistem yang rumit dan besar.
Bisa jadi suatu hari nanti kita, programmer, akhirnya akan mengetahui cara menulis perangkat lunak yang kompleks. Sampai saat itu saya sarankan Anda selalu mulai dengan implementasi prototipe "bodoh" terlebih dahulu, dan kemudian habiskan cukup waktu untuk refactoring sehingga perguruan tinggi Anda dapat mengikuti kode Anda.
sumber
Setelah melihat desain umum yang terlalu dini yang tidak sesuai dengan persyaratan aktual yang datang kemudian, saya membuat aturan untuk saya:
Untuk persyaratan hipotetis hanya menulis kode hipotetis.
Yaitu: Anda disarankan untuk memikirkan perubahan yang mungkin terjadi kemudian. Tetapi hanya gunakan wawasan ini untuk memilih desain untuk kode yang dapat dengan mudah diubah dan dire-reforasi jika persyaratan itu benar-benar muncul. Anda bahkan dapat menulis beberapa kode di kepala Anda (kode hipotetis) yang ingin Anda tulis dalam kasus itu, tetapi jangan menulis kode yang sebenarnya!
sumber
Saya pikir pola pikir yang akan membantu Anda adalah untuk selalu mengupayakan solusi konkret untuk memecahkan masalah alih-alih solusi abstrak. Abstraksi hanya boleh ditambahkan ketika mereka benar-benar membantu menyederhanakan basis kode (ketika misalnya mereka memungkinkan Anda untuk membuat basis kode lebih kecil).
Banyak programmer menemukan bahwa abstraksi hampir muncul dengan sendirinya, ketika mereka mengeringkan kode mereka. Pola Desain dan Praktik Terbaik membantu Anda menemukan peluang untuk melakukan hal itu, tetapi tidak dengan sendirinya tujuan yang layak dikejar.
sumber
Saya pikir over-engineering sering kali muncul dari rasa tidak aman tentang penulisan kode. Semua prinsip dan pola abstrak harus diperlakukan sebagai alat untuk membantu Anda. Yang sering terjadi adalah bahwa mereka diperlakukan sebagai standar, orang harus menyesuaikan diri.
Saya percaya bahwa seorang programmer selalu dalam posisi yang lebih baik untuk memutuskan bagaimana abstrak daripada aksioma.
Sisanya sudah dikatakan oleh KeithS
sumber
Tanyakan pada diri sendiri apa kelebihan dari desain yang baik adalah:
Sekarang, tanyakan pada diri Anda apakah menambahkan semua lapisan abstraksi benar-benar menambah salah satu poin yang disebutkan di atas. Jika tidak, Anda melakukannya SALAH .
Jika Anda dapat menambahkan fitur baru dengan menambahkan 3 baris kode seperti ini:
Kalau begitu tolong, tolong lakukan. Ini menandakan desain Anda sebelumnya bagus dan mudah diadaptasi. Hanya setelah kelas Anda mulai tumbuh melampaui batas tertentu, gunakan refactoring untuk membagi fungsi dan mungkin mengekstrak kelas baru.
Aturan praktis saya adalah bahwa fitur-fitur baru harus diimplementasikan seminimal mungkin, hanya ketika ada sesuatu yang besar untuk dipahami di muka (katakanlah, membutuhkan lebih dari 1 hari atau setengah hari untuk mengimplementasikan) Anda dapat menambahkan desain global yang kasar. Dari sana, hanya tambahkan lapisan abstraksi ketika ukuran kode bertambah. Anda kemudian refactor sesudahnya! Setelah beberapa saat, itu bahkan akan menjadi alami bagi Anda ketika Anda perlu merancang sedikit lebih banyak, atau pergi untuk jalan cepat. Indikasi lain beberapa kode dapat menggunakan pembersihan adalah ketika Anda menggunakannya kembali. Setiap kali Anda menambahkan fitur baru, atau memanggil kode lama di tempat baru, ini saat yang tepat untuk melihat kode lama dan melihat apakah Anda dapat memperbaikinya sedikit sebelum menambahkannya lagi. Dengan cara ini, kode panas perlahan akan menjadi lebih bersih, sementara bagian yang tidak menarik perlahan membusuk dan tidak mengambil waktu berharga Anda.
Jika Anda bekerja seperti ini, Anda tidak akan pernah mendesain sesuatu secara berlebihan. Mungkin diperlukan beberapa disiplin untuk kembali ke kode lama ketika Anda ingin menambahkan sesuatu yang baru, atau meninggalkan kode baru sedikit lebih jelek daripada yang Anda suka, tetapi Anda akan bekerja menuju sesuatu yang produktif daripada direkayasa secara berlebihan.
sumber