Studi Kasus: Penjumlahan 20 + 30 (11)

Penjumlahan 20 + 30

5+2+4-3+10-2=?

20 + 30

1 + 1 + 1

Object ratusan

Eksepsi jika tidak ada ekspresi yang bersesuaian

Sebelum melangkah lebih jauh kita harus menguji apakah ekspresi ke dua bisa menerima puluhan. Berikut ini adalah contoh test untuk hal ini,

[Test] 
public void Penjumlahan20dengan30(){ 
	string input="20 + 30"; 
	IExpressionConverter exConverter=new ExpressionConverter(); 
	IExpression ex=exConverter.Convert(input); 
	Assert.AreEqual("20 + 30",ex.Expression); 
	Assert.AreEqual(50,ex.Value); 
}

Good! Test kita bisa jalan. Dengan demikian puluhan di ekspresi kedua bisa diterima.

Sampai disini sebetulnya kita telah melangkah 75% sisa yang 25% hanyalah perulangan. 1+1+1 boleh kita tulis ([1+1] + 1). Kunci dari keberhasilan konversi kita selanjutnya terletak pada seberapa pintar kita melakukan pemotongan. Daripada banyak teori, kita langsung test saja,

[Test] 
public void Penjumlahan1dengan1dengan1(){ 
	string input="1 + 1 + 1"; 
	IExpressionConverter exConverter=new ExpressionConverter(); 
	IExpression ex=exConverter.Convert(input); 
	Assert.AreEqual("1 + 1 + 1",ex.Expression); 
	Assert.AreEqual(3,ex.Value); 
}

Test ini berakhir red, dengan pesan: expected “1 + 1 + 1” but was “1 + 1”. Dengan demikian kita sebetulnya telah mengenali pola pertama kita tinggal mengulang lagi pola yang kedua. Caranya adalah dengan mengetahui letak atau posisi masing-masing operator. Nah, kita melakukan looping didalam string dan memotong-motongnya. Kita melakukan loop sebanyak operator yang ada. Tantangannya adalah kita harus tahu jumlah operator dan juga posisi. Jumlah operator adalah sebanyak jumlah number dikurang satu: opr=angka -1. Untuk sementara test kita diatas kita ignore. Kita akan mencoba melakukan pencarian jumlah operator dan posisi terlebih dahulu.

Berikut ini adalah test untuk mencari jumlah operator,

[Test] 
public void HitungJumlahOperator() 
{ 
	string input="1 + 1 + 1 - 5 -60 + 10"; 
	string[] opr=input.Split('+','-'); 
	Assert.AreEqual(5,opr.Length-1); 
}

Dengan demikian untuk mengetahui jumlah operator kita gunakan method Split().

Dan berikut ini adalah test method untuk mendapatkan posisi dari operator,

[Test] 
public void DapatkanPosisiOperatorDalamExpresi() 
{ 
	string input="1 + 1 + 1 - 5 -60 + 10"; 
	Assert.AreEqual(2,getOperatorPosition(1,input)); 
	Assert.AreEqual(6,getOperatorPosition(2,input)); 
	Assert.AreEqual(10,getOperatorPosition(3,input)); 
	Assert.AreEqual(14,getOperatorPosition(4,input)); 
}

Jadi kita harus mensupplay nomor operator dan ekspresi input.

private int getOperatorPosition(int number,string input) 
{ 
	string[] opr=input.Split('+','-'); 
	int result=0; 
	for(int i=0; i<number;i++) 
	{ 
		result +=opr&#91;i&#93;.Length; 
	} 
	result +=number-1; 
	return result; 
}
&#91;/sourcecode&#93;
	<p>Sekalilagi kita menggunakan split. Lalu kita susun sebanyak operator yang diinginkan. Apa yang kita dapatkan disini akan kita terapkan di program. </p>
	<p>Sekarang hapus Ignore dan kembali ke test sebelumnya. Lalu saya rubah method interpret menjadi,</p>

