author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Jebakan Bu Lean (The Boolean Trap & Mistake)
Sunday Oct 11th, 2020 11:25 am5 mins read
Jebakan Bu Lean (The Boolean Trap & Mistake)
Source: Pinterest - Businesswoman Caught In Mouse Trap Cartoon Vector

Boolean adalah tipe data yang hanya punya dua nilai. True dan False, hanya itu. Pada beberapa bahasa pemrograman atau database nilai True dan False bisa diganti dengan bilangan 1 dan 0. Boolean ini sangat sederhana karena hanya ada 2 kemungkinan tersebut. Saking simple-nya, pemakaian Boolean ini seringkali menjebak hingga sulit dibaca dan di-maintain. Berikut adalah jebakan-jebakan pada Boolean yang umum ditemui. Sample code-nya gw pakai Java karena ini bahasa yang paling gw kuasai. Tapi kasus seperti ini ga hanya terjadi pada bahasa Java saja.

Penamaan Boolean

Jebakan pertama terletak pada penamaan. Sesuai Naming Convention yang berlaku, penamaan variable yang baik adalah menggunakan kata benda (noun), seperti person, name, object, dan lainnya atau bisa juga berupa kata sifat (adjective) seperti active, height, width, dan lainnya. Sedangkan penamaan method yang baik adalah berupa kata kerja (verb), seperti getPerson(), printName(), constructObject(), dan sebagainya. Tak terkecuali dengan tipe data Boolean. Convention seperti itu dapat memudahkan code untuk dibaca karena code yang baik adalah code yang bisa dibaca secara English oleh manusia. Pada praktiknya, seringkali penamaan variable Boolean menggunakan kata kerja (verb). Contohnya pada code berikut:

boolean isPresent = true;
boolean present = true;
if(isPresent){
	System.out.println("isPresent = " + isPresent);
}
if(present){
	System.out.println("present = " + present);
}

Kira-kira lebih baku mana kalimat "if present" atau "if is present"? secara English tentu lebih baku "if present". Kalau penyebutannya salah, guru bahasa Inggrisnya ntar bisa marah😠.

Accessor pada Java Bean pun begitu. Misalkan pada sebuah Entity ada property Boolean seperti code berikut:

public class Employee{
	private static final long serialVersionUID = 1L;

	private int id;
	private String name;
	private String email;
	private boolean isDead;
	private boolean active;

	public boolean isDead(){
		return isDead;
	}

	public void setDead(boolean dead){
		isDead = dead;
	}

	public boolean isActive(){
		return active;
	}

	public void setActive(boolean active){
		this.active = active;
	}

}

Ini dapat menimbulkan beberapa masalah pada property isDead. Pertama, saat melakukan generate getter and setter, biasanya IDE akan men-generate method isDead() untuk accessor boolean by default, bukan getIsDead(). Mau tidak mau ini harus di-fix manual. Ok lah, anggap aja ini bisa kita fix secara manual. Masalah kedua adalah saat menggunakan Library pihak ketiga. Biasanya Library semacam Hibernate, Spring Data JPA, Jackson, Map Struct, dan sejenisnya membutuhkan method is[property] untuk mengakses property Boolean, karena mereka berpedoman pada Java Bean Convention. Bahkan pada beberapa Library, property Boolean dengan awalan is[property] tetap tidak bisa dibaca meskipun accessor-nya sudah diganti manual. Tentu ini akan sangat merepotkan dan membingungkan. Udah repot, bingung lagi, makanya solusi terbaik ikuti Convention aja 😁. FYI, pada Java Bean Convention sendiri accessor untuk property bertipe Boolean dapat berupa is[property] atau bisa juga get[property], tapi yang paling sering digunakan adalah is[property].

Parameter dengan Boolean

Jebakan selanjutnya adalah saat menggunakan method dengan Boolean sebagai parameter. Penggunaan Boolean pada parameter seringkali akan menimbulkan kebingungan seperti pada code berikut:

public void printGender(boolean maleGender){
	if(maleGender){
		System.out.println("this is man");
	} else {
		System.out.println("this is woman");
	}
}

Ok, untuk sementara waktu code ini terlihat tidak ada masalah sama sekali. Tapi misalkan dalam beberapa waktu ke depan aplikasinya membolehkan pilihan "gender ketiga", ini akan menimbulkan refactor besar-besaran terhadap code terkait yang membutuhkan method ini (bukan berarti gw mendukung LGBTQ ya🤫). Bagaimana jika ada tambahan lagi? Misalkan ada tambahan requirement untuk orang yang ingin merahasiakan jenis kelaminnya. Makin pusing maintanance-nya, makin banyak refactor code, makin susah dibaca method-nya. Belum lagi untuk kasus lain yang lebih kompleks, bisa jadi pilihannya lebih dari empat value. Solusinya adalah dengan mengganti parameter Boolean dengan sebuah constant. Pada Java bisa menggunakan Enum seperti berikut:

public void printGender(Gender gender){
	System.out.println("this is " + gender.getNickName());
}

public enum Gender{
	MALE("man"),
	FEMALE("woman"),
	TRANSGENDER("transgender"),
	OTHER("unknown"),
	;

	private final String nickName;

	Gender(String nickName){
		this.nickName = nickName;
	}

	public String getNickName(){
		return nickName;
	}
}

Dengan begini, perubahan hanya terdapat pada satu tempat saja dan tidak akan mengganggu code lainnya yang sudah ada. Selain itu maintainance-nya juga lebih gampang, ga perlu ribet. Mau nambah opsi lagi berapapun juga cukup nambah constant saja.

Double Negasi

Selain penamaan variable, yang sering membuat bingung adalah negasi pada Boolean. Penggunaan negasi (!) pada method atau variable yang maknanya sudah negatif akan sangat membingungkan. Contohnya sebagai berikut:

