Bagaimana saya mencegah duplikasi kode yang tidak diketahui?


33

Saya bekerja pada basis kode yang agak besar. Ratusan kelas, berton-ton file berbeda, banyak fungsionalitas, membutuhkan lebih dari 15 menit untuk menarik salinan baru, dll.

Masalah besar dengan basis kode yang besar adalah ia memiliki beberapa metode utilitas dan melakukan hal yang sama, atau memiliki kode yang tidak menggunakan metode utilitas ini ketika bisa. Dan juga metode utilitas tidak hanya semua dalam satu kelas (karena itu akan menjadi berantakan campur aduk besar).

Saya agak baru dalam basis kode, tetapi pemimpin tim yang telah mengerjakannya selama bertahun-tahun tampaknya memiliki masalah yang sama. Ini mengarah ke banyak kode dan duplikasi kerja, dan dengan demikian, ketika sesuatu rusak, biasanya rusak dalam 4 salinan pada dasarnya kode yang sama

Bagaimana kita bisa mengekang pola ini? Seperti kebanyakan proyek besar, tidak semua kode didokumentasikan (meskipun ada beberapa) dan tidak semua kode ... baik, bersih. Tetapi pada dasarnya, akan sangat menyenangkan jika kita dapat bekerja untuk meningkatkan kualitas dalam hal ini sehingga di masa depan kita memiliki lebih sedikit duplikasi kode, dan hal-hal seperti fungsi utilitas lebih mudah ditemukan.

Juga, fungsi utilitas biasanya baik di beberapa kelas pembantu statis, di beberapa kelas pembantu non-statis yang bekerja pada objek tunggal, atau merupakan metode statis pada kelas yang terutama "membantu" dengan.

Saya punya satu percobaan dalam menambahkan fungsi utilitas sebagai metode Extension (saya tidak perlu internal kelas, dan itu pasti hanya diperlukan dalam skenario yang sangat spesifik). Ini memiliki efek mencegah mengacaukan kelas utama dan semacamnya, tetapi itu tidak benar-benar dapat ditemukan kecuali Anda sudah mengetahuinya


Jawaban:


30

Jawaban sederhananya adalah Anda benar-benar tidak dapat mencegah duplikasi kode. Namun Anda dapat "memperbaikinya" melalui proses penambahan berulang yang sulit dan berkesinambungan yang bermuara menjadi dua langkah:

Langkah 1. Mulai menulis tes pada kode lawas (lebih disukai menggunakan kerangka pengujian)

Langkah 2. Tulis ulang / refactor kode yang digandakan menggunakan apa yang telah Anda pelajari dari tes

Anda dapat menggunakan alat analisis statis untuk mendeteksi kode duplikat dan untuk C # ada banyak alat yang dapat melakukan ini untuk Anda:

Alat seperti ini akan membantu Anda menemukan poin dalam kode yang melakukan hal serupa. Lanjutkan menulis tes untuk menentukan apakah itu benar-benar dilakukan; gunakan tes yang sama untuk membuat kode duplikat lebih mudah digunakan. "Refactoring" ini dapat dilakukan dengan berbagai cara dan Anda dapat menggunakan daftar ini untuk menentukan yang benar:

Selain itu ada juga buku lengkap tentang topik ini oleh Michael C. Feathers, Bekerja Efektif dengan Legacy Code . Secara mendalam strategi yang berbeda dapat Anda ambil untuk mengubah kode menjadi lebih baik. Dia memiliki "algoritma perubahan kode warisan" yang tidak jauh dari proses dua langkah di atas:

  1. Identifikasi poin perubahan
  2. Temukan poin tes
  3. Hentikan ketergantungan
  4. Tulis tes
  5. Lakukan perubahan dan refactor

Buku ini adalah bacaan yang baik jika Anda berurusan dengan pengembangan brown-field, yaitu kode lawas yang perlu diubah.

Pada kasus ini

Dalam kasus OP, saya dapat membayangkan kode yang tidak dapat diuji disebabkan oleh pot madu untuk "metode dan trik utilitas" yang mengambil beberapa bentuk:

  • metode statis
  • penggunaan sumber daya statis
  • kelas tunggal
  • nilai sihir

Perhatikan bahwa tidak ada yang salah dengan ini, tetapi di sisi lain mereka biasanya sulit dipertahankan dan diubah. Metode ekstensi dalam. NET adalah metode statis, tetapi juga relatif mudah untuk diuji.

Sebelum Anda melanjutkan dengan refactorings, bicarakan dengan tim Anda tentang hal itu. Mereka harus disimpan pada halaman yang sama dengan Anda sebelum Anda melanjutkan dengan apa pun. Ini karena jika Anda refactoring sesuatu maka kemungkinan besar Anda akan menyebabkan konflik penggabungan. Jadi sebelum mengerjakan ulang sesuatu, selidiki, beri tahu tim Anda untuk mengerjakan poin kode tersebut dengan hati-hati sampai Anda selesai.

Karena OP baru mengenal kode tersebut, ada beberapa hal lain yang harus dilakukan sebelum Anda harus melakukan apa pun:

  • Luangkan waktu untuk belajar dari basis kode, yaitu pecahkan "segalanya", uji "segalanya", kembalikan.
  • Minta seseorang dari tim untuk meninjau kode Anda sebelum melakukan. ;-)

Semoga berhasil!


Kami sebenarnya memiliki cukup banyak unit dan pengujian integrasi. Bukan cakupan 100%, tetapi beberapa hal yang kami lakukan hampir tidak mungkin dilakukan unit test tanpa perubahan radikal pada basis kode kami. Saya tidak pernah mempertimbangkan menggunakan analisis statis untuk menemukan duplikasi. Saya harus mencobanya nanti.
Earlz

@ Earlz: Analisis kode statis mengagumkan! ;-) Juga, setiap kali Anda perlu melakukan perubahan, pikirkan solusi untuk membuat perubahan lebih mudah (periksa refactor untuk katalog pola untuk ini)
Spoike

+1 Saya mengerti jika seseorang memberi hadiah pada Q ini untuk memberi penghargaan kepada pengguna ini sebagai "ekstra membantu". Katalog Refactor to Patterns adalah emas, hal-hal seperti ini dalam mode GuidanceExplorer.codeplex.com adalah alat bantu pemrograman yang hebat.
Jeremy Thompson

2

Kita juga bisa mencoba melihat masalahnya dari sudut lain. Alih-alih berpikir bahwa masalahnya adalah duplikasi kode, kami dapat mempertimbangkan jika masalahnya berasal dari kurangnya kebijakan untuk penggunaan kembali kode.

Baru-baru ini saya membaca buku Rekayasa Perangkat Lunak dengan Komponen yang Dapat Digunakan Kembali dan memang memiliki serangkaian ide yang sangat menarik tentang cara menumbuhkan penggunaan kembali kode di tingkat organisasi.

Penulis buku ini, Johannes Sametinger, menjelaskan sekumpulan hambatan untuk penggunaan kembali kode, beberapa secara konsep beberapa teknis. Contohnya:

