Duplikasi kode ilusi

56

Insting yang biasa adalah menghapus duplikasi kode yang Anda lihat dalam kode. Namun, saya menemukan diri saya dalam situasi di mana duplikasi itu ilusi .

Untuk menjelaskan situasinya secara lebih terperinci: Saya sedang mengembangkan aplikasi web, dan sebagian besar tampilan pada dasarnya sama - mereka menampilkan daftar item yang dapat digulir dan dipilih oleh pengguna, daftar kedua yang berisi item yang dipilih, dan "Simpan "tombol untuk menyimpan daftar baru.

Tampak bagi saya bahwa masalahnya mudah. Namun, masing-masing dan setiap tampilan memiliki kebiasaan sendiri - kadang-kadang Anda perlu menghitung ulang sesuatu, kadang-kadang Anda harus menyimpan beberapa data tambahan dll. Ini, saya diselesaikan dengan memasukkan kait panggilan balik dalam kode logika utama.

Ada begitu banyak perbedaan kecil antara pandangan yang menjadi kurang dan kurang dapat dipelihara, karena saya perlu memberikan panggilan balik untuk semua fungsi pada dasarnya, dan logika utama mulai terlihat seperti urutan besar panggilan panggilan balik. Pada akhirnya saya tidak menghemat waktu atau kode, karena setiap tampilan memiliki kode sendiri yang dieksekusi - semuanya dalam panggilan balik.

Masalahnya adalah:

  • perbedaannya sangat kecil sehingga kode terlihat hampir persis sama di semua tampilan,
  • ada begitu banyak perbedaan sehingga ketika Anda melihat detailnya, untuk kode tidak sama

Bagaimana saya harus menangani situasi ini?
Apakah memiliki logika inti yang seluruhnya terdiri dari panggilan balik panggilan merupakan solusi yang baik?
Atau haruskah saya lebih suka menduplikasi kode dan menjatuhkan kompleksitas kode berbasis panggilan balik?

Mael
sumber
38
Saya biasanya merasa terbantu untuk membiarkan duplikasi pergi pada awalnya. Setelah saya memiliki beberapa contoh, jauh lebih mudah untuk melihat apa yang umum dan apa yang tidak dan datang dengan cara untuk berbagi bagian yang umum.
Winston Ewert
7
Pertanyaan yang sangat bagus - satu hal yang perlu dipertimbangkan bukan hanya duplikasi fisik kode tetapi duplikasi semantik. Jika perubahan dalam satu bagian kode berarti perubahan yang sama akan diduplikasi di bagian lain, maka bagian itu mungkin merupakan kandidat untuk refactoring atau abstraksi. Kadang-kadang Anda dapat menormalkan ke titik di mana Anda benar-benar menjebak diri sendiri, jadi saya juga akan mempertimbangkan implikasi praktis untuk memperlakukan duplikasi sebagai berbeda secara semantik - mereka mungkin lebih berat daripada konsekuensi dari mencoba deduplicate.
Semut P
Ingat, Anda hanya dapat menggunakan kembali kode yang melakukan hal yang sama. Jika aplikasi Anda melakukan hal yang berbeda pada layar yang berbeda, itu akan membutuhkan panggilan balik yang berbeda. Tidak ada jika, ands, atau tetapi tentang itu.
corsiKa
13
Aturan praktis saya tentang ini adalah: Jika saya membuat perubahan pada kode di satu tempat, apakah itu bug jika saya tidak membuat perubahan yang sama persis di tempat lain? Jika demikian, itu adalah jenis duplikasi yang buruk. Jika saya tidak yakin, pergi dengan mana yang lebih mudah dibaca untuk saat ini. Dalam contoh Anda, perbedaan dalam perilaku disengaja, dan tidak dianggap sebagai bug, sehingga beberapa duplikasi baik-baik saja.
Ixrec
Anda mungkin tertarik untuk membaca tentang Pemrograman Berorientasi Aspek.
Ben Jackson

Jawaban:

53

Pada akhirnya Anda harus membuat penilaian tentang apakah akan menggabungkan kode yang sama untuk menghilangkan duplikasi.

Tampaknya ada kecenderungan yang tidak menguntungkan untuk mengambil prinsip-prinsip seperti "Jangan ulangi dirimu sendiri" sebagai aturan yang harus diikuti dengan menghafal setiap saat. Sebenarnya, ini bukan aturan universal tetapi pedoman yang seharusnya membantu Anda memikirkan dan mengembangkan desain yang baik.

Sebagai segalanya dalam hidup, Anda harus mempertimbangkan manfaat versus biayanya. Berapa banyak kode duplikat akan dihapus? Berapa kali kode diulang? Berapa banyak usaha untuk menulis desain yang lebih umum? Seberapa besar kemungkinan Anda mengembangkan kode di masa mendatang? Dan seterusnya.

Tanpa mengetahui kode spesifik Anda, ini tidak jelas. Mungkin ada cara yang lebih elegan untuk menghapus duplikasi (seperti yang disarankan oleh LindaJeanne). Atau, mungkin tidak ada cukup pengulangan yang benar untuk menjamin abstraksi.

Perhatian yang kurang untuk desain adalah jebakan, tetapi juga hati-hati over-desain.


sumber
Komentar Anda tentang "kecenderungan yang tidak menguntungkan" dan panduan berikut yang membabi buta ada di tempat, saya pikir.
Mael
1
@ David Apakah Anda mengatakan bahwa jika Anda tidak akan mempertahankan kode ini di masa mendatang, Anda tidak akan memiliki alasan yang baik untuk mendapatkan desain yang tepat? (jangan tersinggung, hanya ingin tahu apa yang Anda pikirkan tentang itu)
Terlihat
2
@Mael Tentu saja kita bisa menganggapnya sebagai pergantian frase yang tidak menguntungkan! : D Namun, saya pikir kita harus ketat pada diri kita sendiri seperti kita untuk orang lain ketika menulis kode (saya menganggap diri saya sebagai orang lain ketika saya membaca kode saya sendiri 2 minggu setelah menulisnya).
Melihat
2
@ user61852 maka Anda akan sangat tidak suka Kode Codeless .
RubberDuck
1
@ user61852, haha - tapi bagaimana kalau tidak semua tergantung (informasi tidak diberikan dalam pertanyaan)? Beberapa hal kurang bermanfaat daripada kepastian berlebih.
43

Ingatlah bahwa KERING adalah tentang pengetahuan . Tidak masalah apakah dua bagian kode terlihat sama, identik, atau sama sekali berbeda, yang penting adalah jika bagian pengetahuan yang sama tentang sistem Anda dapat ditemukan di keduanya.

Sepotong pengetahuan mungkin merupakan fakta ("deviasi maksimum yang diizinkan dari nilai yang dimaksudkan adalah 0,1%") atau mungkin beberapa aspek dari proses Anda ("antrian ini tidak pernah berisi lebih dari tiga item"). Ini pada dasarnya setiap informasi yang dikodekan dalam kode sumber Anda.

Jadi, ketika Anda memutuskan apakah sesuatu itu duplikasi yang harus dihapus, tanyakan apakah itu duplikasi pengetahuan. Jika tidak, itu mungkin duplikasi insidental, dan mengekstraksinya ke beberapa tempat umum akan menimbulkan masalah ketika nanti Anda ingin membuat komponen serupa di mana bagian yang tampaknya terduplikasi berbeda.

Ben Aaronson
sumber
12
Ini! Fokus KERING adalah menghindari perubahan duplikat .
Matthieu M.
Ini cukup membantu.
1
Saya pikir fokus KERING adalah untuk memastikan bahwa tidak ada dua bit kode yang harus berperilaku identik tetapi tidak. Masalahnya bukan pekerjaan ganda karena perubahan kode harus diterapkan dua kali, masalah sebenarnya adalah ketika perubahan kode perlu diterapkan dua kali tetapi tidak.
gnasher729
3
@ gnasher729 Ya, itu intinya. Jika dua potong kode memiliki duplikasi pengetahuan , maka Anda akan berharap bahwa ketika yang satu perlu diubah, yang lain juga perlu berubah, yang mengarah ke masalah yang Anda jelaskan. Jika mereka memiliki duplikasi insidental , maka ketika seseorang perlu berubah, yang lain mungkin perlu tetap sama. Dalam hal ini jika Anda telah mengekstraksi metode umum (atau apa pun), Anda sekarang memiliki masalah yang berbeda untuk dihadapi
Ben Aaronson
1
Juga duplikasi penting dan duplikasi yang tidak disengaja , lihat An Doppelgänger yang Tidak Disengaja di Ruby dan SAYA KERING-MENINGKATKAN Kode Saya dan Sekarang Sulit untuk Bekerja dengannya. Apa yang terjadi? . Duplikat tak disengaja juga terjadi di kedua sisi batas konteks . Ringkasan: hanya menggabungkan duplikat jika masuk akal bagi klien mereka agar dependensi ini dimodifikasi secara bersamaan .
Eric
27

Sudahkah Anda mempertimbangkan untuk menggunakan pola Strategi ? Anda akan memiliki satu kelas Tampilan yang berisi kode umum & rutin yang dipanggil oleh beberapa tampilan. Anak-anak dari kelas Tampilan akan berisi kode khusus untuk instance tersebut. Mereka semua akan menggunakan antarmuka umum yang Anda buat untuk Tampilan, dan dengan demikian perbedaannya akan dienkapsulasi & koheren.

LindaJeanne
sumber
5
Tidak, saya belum mempertimbangkannya. Terima kasih atas sarannya. Dari bacaan cepat tentang pola Strategi sepertinya sesuatu yang saya cari. Saya pasti akan menyelidiki lebih lanjut.
Mael
3
ada pola metode templat . Anda juga dapat mempertimbangkan itu
Shakil
5

Apa potensi perubahan? Misalnya, aplikasi kami memiliki 8 area bisnis yang berbeda dengan potensi 4 atau lebih tipe pengguna untuk setiap area. Tampilan dikustomisasi berdasarkan jenis pengguna dan area.

Awalnya, ini dilakukan menggunakan tampilan yang sama dengan beberapa pemeriksaan di sana-sini untuk menentukan apakah hal-hal yang berbeda harus ditampilkan. Seiring waktu, beberapa bidang bisnis telah memutuskan untuk melakukan hal-hal yang berbeda secara drastis. Pada akhirnya, kami pada dasarnya bermigrasi ke satu tampilan (tampilan sebagian, dalam kasus ASP.NET MVC) per bagian fungsionalitas per area bisnis. Tidak semua area bisnis memiliki fungsi yang sama, tetapi jika seseorang menginginkan fungsi yang dimiliki orang lain, area tersebut memiliki pandangan sendiri. Ini jauh lebih rumit untuk memahami kode, serta untuk testability. Misalnya, membuat perubahan untuk satu area tidak akan menyebabkan perubahan yang tidak diinginkan untuk area lain.

Seperti @ dan1111 sebutkan, itu bisa datang ke panggilan penilaian. Pada waktunya, Anda mungkin menemukan apakah itu berfungsi atau tidak.

ps2goat
sumber
2

Satu masalah mungkin adalah Anda menyediakan antarmuka (antarmuka teoretis, bukan fitur bahasa) hanya untuk satu tingkat fungsionalitas:

A(a,b,c) //a,b,c are your callbacks or other dependencies

Alih-alih beberapa tingkat tergantung pada seberapa banyak kontrol yang diperlukan:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

Sejauh yang saya mengerti, Anda hanya mengekspos antarmuka tingkat tinggi (A), menyembunyikan detail implementasi (hal-hal lain di sana).

Menyembunyikan detail implementasi memiliki kelebihan, dan Anda baru saja menemukan kelemahan - kontrol terbatas, kecuali jika Anda secara eksplisit menambahkan fitur untuk setiap hal yang mungkin terjadi saat langsung menggunakan antarmuka level rendah.

Jadi, Anda memiliki dua opsi. Entah Anda hanya menggunakan antarmuka tingkat rendah, gunakan antarmuka tingkat rendah karena antarmuka tingkat tinggi terlalu banyak pekerjaan yang harus dipertahankan, atau memperlihatkan antarmuka tingkat tinggi dan rendah. Satu-satunya pilihan yang masuk akal adalah menawarkan antarmuka level tinggi dan rendah (dan semuanya di antara), dengan asumsi Anda ingin menghindari kode yang berlebihan.

Kemudian ketika menulis satu lagi dari barang-barang Anda, Anda melihat semua fungsi yang tersedia yang telah Anda tulis sejauh itu (kemungkinan yang tak terhitung, terserah Anda untuk memutuskan mana yang mungkin digunakan kembali) dan menyatukannya.

Gunakan satu objek di mana Anda perlu sedikit kontrol.

Gunakan fungsionalitas tingkat terendah ketika beberapa keanehan perlu terjadi.

Ini juga tidak terlalu hitam-putih. Mungkin kelas tingkat tinggi besar Anda BISA cukup mencakup semua kasus penggunaan yang mungkin. Mungkin kasus penggunaannya sangat beragam sehingga tidak ada yang lebih dari sekadar fungsionalitas primitif tingkat terendah. Terserah Anda untuk menemukan keseimbangan.

Semangka
sumber
1

Sudah ada jawaban berguna lainnya. Saya akan menambahkan milik saya.

Duplikasi buruk karena

  1. itu mengacaukan kode
  2. itu mengacaukan comphrension kami dari kode tetapi yang paling penting
  3. karena jika Anda mengubah sesuatu di sini dan Anda juga harus mengubah sesuatu di sana , Anda bisa melupakan / memperkenalkan bug / .... dan sulit untuk tidak pernah lupa.

Jadi intinya adalah: Anda tidak menghilangkan duplikasi demi itu atau karena seseorang mengatakan itu penting. Anda melakukannya karena Anda ingin mengurangi bug / masalah. Dalam kasus Anda, tampaknya jika Anda mengubah sesuatu dalam tampilan, Anda mungkin tidak perlu mengubah baris yang sama persis di semua tampilan lainnya. Jadi, Anda memiliki duplikasi yang jelas , bukan duplikasi yang sebenarnya.

Poin penting lainnya adalah untuk tidak pernah menulis ulang dari awal sesuatu yang berfungsi sekarang hanya berdasarkan masalah prinsip, seperti yang dikatakan Joel (Anda mungkin sudah pernah mendengar tentang dia ....). Jadi, jika pandangan Anda berfungsi, lanjutkan untuk meningkatkan langkah demi langkah dan jangan menjadi mangsa "satu kesalahan strategis terburuk yang dapat dilakukan oleh perusahaan perangkat lunak".

Francesco
sumber