private IExpression interpret(string ekspresi) 
{ 
	ekspresi=ekspresi.Trim(); 
	ekspresi=ekspresi.Trim(PLUS,MINUS); 
	if(!(ekspresi.Contains(PLUS.ToString())||ekspresi.Contains(MINUS.ToString()))) 
		return Number.CreateNewNumber(ekspresi); 
	else{ 
		int totalOperator=getTotalOperator(ekspresi); 
		IExpression exp1=getExp1(ekspresi); 
		string subEkspresi=ekspresi; 
		for(int i=0;i<totalOperator;i++){ 
			exp1=composeOperation(exp1,subEkspresi); 
			subEkspresi=getNextExpression(i+1,ekspresi); 
		} 
		return exp1; 
	} 
}
&#91;/sourcecode&#93;
	<p>Pertama, saya harus mendapatkan jumlah operator didalam ekspresi. Cara untuk mendapatkannya sudah kita riset sebelumnya.</p>
	<p>Kedua, saya harus mendapatkan ekspresi 1 (exp1). Jadi untuk ekspresi “5+4+1+3” perulangannya akan seperti ini. </p>
<pre>
Langkah 1: exp1=5. Exp2=4. Operator=+. SubEkspresi=”5+4+1+3”
Langkah 2: exp1=5+4. Exp2=1 Operator=+. SubEkspresi=”4+1+3”
Langkah 3: exp1=5+4+1. Exp2=3 Operator=+. SubEkspresi=”1+3”
</pre>
	<p>Perhatikan disini ada perubahan dari string menjadi komposisi. Exp1 adalah komposisi yang selalu kita suntikkan sebagai pembentuk komposisi berikutnya.</p>
	<p>Method  getNextExpression saya maksudkan untuk menapatkan ekspresi berikutnya yang akan saya suntikkan ke dalam komposisi.</p>

private int getTotalOperator(string ekspresi) 
{ 
	string[] opr=ekspresi.Split(PLUS,MINUS); 
	return opr.Length-1; 
}

Perhatikan, getTotalOperator sama persis dengan yang ada didalam test. Jadi, saya akan membuat test semua asumsi yang tidak jelas dalam kepala saya menjadi code yang lebih jelas. Hasilnya bisa langsung kita kopikan. Ini lebih aman dari pada saya buat didalam program.

Untuk kasus seperti ini biasanya para pengguna IDE melakukan debugging dengan melihat apa hasilnya didalam IDE pada saat program dirun. Disini kita hanya menukil sebagian kecil dari fungsi yang kita inginkan.

private string getNextExpression(int next,string ekspresi) 
{ 
	int oprIndex=getOperatorPosition(next,ekspresi); 
	return ekspresi.Substring(oprIndex+1); 
}

Method getNextExpression saya masksudkan untuk mendapatkan ekspresi berikutnya (subekspresi) seperti yang telah saya jelaskan diatas. Jadi saya harus mendapatkan index ke-n dari operator tersebut. Dari situ kemudian dipotong sampai ke belakang.

private int getOperatorPosition(int number,string input) 
{ 
	string[] opr=input.Split(PLUS,MINUS); 
	int result=0; 
	for(int i=0; i<number;i++) 
	{ 
		result +=opr&#91;i&#93;.Length; 
	} 
	result +=number-1; 
	return result; 
}
&#91;/sourcecode&#93;
	<p>Perhatikan, sama persis dengan yang kita riset.</p>
	<p>Nah, bagaimana dengan komposisi,</p>

private IExpression composeOperation(IExpression exp1,string ekspresi) 
{ 
	IExpression exp2=getExp2(ekspresi); 
	return getOperator(ekspresi,exp1,exp2); 
}

Ternyata sederhana saja dan merupakan kopian dari code sebelumnya he he he. Jadi sebetulnya saya hanya memotong bagian yang mesti diulang-ulang. Itu saja.

Sekarang kita jalankan test. Ups!! Pesan kesalahannya : Expression 0 not operator.

Pesan ini mengindikasikan kegagalan untuk mendapatkan operator. Sayangnya tidak jelas operataor keberapa. Buntu.