Konseptual dan Teknis

  • Kesulitan menemukan perangkat lunak yang dapat digunakan kembali : perangkat lunak tidak dapat digunakan kembali kecuali dapat ditemukan. Penggunaan kembali tidak mungkin terjadi ketika repositori tidak memiliki informasi yang cukup tentang komponen atau ketika komponen diklasifikasikan dengan buruk.
  • Nonreusability dari perangkat lunak yang ditemukan : akses mudah ke perangkat lunak yang ada tidak selalu meningkatkan penggunaan kembali perangkat lunak. Secara tidak sengaja, perangkat lunak jarang ditulis sedemikian rupa sehingga orang lain dapat menggunakannya kembali. Memodifikasi dan mengadaptasi perangkat lunak orang lain bisa menjadi lebih mahal daripada memprogram fungsionalitas yang diperlukan dari awal.
  • Komponen lawas yang tidak cocok untuk digunakan kembali : Penggunaan kembali komponen sulit atau tidak mungkin kecuali komponen tersebut telah dirancang dan dikembangkan untuk digunakan kembali. Cukup mengumpulkan komponen yang ada dari berbagai sistem perangkat lunak lama dan mencoba menggunakannya kembali untuk pengembangan baru tidak cukup untuk digunakan kembali secara sistematis. Rekayasa ulang dapat membantu dalam mengekstraksi komponen yang dapat digunakan kembali, namun upaya ini mungkin cukup besar.
  • Teknologi berorientasi objek : Dipercaya secara luas bahwa teknologi berorientasi objek memiliki dampak positif pada penggunaan kembali perangkat lunak. Sayangnya dan salah, banyak juga yang percaya penggunaan kembali tergantung pada teknologi ini atau bahwa mengadopsi teknologi berorientasi objek cukup untuk penggunaan kembali perangkat lunak.
  • Modifikasi : komponen tidak akan selalu persis seperti yang kita inginkan. Jika diperlukan modifikasi, kita harus dapat menentukan efeknya pada komponen dan hasil verifikasi sebelumnya.
  • Penggunaan kembali sampah : Menyertifikasi komponen yang dapat digunakan kembali untuk tingkat kualitas tertentu membantu meminimalkan kemungkinan cacat. Kontrol kualitas yang buruk adalah salah satu hambatan utama untuk digunakan kembali. Kami membutuhkan beberapa cara untuk menilai apakah fungsi yang diperlukan cocok dengan fungsi yang disediakan oleh komponen.

Kesulitan teknis dasar lainnya termasuk

  • Menyetujui apa yang merupakan komponen yang dapat digunakan kembali.
  • Memahami apa yang dilakukan komponen dan bagaimana menggunakannya.
  • Memahami bagaimana antarmuka komponen yang dapat digunakan kembali ke seluruh desain.
  • Merancang komponen yang dapat digunakan kembali sehingga mudah diadaptasi dan dimodifikasi dengan cara yang terkontrol.
  • Mengatur repositori sehingga programmer dapat menemukan dan menggunakan apa yang mereka butuhkan.

Menurut penulis, tingkat reusabilitas yang berbeda terjadi tergantung pada kematangan organisasi.

  • Penggunaan kembali ad-hoc di antara kelompok aplikasi : jika tidak ada komitmen eksplisit untuk menggunakan kembali, maka penggunaan kembali dapat terjadi secara informal dan serampangan. Sebagian besar penggunaan kembali, jika ada, akan terjadi dalam proyek. Ini juga mengarah pada pemulung kode dan berakhir pada duplikasi kode.
  • Penggunaan kembali berdasarkan repositori di antara kelompok aplikasi : situasi sedikit membaik ketika repositori komponen digunakan dan dapat diakses oleh berbagai kelompok aplikasi. Namun, tidak ada mekanisme eksplisit untuk menempatkan komponen ke dalam repositori dan tidak ada yang bertanggung jawab atas kualitas komponen dalam repositori. Ini dapat menyebabkan banyak masalah dan menghambat penggunaan kembali perangkat lunak.
  • Penggunaan kembali terpusat dengan grup komponen: Dalam skenario ini, grup komponen secara eksplisit bertanggung jawab untuk repositori. Grup menentukan komponen mana yang akan disimpan dalam repositori dan memastikan bahwa kualitas komponen-komponen ini dan ketersediaan dokumentasi yang diperlukan, dan membantu mengambil komponen yang sesuai dalam skenario penggunaan kembali tertentu. Grup aplikasi dipisahkan dari grup komponen, yang bertindak sebagai semacam subkontraktor untuk setiap grup aplikasi. Tujuan kelompok komponen adalah untuk meminimalkan redundansi. Dalam beberapa model, anggota kelompok ini juga dapat mengerjakan proyek tertentu. Selama start-up proyek, pengetahuan mereka sangat berharga untuk mendorong penggunaan kembali dan berkat keterlibatan mereka dalam proyek tertentu, mereka dapat mengidentifikasi kandidat yang mungkin untuk dimasukkan dalam repositori.
  • Penggunaan Kembali Berbasis Domain : Spesialisasi kelompok komponen berjumlah pada penggunaan kembali berbasis domain. Setiap grup domain bertanggung jawab atas komponen dalam domainnya, misalnya komponen jaringan, komponen antarmuka pengguna, komponen basis data.

Jadi, mungkin, di samping semua saran yang diberikan dalam jawaban lain, Anda dapat bekerja pada merancang program reusability, melibatkan manajemen, membentuk grup komponen yang bertanggung jawab untuk mengidentifikasi komponen yang dapat digunakan kembali dengan melakukan analisis domain dan menentukan repositori komponen yang dapat digunakan kembali yang dapat dengan mudah dikembangkan oleh pengembang lain. permintaan dan cari solusi matang untuk masalah mereka.


1

Ada 2 solusi yang mungkin:

Pencegahan - Cobalah untuk memiliki dokumentasi sebaik mungkin. Jadikan setiap fungsi didokumentasikan dengan baik dan mudah dicari melalui seluruh dokumentasi. Juga, saat menulis kode, jelaskan ke mana kode harus pergi, jadi jelas ke mana harus mencari. Membatasi jumlah kode "utilitas" adalah salah satu poin utama dari ini. Setiap kali saya mendengar "mari membuat kelas utilitas", rambut saya naik dan darah saya membeku, karena itu jelas merupakan masalah. Selalu punya cara cepat dan mudah untuk meminta orang mengetahui basis kode setiap kali beberapa fitur sudah ada.

Solusi - Jika pencegahan gagal, Anda harus dapat dengan cepat dan efisien menyelesaikan potongan kode yang bermasalah. Proses pengembangan Anda harus memungkinkan untuk memperbaiki kode duplikat dengan cepat. Pengujian unit sangat cocok untuk ini, karena Anda dapat memodifikasi kode secara efisien tanpa takut merusaknya. Jadi jika Anda menemukan 2 keping kode yang serupa, mengabstraksikannya menjadi suatu fungsi atau kelas harus mudah dengan sedikit refactoring.

Saya pribadi tidak berpikir pencegahan itu mungkin. Semakin Anda mencoba, semakin bermasalah untuk menemukan fitur yang sudah ada.


0

Saya tidak berpikir masalah semacam ini memiliki solusi umum. Kode duplikat tidak akan dibuat jika pengembang memiliki cukup kesediaan untuk mencari kode yang ada. Pengembang juga dapat memperbaiki masalah di tempat jika mereka mau.

Jika bahasanya C / C ++ penggabungan duplikasi akan lebih mudah karena fleksibilitas menghubungkan (orang dapat memanggil externfungsi apa pun tanpa informasi sebelumnya). Untuk Java atau .NET, Anda mungkin perlu membuat kelas pembantu dan / atau komponen utilitas.

Saya biasanya mulai menghapus duplikasi kode yang ada hanya jika kesalahan utama muncul dari bagian yang digandakan.


0

Ini adalah masalah khas dari proyek yang lebih besar yang telah ditangani oleh banyak programmer, yang telah memberikan kontribusi di bawah banyak tekanan teman sebaya. Sangat menggoda untuk membuat salinan kelas dan menyesuaikannya dengan kelas tertentu. Namun, ketika masalah ditemukan di kelas asal, itu juga harus dipecahkan dalam dekritnya yang sering dilupakan.

Ada solusi untuk ini dan itu disebut Generics yang telah diperkenalkan di Java 6. Ini adalah setara dengan C ++ yang disebut Template. Kode yang kelas pastinya belum diketahui dalam Kelas Generik. Silakan periksa Java Generics dan Anda akan menemukan berton-ton dokumentasi untuknya.

Pendekatan yang baik adalah menulis ulang kode yang tampaknya akan disalin / ditempel di banyak tempat dengan menulis ulang yang pertama yang perlu Anda perbaiki yaitu karena bug tertentu. Tulis ulang untuk menggunakan Generics dan juga menulis kode pengujian yang sangat ketat.

Pastikan bahwa setiap metode dari kelas Generik dipanggil. Anda juga dapat memperkenalkan alat cakupan kode: kode generik harus sepenuhnya mencakup kode karena akan digunakan di beberapa tempat.

Juga menulis kode pengujian yaitu menggunakan JUnit atau serupa untuk kelas yang ditunjuk pertama yang akan digunakan bersama dengan potongan kode Generik.

Mulai gunakan kode Generik untuk versi yang disalin kedua (sebagian besar kali) ketika semua kode sebelumnya bekerja dan sepenuhnya diuji. Anda akan melihat bahwa ada beberapa baris kode yang spesifik untuk Kelas yang ditunjuk. Anda dapat memanggil baris-baris kode ini dalam metode yang dilindungi abstrak yang perlu diimplementasikan oleh kelas turunan yang menggunakan kelas dasar Generic.

Ya itu adalah pekerjaan yang membosankan, tetapi saat Anda melanjutkannya akan lebih baik dan lebih baik untuk merobek kelas yang sama dan menggantinya dengan sesuatu yang sangat sangat bersih, ditulis dengan baik dan jauh lebih mudah untuk dipertahankan.

Saya memiliki situasi yang serupa di mana pada kelas generik akhirnya menggantikan sesuatu seperti 6 atau 7 kelas hampir identik lainnya yang semuanya hampir identik tetapi telah disalin dan ditempelkan oleh berbagai programmer selama periode waktu.

Dan ya, saya sangat mendukung pengujian kode secara otomatis. Ini akan lebih mahal di awal tetapi pasti akan menghemat banyak waktu secara keseluruhan. Dan cobalah untuk mencapai cakupan kode secara keseluruhan minimal 80% dan 100% untuk kode Generik.

Semoga ini bisa membantu dan semoga sukses.


0

Saya benar-benar akan menggemakan pendapat yang paling tidak populer di sini dan memihak Gangnusdan menyarankan bahwa duplikasi kode tidak selalu berbahaya dan kadang-kadang bisa menjadi kejahatan yang lebih rendah.

Jika, katakanlah, Anda memberi saya opsi untuk menggunakan:

A) Pustaka gambar yang stabil (tidak berubah) dan kecil, telah teruji dengan baik , yang menduplikasi beberapa lusin baris kode matematika sepele untuk vektor matematika seperti produk titik dan lerps dan klem, tetapi sepenuhnya dipisahkan dari hal lain dan dibangun di sebagian kecil dari Sebentar.

B) perpustakaan gambar tidak stabil (cepat berubah) yang bergantung pada perpustakaan matematika epik untuk menghindari beberapa lusin baris kode yang disebutkan di atas, dengan perpustakaan matematika menjadi tidak stabil dan terus-menerus menerima pembaruan dan perubahan baru, dan oleh karena itu perpustakaan gambar juga harus dibangun kembali jika tidak langsung berubah juga. Dibutuhkan 15 menit untuk membersihkan semuanya.

... maka jelas itu harus menjadi no-brainer bagi kebanyakan orang bahwa A, dan sebenarnya justru karena duplikasi kode minor, lebih disukai. Penekanan utama yang perlu saya buat adalah bagian yang telah teruji dengan baik . Jelas tidak ada yang lebih buruk daripada memiliki kode duplikat yang bahkan tidak berfungsi sejak awal, pada saat itu duplikat bug.

Tetapi ada juga kopling dan stabilitas untuk dipikirkan, dan beberapa duplikasi sederhana di sana-sini dapat berfungsi sebagai mekanisme decoupling yang juga meningkatkan stabilitas (sifat tidak berubah) dari paket.