boolean notAlive = isNotAlive();
if(!notAlive){
	System.out.println("it's alive!");
}

Bisa bikin miskomunikasi dan pusing saat dibaca kan😵? Solusinya adalah by default gunakan method boolean bermakna positif isAlive(). Jika boolean bermakna negatif juga sering dipakai maka sebaiknya disediakan saja keduanya, yaitu isAlive() dan isNotAlive() untuk negasinya.

public boolean isAlive(){
	return true;
}

public boolean isNotAlive(){
	return !isAlive();
}

Dengan begitu, user tinggal pilih sesuai kebutuhan method mana yang mau dipakai.

Conditional Tanpa Nama

Satu lagi yang membingungkan adalah ketika ada logic conditional yang agak panjang. Misalnya logicnya adalah kita akan memprint objek employee kalau objeknya memiliki akun gmail. Kriterianya yaitu ketika statusnya aktif, emailnya ga null, dan emailnya diakhiri dengan "@gmail.com". Biasanya karena engineernya lagi males, logic tersebut ditulis aja di dalam kurung conditionalnya. Kalau conditionalnya cuma satu atau dua statement masih mending, tapi kalau banyak, maksud dari big picture code tersebut jadi ga keliatan. Contohnya seperti ini:

if(employee.getEmail() != null && employee.getEmail().endsWith("@gmail.com") && employee.isActive()){
	System.out.println("employee " + employee + " si eligible to execute");
}

Masalahnya adalah ketika engineer lain membaca code tersebut yang ga tau apa-apa jadi bertanya-tanya, "itu maksudnya apa ya?"🤔. Dia terpaksa harus membaca detail keseluruhan isi conditionalnya untuk bisa mengerti. Solusinya adalah conditional tersebut dibungkus jadi variabel boolean dan dikasih nama "employeeHasGmail".

boolean employeeHasGmail = employee.getEmail() != null && employee.getEmail().endsWith("@gmail.com") && employee.isActive();
if(employeeHasGmail){
	System.out.println("employee " + employee + " is eligible to execute");
}

Sekarang lebih enak dibaca. Engineer lain jadi paham bahwa maksudnya adalah untuk memprint employee yang memiliki akun gmail. Ga perlu membaca keseluruhan isi conditionalnya. Cukup tau dari nama variabelnya saja.

Melanggar Single Responsibility Principle

Ga hanya itu, misalkan pada saat ingin melakukan aktivasi atau inaktivasi email, seperti pada kasus berikut:

public void activateEmail(boolean activate){
	if(activate){
		//logic to activate email
	} else {
		//logic to inactivate email
	}
}

Secara bahasa, "activate email" merupakan perintah untuk melakukan aktivasi email. Tidak perlu lagi ditambahkan parameter True atau False karena "activate email" sudah bermakna positif dan mengakibatkan Double Positive. Selain "activate", kata kunci lainnya yang juga bermakna positive adalah "enable/disable". Penggunaan "disableEmail = false" atau "enableEmail = true" juga mengakibatkan Double makna. Kalaupun ada perintah untuk inaktivasi email, maka itu sudah diluar scope dari perintah "activate email". Selain karena akan membingungkan, code di atas juga melanggar Single Responsibility Principle. Solusinya adalah membagi code tersebut menjadi dua perintah berbeda.

public void activateEmail(){
	//logic to activate email
}

public void inactivateEmail(){
	//logic to inactivate email
}

Jadi sekarang masing-masing perintah fokus bertanggung-jawab pada tugasnya masing-masing. Nama methodnya lebih to-the-point dan jelas.

Kesimpulan

Kesimpulan dari gw adalah think twice ketika menggunakan Boolean. Ini bukan sebuah "larangan" atau "hukum" yang wajib ditaati. Ini hanya semacam panduan agar code lebih dapat dibaca dan dikembangkan. Tetap ikuti arahan tentang kata benda (noun) untuk variable, dan kata kerja (verb) untuk method. Penamaan Boolean yang tidak tepat dapat menimbulkan kebingungan saat memahami code. Penggunaan Boolean pada parameter juga seringkali susah di-maintain seperti kasus di atas. Bukan hanya untuk kasus simple saja, secara umum memang pemakaian Boolean sebagai parameter seringkali tidak tahan perubahan alias susah di-maintain. Misalkan pada bisnis yang sedang berjalan, sekarang hanya ada dua pilihan status pembayaran, dibayar dan belum dibayar. Jadi hanya menggunakan Boolean isPaid. Atau dua pilihan status pengiriman, diterima dan tidak diterima. Jadi hanya menggunakan Boolean isReceived. Nah, ketika terjadi improvement di masa depan, misalkan pilihan pembayaran bisa juga dibayar setengah, atau status pengiriman juga bisa diterima sebagian. Ini dapat mengakibatkan "kebakaran" dan rombak code. Makanya, lebih baik gunakan constant sedari awal. Tapi bukan berarti setiap pemakaian Boolean pada parameter benar-benar "diharamkan". Tetap ada pengecualian untuk beberapa kasus yang memang membutuhkan Boolean sebagai parameter. Salah satunya setter untuk property Boolean pada Entity model yang tentu saja membutuhkan Boolean sebagai parameter. Kalau nggak, gimana caranya nge-set nilainya 😂. Dan ini lebih sering berlaku pada bisnis Class. Sedangkan untuk Class yang isinya lebih teknis mungkin ada pengecualian pada kasus tertentu. Oh ya, untuk bahasa yang tidak ada type Enum bisa menggunakan object seperti pada Javascript. Untuk Jebakan lainnya, bisa dibaca juga post tentang Jebakan Optional