Unexploited Encapsulation
Tampaknya ini smell udah familiar dengan Martin Fowler - Switch Statments... Intinya jangan pakai if-else atau switch untuk seleksi Object aja..! Selesai 😀
Smell ini terjadi karena adanya pemakaian switch
ataupun if-else
statement untuk melakukan validasi tipe object. Smell ini hampir sama dengan smell Switch statements.
Tidak semua switch
atau if-else
itu berbahaya. Perlu dipertimbangkan apakah akan terjadi violasi terhadap OCP (Open Closed Principle).
Contoh
Permasalahan
Lihat class ShapePrinter.java dan CharNeededCounter.java.
if(shape.equalsIgnoreCase("square")){
...
} else if(shape.equalsIgnoreCase("triangle")){
...
} else {
...
}
if(shape.equalsIgnoreCase("square")){
...
} else if(shape.equalsIgnoreCase("triangle")){
...
} else {
...
}
Kedua class tersebut akan melanggar OCP. Bayangkan bila ada tipe Shape
baru yang perlu dibuat, tentu saja akan bertambah lagi if
di masing-masing ShapePrinter dan CharNeededCounter.
Misal bertambah logic shape Circle
. Violasi OCP terjadi di 2 class tersebut.
if(shape.equalsIgnoreCase("square")){
...
} else if(shape.equalsIgnoreCase("triangle")){
...
} else if(shape.equalsIgnoreCase("circle")){
...
} else {
...
}
if(shape.equalsIgnoreCase("square")){
...
} else if(shape.equalsIgnoreCase("triangle")){
...
} else if(shape.equalsIgnoreCase("circle")){
...
} else {
...
}
Di dalam contoh ini, if-else square dan triangle ada di 2 class. Pada kondisi nyata bila hal ini dibiarkan terjadi, if-else square dan triangle akan terus beranak-pinak bila ada kebutuhan logic lain.
Semakin sedikit kita menggunakan if-else di dalam code, maka akan semakin baik. Bahkan ada orang yang membuat campaign untuk ini: Anti-IF Campaign.
Penyelesaian
Untuk contoh kasus ini, kita melakukan tahapan berikut:
Opsi 1: Replace Type Code with Subclasses
Kita memiliki dua type code. square
dan triangle
. Oleh karena itu, kita buat class Square, Shape sebagai abstract class yang memiliki fungsi charNeeded
dan print
, lalu Triangle.java dan Square.java meng-extends class Shape
.
Opsi 2: Replace Conditional with Polymorphism
Setelah class Square
dan Triangle
sudah terbentuk. Logic print dari ShapePrinter
dan logic menghitung karakter dari CharNeededCounter
dipindahkan ke masing-masing class.
Dengan begini, bila ada jenis baru, misalkan Circle
, kita tinggal extends dari class Shape
.
When to Ignore
Perlu diketahui jika pada kasus tersebut berkaitan dengan masalah validasi tipe object melalui inputan user ataupun class ini dibuatkan dengan menerapkan design pattern Factory, maka perlu diketahui bahwa smell ini dapat diabaikan namun dengan catatan pada kasus ShapeFactory
berikut.
ShapeFactory
User tetap akan meng-input string melalui console. Oleh karena itu, kita perlu menyiapkan sebuah class Factory untuk membuat class Shape dari string yang diinput.
Harusnya Anda menyadari bahwa terjadi violasi OCP disini. Bila class Circle
dibuat, maka if di Factory bertambah. Hal ini dimaklumi karena OCP hanya dilanggar satu kali saja di dalam Factory (tidak akan dilanggar lagi di tempat lain) dan memang terpaksa dilakukan karena input dari user adalah string. Ibaratkan Factory disini berperan sebagai anti-corruption layer.
Di beberapa bahasa pemrograman, ada cara spesifik untuk mengakali menghilangkan if-else pada class Factory, contohnya untuk Java: https://www.javacodegeeks.com/2014/10/factory-without-if-else.html.