Duplikasi kode tanpa abstraksi yang jelas

14

Pernahkah Anda menemukan kasus duplikasi kode di mana, setelah melihat garis-garis kode, Anda tidak bisa cocok dengan abstraksi tematik untuknya yang dengan setia menggambarkan perannya dalam logika? Dan apa yang Anda lakukan untuk mengatasinya?

Ini adalah duplikasi kode, jadi idealnya kita perlu melakukan beberapa refractoring, seperti misalnya menjadikannya fungsinya sendiri. Tetapi karena kode tidak memiliki abstraksi yang baik untuk menggambarkannya, hasilnya akan menjadi fungsi aneh yang bahkan kita tidak bisa mencari tahu nama yang bagus, dan yang perannya dalam logika tidak jelas hanya dengan melihatnya. Bagi saya, itu menyakitkan kejelasan kode. Kita bisa mempertahankan kejelasan dan membiarkannya apa adanya, tetapi kemudian kita melukai kelestariannya.

Menurut Anda apa cara terbaik untuk mengatasi sesuatu seperti ini?

EpsilonVector
sumber

Jawaban:

18

Terkadang duplikasi kode adalah hasil dari "permainan kata": Dua hal terlihat sama, tetapi tidak.

Ada kemungkinan bahwa abstrak yang berlebihan dapat merusak modularitas sebenarnya dari sistem Anda. Di bawah rezim modularitas, Anda harus memutuskan "apa yang mungkin berubah?" dan "apa yang stabil?". Apa pun yang stabil akan dimasukkan ke dalam antarmuka, sementara apa pun yang tidak stabil akan dienkapsulasi dalam implementasi modul. Kemudian, ketika hal-hal berubah, perubahan yang perlu Anda lakukan diisolasi ke modul itu.

Refactoring diperlukan ketika apa yang Anda pikir stabil (mis. Panggilan API ini akan selalu mengambil dua argumen) perlu diubah.

Jadi, untuk dua fragmen kode duplikat ini, saya akan bertanya: Apakah perubahan yang diperlukan untuk satu berarti yang lain harus diubah juga?

Bagaimana Anda menjawab pertanyaan itu mungkin memberi Anda wawasan yang lebih baik tentang abstraksi yang baik.

Pola desain juga merupakan alat yang berguna. Mungkin kode duplikat Anda melakukan traversal dari beberapa bentuk, dan pola iterator harus diterapkan.

Jika kode duplikat Anda memiliki beberapa nilai pengembalian (dan itulah sebabnya Anda tidak bisa melakukan metode ekstrak sederhana), maka mungkin Anda harus membuat kelas yang menyimpan nilai yang dikembalikan. Kelas bisa memanggil metode abstrak untuk setiap titik yang bervariasi antara dua fragmen kode. Anda kemudian akan membuat dua implementasi konkret dari kelas: satu untuk setiap fragmen. [Ini secara efektif pola desain Metode Templat, tidak menjadi bingung dengan konsep templat di C ++. Atau, apa yang Anda lihat mungkin lebih baik diselesaikan dengan pola Strategi.]

Cara alami dan berguna lain untuk memikirkannya adalah dengan fungsi tingkat tinggi. Misalnya, membuat lambdas atau menggunakan kelas dalam anonim agar kode dapat diteruskan ke abstraksi. Secara umum, Anda dapat menghapus duplikasi, tetapi kecuali jika benar-benar ada hubungan di antara mereka [jika ada yang berubah, maka harus yang lain] maka Anda mungkin menyakiti modularitas, tidak membantunya.

Macneil
sumber
4

Ketika Anda menghadapi situasi seperti ini, sebaiknya pikirkan abstraksi "non-tradisional". Mungkin Anda memiliki banyak duplikasi dalam suatu fungsi dan memfaktorkan fungsi lama yang tidak cocok dengan baik karena Anda harus melewati terlalu banyak variabel. Di sini, fungsi bersarang gaya D / Python (dengan akses ke lingkup luar) akan berfungsi dengan baik. (Ya, Anda bisa membuat kelas untuk menampung semua keadaan itu, tetapi jika Anda hanya menggunakannya dalam dua fungsi, ini adalah solusi yang jelek dan bertele-tele karena tidak memiliki fungsi bersarang.) Mungkin warisan tidak cukup pas, tetapi sebuah Mixin akan bekerja dengan baik. Mungkin yang benar-benar Anda butuhkan adalah makro. Mungkin Anda harus mempertimbangkan beberapa metaprogramming template atau refleksi / introspeksi, atau bahkan pemrograman generatif.

Tentu saja, dari sudut pandang pragmatis, ini semua sulit jika tidak mustahil dilakukan jika bahasa Anda tidak mendukungnya dan tidak memiliki cukup kemampuan pemrograman untuk mengimplementasikannya secara bersih dalam bahasa tersebut. Jika ini masalahnya, saya tidak tahu harus mengatakan apa kepada Anda kecuali "dapatkan bahasa yang lebih baik". Selain itu, mempelajari bahasa tingkat tinggi dengan banyak kemampuan abstraksi (seperti Ruby, Python, Lisp atau D) dapat membantu Anda memprogram lebih baik dalam bahasa tingkat rendah di mana beberapa teknik mungkin masih dapat digunakan, tetapi kurang jelas.

dsimcha
sumber
+1 untuk banyak teknik hebat yang dikompresi dalam ruang yang sempit. (Yah, pasti +1 untuk teknik yang dijelaskan juga.)
Macneil
3

Secara pribadi saya mengabaikannya dan melanjutkan. Kemungkinannya adalah, jika suatu kasus aneh itu lebih baik untuk diduplikasi, Anda bisa menghabiskan waktu lama untuk refactoring dan pengembang berikutnya akan melihat dan membatalkan perubahan Anda!

Toby
sumber
2

Tanpa sampel kode, sulit untuk mengatakan mengapa kode Anda tidak memiliki abstraksi yang mudah diidentifikasi. Dengan peringatan itu, berikut adalah beberapa ide:

  • alih-alih membuat satu fungsi baru untuk menyimpan kode umum, pisahkan fungsionalitas menjadi beberapa bagian berbeda;
  • kelompokkan potongan kecil bersama-sama berdasarkan tipe data umum atau perilaku abstrak;
  • tulis ulang kode duplikat yang diberikan potongan baru;
  • jika kode baru masih menentang abstraksi yang jelas, pisahkan menjadi kecil juga dan ulangi prosesnya.

Kesulitan terbesar dalam latihan ini adalah bahwa fungsi Anda kemungkinan menggabungkan terlalu banyak perilaku yang tidak berhubungan pada tingkat abstraksi tertentu, dan Anda perlu menangani beberapa dari mereka di tingkat yang lebih rendah. Anda menduga dengan benar bahwa kejelasan adalah kunci untuk mempertahankan kode, tetapi memperjelas perilaku kode (kondisi saat ini) sangat berbeda dengan membuat maksud kode menjadi jelas.

Buat bagaimana potongan kode yang lebih kecil abstrak dengan meminta tanda tangan fungsinya mengidentifikasi apa, dan potongan yang lebih besar harus lebih mudah untuk diklasifikasi.

Huperniket
sumber