Jadi saran saya sebenarnya adalah untuk lebih fokus pada pengujian dan mencoba menemukan sesuatu yang benar-benar stabil (seperti tidak berubah, menemukan beberapa alasan untuk berubah di masa depan) dan dapat diandalkan yang ketergantungannya pada sumber luar, jika ada, adalah sangat stabil, lebih dari mencoba untuk menghapus semua bentuk duplikasi dalam basis kode Anda. Dalam lingkungan tim yang besar, yang terakhir cenderung menjadi tujuan yang tidak praktis, belum lagi bahwa itu dapat meningkatkan kopling dan jumlah kode tidak stabil yang Anda miliki di basis kode Anda.


-2

Jangan lupa bahwa duplikasi kode tidak selalu berbahaya. Bayangkan: sekarang Anda memiliki beberapa tugas yang harus diselesaikan dalam modul proyek yang benar-benar berbeda. Sekarang ini adalah tugas yang sama.

Mungkin ada tiga alasan untuk itu:

  1. Beberapa tema seputar tugas ini sama untuk kedua modul. Dalam hal ini duplikasi kode buruk dan harus dilikuidasi. Akan pintar untuk membuat kelas atau modul untuk mendukung tema ini dan menggunakan metode di kedua modul.

  2. Tugas ini bersifat teoritis dalam hal proyek Anda. Misalnya, dari fisika atau matematika, dll. Tugas ada secara mandiri di proyek Anda. Dalam hal ini duplikasi kode buruk dan harus dilikuidasi juga. Saya akan membuat kelas khusus untuk fungsi tersebut. Dan gunakan fungsi tersebut di modul mana pun di mana Anda membutuhkannya.

  3. Tetapi dalam kasus lain, kebetulan tugas adalah kebetulan sementara dan tidak lebih. Akan berbahaya untuk percaya bahwa tugas-tugas ini akan tetap sama selama perubahan proyek karena refactoring dan bahkan debugging. Dalam hal ini akan lebih baik untuk membuat dua fungsi / potongan kode yang sama di tempat yang berbeda. Dan perubahan di masa depan pada salah satu dari mereka tidak akan menyentuh yang lain.

Dan kasus ke-3 ini sangat sering terjadi. Jika Anda menduplikasi "tanpa sadar", sebagian besar karena alasan ini - itu bukan duplikasi nyata!

Jadi, cobalah untuk tetap bersih ketika itu benar-benar diperlukan dan jangan takut duplikasi jika bukan keharusan.


2
code duplication is not always harmfuladalah salah satu saran yang buruk.
Tulains Córdova

1
Haruskah saya tunduk pada otoritas Anda? Saya telah meletakkan alasan saya di sini. Jika saya salah, tunjukkan di mana kesalahannya. Sekarang ini agaknya seperti kemampuan Anda yang buruk untuk terus berdiskusi.
Gangnus

3
Duplikasi kode adalah salah satu masalah inti dalam pengembangan perangkat lunak dan banyak ilmuwan komputasi dan ahli teori telah mengembangkan paradigma dan metodologi hanya untuk menghindari duplikasi kode sebagai sumber utama masalah pemeliharaan dalam pengembangan perangkat lunak. Ini seperti mengatakan "menulis kode yang buruk tidak selalu buruk", dengan cara itu segala sesuatu dapat dibenarkan secara retoris. Mungkin Anda benar, tetapi menghindari duplikasi kode adalah prinsip yang terlalu baik untuk dijalani sehingga mendorong kebalikannya.
Tulains Córdova

Saya telah mengajukan argumen di sini. Kamu belum. Referensi ke pihak berwenang tidak akan berfungsi sejak abad ke-16. Anda tidak dapat menjamin bahwa Anda telah memahami mereka dengan benar dan mereka juga berwenang untuk saya.
Gangnus

Anda benar, duplikasi kode bukan salah satu masalah inti dalam pengembangan perangkat lunak, dan tidak ada paradigma dan metodologi yang dikembangkan untuk menghindarinya.
Tulains Córdova
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.