Change Preventers
Smell → Martin Fowler Code Smells → Change Preventers
Semua smell di dalam grup ini berkaitan dengan code yang mempersulit kita ketika ingin melakukan perubahan atau penambahan fitur.
Divergent Change
sourcemaking | refactoring.guru | before | after
Martin Fowler:
Divergent change occurs when one module is often changed in different ways for different reasons. If you look at a module and say, “Well, I will have to change these three functions every time I get a new database; I have to change these four functions every time there is a new financial instrument,” this is an indication of divergent change. The database interaction and financial processing problems are separate contexts, and we can make our programming life better by moving such contexts into separate modules. That way, when we have a change to one context, we only have to understand that one context and ignore the other. We always found this to be important, but now, with our brains shrinking with age, it becomes all the more imperative. Of course, you often discover this only after you’ve added a few databases or financial instruments; context boundaries are usually unclear in the early days of a program and continue to shift as a software system’s capabilities change.
Penjelasan Smell
Ada sebuah perusahaan menciptakan mesin printer yang bisa scan dokumen. Pada suatu kasus, fungsi scanner ini ingin mereka tambahkan kemampuan agar bisa scan layar HP. Mau tak mau harus ganti komponen printer yaitu ticker (pengatur kecepatan scan dan print). Setelah ticker diganti ternyata komponen tersebut memperlambat proses print dan harus diganti lagi komponen tersebut dan pita printer. Setelah kedua komponen berganti, tiba-tiba mereka ingin fungsi scanner tersebut difungsikan sebagai mesin fotokopi langsung. Mereka harus memodifikasikan board dengan menambahkan paralelitas scanner dan printer. Namun semua itu telah membuang-buang waktu hingga 3 minggu hanya karena penambahan 2 fitur baru lho!
Hati-hati ketika mempelajari smell ini dari sourcemaking karena bagian Signs and Symptoms di sourcemaking berbeda dengan definisi Martin Fowler di bukunya. Kita akan menggunakan definisi sesuai dengan sumbernya.
Seperti di paragraf dari Martin Fowler diatas, smell ini terjadi ketika sebuah class sering berubah untuk alasan yang berbeda-beda. Dengan kata lain, class ini melanggar Single Responsibility Principle.
Disebut di paragraf diatas: context boundaries are usually unclear. Terkadang sulit untuk menentukan apa yang menjadi tanggungjawab class ini atau bukan. Hal ini kembali ke diskusi tim masing-masing ketika code design, atau menunggu gejala benar-benar jelas ketika terbukti benar class ini sering berubah untuk alasan yang berbeda. Pemisahan tanggungjawab paling klasik adalah menggunakan MVC dimana model, view, dan controller dipisah. Bila ingin pemisahan yang lebih detail, Anda juga bisa menggunakan layered architecture yang sudah diajarkan di kelas DDD.
Perhatikan class Rectangle di package before
. Disana terdapat field width
dan weight
. Terdapat method area()
dan perimeter()
. Dan juga terdapat method print(String style)
. Kita bisa berargumen bahwa class ini memiliki dua tanggungjawab, yaitu mengurus kalkulasi Rectangle dan juga mengatur tampilan Rectangle ke dalam console.
Penyelesaian
Kita pindahkan method print
di class Rectangle
ke class yang baru. Karena di print
juga terdapat smell primitive obsession, sekalian kita buatkan struktur baru menggunakan strategy design pattern. Perhatikan hasil refactor-nya di package after
.
Shotgun Surgery
sourcemaking | refactoring.guru | before | after
Penjelasan Smell
Pada suatu hari, terdapat mobil fortuner yang ingin diganti mesin diesel menjadi diesel V8! Ngomongnya gampang.. Saya turunkan mesin kamu.. Saya muatkan mesin diesel V8 dan ternyata mesin diesel tersebut kebesaran! Mau tak mau saya potong frame mobil kamu dan saya harus bongkar dashboard kamu hanya agar mesin tersebut muat ke dalam mobil. Setelah ganti mesin, datang lagi masalah kalo mesin tersebut memerlukan radiator khusus.. ganti lagi radiatornya.. eh enggak dingin AC-nya.. ganti bagian lain dan lainnya lagi. Pokoknya ribet!! 💢
Smell ini terjadi ketika kita ingin mengganti atau menambahkan fitur ke dalam code, kita perlu mengganti bagian code yang tersebar di banyak class lain. Analoginya adalah bila seseorang ditembak dengan senjata api jenis shotgun, luka tembak akan menyebar di banyak tempat.
Perhatikan code di package before
. Class PriceService
dan PriceIncludeTaxService
, di dalamnya terdapat code untuk mengubah price yang access modifier-nya public. Masing-masing fungsi update juga memiliki validasi price masing-masing.
Sekarang, kita mendapatkan permintaan fitur baru dimana setiap kali ada update value pada class Product
, maka kita melakukan logging menggunakan class Logger.
Bila kita tidak segera melakukan refactor, kita perlu selalu melakukan perubahan ke setiap bagian code update value yang tersebar di banyak class. Karena ini hanya contoh, tersebarnya baru di dua class saja. Bayangkan bila tersebar di puluhan class. Selain capek, hal ini juga rawan human-error karena bisa ada bagian code yang lupa diganti.
Contoh masalah lainnya: bila cara validasi price diganti, kita juga perlu melakukan shotgun surgery.
Penyelesaian
Kita ubah access modifier price menjadi private. Code validasi price dipindahkan ke setPrice
. Lalu fungsi setter ditambahkan logging.
Sekarang, update value price sudah terpusat di dalam fungsi setPrice
, sehingga bila suatu saat nanti ada perubahan mengenai update value price, kita cukup mengubah 1x saja di satu tempat.
Parallel Inheritance Hierarchies
sourcemaking | refactoring.guru | before | after
Penjelasan Smell
Anggap ada bodi laptop dan layar tablet. Disaat ada komponen memory di body laptop, layar tablet musti ada memory. Disaat bodi laptop memiliki keyboard, layar tablet juga harus ada keyboard virtual. Dan disaat laptop itu ada komponen baru, layar tablet tersebut musti punya komponen itu juga. Hierarki maha bucin memang selalu ikut-ikutan terhadap segala hal yang ia senangi! 💫💢
Smell ini terjadi ketika ada hirarki inheritance yang selalu ditambahkan bersama-sama.
Bayangkan ada dua atau lebih hirarki parent-children class. Lalu kita perlu menambahkan (extends) satu class baru di salah satu hirarki, hirarki lain ikut-ikutan juga butuh untuk extends class mengikuti hirarki ini. Contoh: Abstract class A di-extends menjadi A1 dan A2. Lalu ada abstract class B di-extends menjadi B1 dan B2. Ketika A di-extends menjadi A3, B juga ikut-ikutan butuh B3.
Untuk menghadapi smell ini, kita perlu mempertimbangkan apakah cocok bila hirarki-hirarki ini digabung saja menjadi satu.
Smell tidak perlu ditangani bila memang hirarki paralel ini disengaja untuk keperluan pemisahan konteks/tanggungjawab. Atau ketika digabung code malah menjadi lebih jorok.
Perhatikan code di package before
. Hirarki Shape2D
selalu berkembang berbarengan dengan hirarki AreaInterface
. Bila ada Shape2D
yang baru, misalnya Circle
. maka AreaInterface
juga akan ikut-ikutan membuat anak baru CircleAreaCalculator
.
Penyelesaian
Kita pindahkan logika perhitungan area agar digabung saja ke dalam Shape2D
.
Referensi Gambar
Semua gambar referensi mengikuti pictorial gambar pada Refactoring.guru dengan tetap mengutamakan link credit pada sourcemaking.com maupun refactoring.guru