Para newbie dalam TDD biasanya akan membuat applikasi konsole, lalu membuat method Main(). Membuat code yang bersesuaian didalam Main. Menandaiknya dengan tanda merah. Kemudian menjalankan program. Ketika tanda kuning sudah jatuh didalam tanda merah. Kursor mouse diarahkan disalah satu variabel kemudian dilihat value-nya. Setelah itu ambil keputusan. Diubah dan diulang lagi.

Saya tidak akan mengajarkan seperti itu. Saya lebih suka meletakkan Console.Writeline() kemudian melihat hasilnya di console NUnit. Setelah semuanya selesai, Console.Writeline itu akan saya hapus.

Jadi saya akan letakkan Console.WriteLine didalam method getOperator,

private IExpression getOperator(string ekspresi,IExpression exp1, IExpression exp2) 
{ 
	string afterExp1=ekspresi.Substring(exp1.Expression.Length).Trim(); 
	Console.WriteLine("exp1 {0}, exp2 {1}, ekspresi {2}, afterExp1 {3}", 
	                  exp1.Expression,exp2.Expression,ekspresi,afterExp1); 
	string opr=afterExp1.Substring(0,1); 
	if(opr.Equals(PLUS.ToString())){ 
		return new Plus(exp1,exp2); 
	}else 
		throw new ApplicationException(String.Format("Expression {0} not operator",opr)); 
}

Output dari console ini adalah:

	exp1 10, exp2 20, ekspresi 10 + 20 + 30 + 40 + 51, afterExp1 + 20 + 30 + 40 + 51 
	exp1 10 + 20, exp2 30, ekspresi  20 + 30 + 40 + 51, afterExp1 0 + 40 + 51 

Dari data diatas, terlihat kita gagal dilooping kedua. Baik exp1 dan exp2 serta subekspresi sudah benar. Tetapi logika untuk mendapatkan operator berikutnya salah.

Sebelumnya, untuk mendapatkan operator kita lakukan dengan memotong subekspresi dengan ekspresi pertama sehingga harapannya afterExp1 tinggal + 30 + 40 + 51. Karena ekspresi pertama kini bukan lagi ekspresi tunggal tapi sudah komposisi, akibatnya pemotongannya terlalu panjang.

Maka, tidak ada cara lain kecuali mengubah algoritma pemotongan. Pertama, kita harus mendapatkan posisi operator pertama dalam subekspresi. Untungnya kita telah memiliki method untuk mendapatkan posisi itu. Index posisi yang dihasilkan kemudian kita pakai untuk mendapatkan operator. Jadi saya ubah menjadi,

private IExpression getOperator(string ekspresi,IExpression exp1, IExpression exp2) 
{ 
	int oprIndex=getOperatorPosition(1,ekspresi); 
	string opr=ekspresi.Substring(oprIndex,1); 
	Console.WriteLine("exp1 {0}, exp2 {1}, ekspresi {2}, afterExp1 {3}", 
	                  exp1.Expression,exp2.Expression,ekspresi,ekspresi.Substring(oprIndex)); 
	 
	if(opr.Equals(PLUS.ToString())){ 
		return new Plus(exp1,exp2); 
	}else 
		throw new ApplicationException(String.Format("Expression {0} not operator",opr)); 
}

Perhatikan angka 1 dalam method getOperatorPosition(1,ekspresi). Jadi saya hanya ambil posisi yang pertama. Console out yang dihasilkan,

	exp1 10, exp2 20, ekspresi 10 + 20 + 30 + 40 + 51, afterExp1 + 20 + 30 + 40 + 51 
	exp1 10 + 20, exp2 30, ekspresi  20 + 30 + 40 + 51, afterExp1 + 30 + 40 + 51 
	exp1 10 + 20 + 30, exp2 40, ekspresi  30 + 40 + 51, afterExp1 + 40 + 51 
	exp1 10 + 20 + 30 + 40, exp2 51, ekspresi  40 + 51, afterExp1 + 51 

Setelah perubahan ini afterExp1 menunjukkan nilai yang benar. Jadi cukup memotong satu karakter diujung, didapatlah operator.

Console.WriteLine saya hapus. Semua test berjalan dengan benar.

Iklan

There are no comments on this post.

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: