Eclipse Altında Birim ve Entegrasyon Testleri Nasıl Ayrı Koşturulur?

Çalıştığım projede aynı sınıfın entegrasyon ve birim testleri aynı paketin içinde yer alıyor. Maven ile birim testlerini yapıdan (build) önce, entegrasyon testlerini yapı ve deployment sonrası koşturmak mümkün. Bu şekilde testler ayırt edilebiliyor. Lakin Eclipse altında testleri koşturmak istediğim zaman, doğal olarak JUnit plugin testleri ayırt etmeden, beraber koşturuyor. Geliştirme esnasında bu sıkıntılı bir durum, çünkü sadece birim testlerini koşturarak, seri bir şekilde geribildirim almak istiyorum.

Bu durumu çözmek için şöyle bir TestSuite sınıfı oluşturulabilir:

[sourcecode language=’java’]
@RunWith(Suite.class)
@SuiteClasses({ MyClassTest.class, MySecondClassTest.class })
public class AllTests {

}
[/sourcecode]

AllTests sınıfı koşturulduğu taktirde, MyClassTest ve MySecondClassTest isimli testler koşturulur. Tüm birim testlerinin @SuiteClasses anotasyonunda belirtilmesi gerekiyor. Yüzlerce test sınıfının olduğu bir uygulamada birkaç birim testinin AllTests sınıfına unutularak, eklenmeyeceği aşikar. Bu yüzden bu çözümü uygun bulmuyorum.

Junit-Toolbox yardımı ile TestSuite bünyesinde koşturulacak testleri ayırt etmek mümkün. Aşağıda bunun bir örneğini görmektesiniz.

[sourcecode language=’java’]
import org.junit.runner.RunWith;

import com.googlecode.junittoolbox.ParallelSuite;
import com.googlecode.junittoolbox.SuiteClasses;

@RunWith(ParallelSuite.class)
@SuiteClasses({ “**/*Test.class”, “!**/*IntegrationTest.class”,
“!**/*RestTest.class” })
public class FastUnitTestSuite {
}
[/sourcecode]

FastUnitTestSuite bünyesinde sadece ismi Test.class ile biten testler koşturuluyor. Bunlar projede yer alan birim (unit) testleri. Entegrasyon testlerinin isimleri *IntegrationTest.class şeklinde oldugundan, !**/*IntegrationTest.class ile bu testlerin koşturulması engellenmiş oluyor.

Junit-Toolbox projesini şu şekilde Maven projesine dahil etmek mümkün.

[sourcecode language=’java’]

com.googlecode.junit-toolbox
junit-toolbox
1.8

[/sourcecode]


EOF (End Of Fun)
Özcan Acar

20 Yaş, 30 Yaş, 35 Yaş ve Bugünki Programcı Ben

Bu yazıyı kaleme alırken Can Yücel’in o güzel yazısından esinlendim. Onun yaptığı gibi yirmi, otuz ve otuz beş yaşında olan benleri coding dojo ya davet ettim. Maksat biraz yazılım üzerine sohbet etmekti. Ortaya çıkan bu oldu.

Birlikte kod kata yapalım, eşli programlama süper dedim.
35 yaşımdaki ben iyi olur, ben varım dedi.
30 yaşımdaki ben, ilgi alanıma giriyor, öğrenmek isterim dedi.
20 yaşımdaki ben, ilgimi çekmiyor, oyun oynayabilir miyiz diye sordu.

Eşleşdik ve birlikte stack katayı yapmaya başladık.
35 yaşımdaki ben, 20 ve 30 yaşındaki benlerin yazdığı kodları hiç beğenmedi. Kendisi interface bazlı programlamayı ve temiz kod (clean code) yazmayı yeğliyormuş.
Baktım kimse test yazmamış. Test güdümlü yazılım nasıl yapılır, gösterdim. 35 yaşımdaki ben, biliyorum, ama her zaman mümkün değil işte dedi. Her zaman mümkün olduğunu göstermeye çalıştım.
30 yaşındaki ben, uygulama kodundan daha çok test kodu yazdın diye bana çıkıştı. Müşterinin bunu istediğinden emin değilim dedi.
20 ve 30 yaşındaki benler kim daha uzun metot yazar yarışına girdiler. 35 yaşındaki ben onların yazdıkları kodları yeniden yapılandırma metotlarıyla okunur hale getirdi. 20 ve 30 yaşındaki benlerin yeni kodu okuduklarından ağızları açık kaldı.

Öğle arası verdik, laflıyoruz.
20 yaşındaki ben sabahlara kadar kod yazdığını, bundan büyük zevk aldığını söyledi. Düzenli bir hayatının olmadığı belliydi.
30 yaşındaki ben çok oturmaktan dolayı sırt ağrılarından şikayet etmeye başladı.
35 yaşındaki ben sırt agrılarına öğle arası yürüyüşlerinin iyi geldiğini, yürüyüşleri düzenli olarak yaptığını söyledi.
Bana da düzenli olarak spor yapıp, yediğime, içtiğime, düzenli uyumaya dikkat ettigimi söylemek kaldı. 20 sene önce bilgisayar başında sabahlardım. Bunun yaşlanınca sürdürülebilir bir durum olmadığını, mesai saatlerinde verimli olmak için işin ve özel hayatın ayrılması gerektiğini belirttim. Sağlam kafanın sağlam vücutta olduğunun bilincine varmıştım.

20 yaşındaki ben en kral dilin Java olduğunu iddia etti. Yeni, yeni öğrenmeye başlamıştı. Başka bir dil bilgiği de yoktu.
30 yaşındaki ben de işi Java dili ile götürüyorum dedi. Başka bir dil öğrenmenin gerekli olduğunu düşünmüyorum dedi.
35 yaşındaki ben ikisine de kızdı. İyi bir programcı olmak için birden fazla dili bilmek lazımdır dedi.
Ben 35 yaşındaki bana katıldığımı söyledim. 20 yaşındaki ben çok biliyorsunuz dedi. Ölene kadar Java ile program yazabileceğimi size göstereceğim dedi. Bundan bu kadar emin olma dedim. İlgi alanların değişebilir.

20 yaşındaki ben kitaplığımdaki kitapları ve masamdaki düzenekleri görünce, sen programcı mısın, yoksa elektronikçi misin diye sordu?
Bu sorunun cevabını 20 sene sonra alacaksın dedim. O kendisinin programcı olduğunu ve öyle kalacağını söyledi. Kod yazmaktan başka hiçbir şeyin onu ilgilendirmediğini söyledi. İyi bir programcı olabilmek için programcılık haricinde ilgi alanları geliştirmesi gerektiğini henüz bilmiyordu.

Kim donanımdan anlar diye sordum. 30 yaşındaki ben, işim olmaz, ben programcıyım dedi. 35 yaşımdaki benin bu konuda fikri değişikti. İyi bir programcı olabilmek için donanımdan ve özellikle mikro işlemcilerin içinde olup, bitenlerden haberdar olunmalı fikrindeydi, ama nereden başlayacağı konusunda fikri yoktu. Onlara mikro denetleyicilere göz atmaları tavsiyesinde bulundum. İçlerinde elektroniğe karşı bir ilgi varsa, micro denetleyiciler onlara çok başka bir dünyanın, programcılığı da içine alan bir dünyanın kapılarını açacaktı. İçeri girip, girmeyeceklerine kendilerine kadar vereceklerdi, ama şimdi en azından kapının nerede olduğunu biliyorlardı.

35 yaşım kendisini mühendis gibi değil, daha çok zanaatkar gibi gördüğünü söyledi. Bir usta-çırak ilişkisinden gelmemenin ezikliğini çektiğini söyledi.
30 yaşım ben bilgisayar mühendisiyim, bir mühendisim, boşuna okumadım ben dedi.
20 yaşım ben bilgisayar mühendisi olmak istiyorum dedi. İyi bir maaş alabilmek ve kariyer yapabilmek için bu şart dedi.
Yazılım hem mühendislik, hem zanaat, çözüm üreterek mühendis, çözümü koda dökerek zanaatkar oluyoruz dedim. Programcı olarak usta-çırak geleneğinden gelmediğimiz için zanaatkar tarafımızın eksik olduğunu, bu yüzden kötü programlar yazdığımızı, ama pratik yaparak, bu sorunun üstesinden gelebilecegimizi söyledim.

Programcı yeteneklerinizi nasıl geliştiyorsunuz diye sordum.
35 yaşım kod katalarını keşfettiğini ve bu şekilde mesai dışında pratik yaptığını söyledi.
30 yaşım mesai çerçevesindeki aktivitelerinin iyi bir programcı olmak için yeterli olduğu görüşündeydi. Katalardan ve pratik yapmaktan bihaberdi.
Kodkata.com a bakmalarını tavsiye ettim. Pratik yapmanın daha iyi bir programcı olmak için gerekli olduğunu biliyordum.

35 yaşıma diğer ikisini nasıl bulduğunu sordum.
Çaylak bunlar, bir şeyden anlamıyorlar dedi.
Kendisini nasıl bulduğumu sordu.
Olacak, doğru yoldasın, ilgi alanlarını çoğaltmaya bak, her şey program yazmak değil dedim. İyi bir programcı olabilmek için edebiyat kitapları okumanın, kendi ana diline hakim olmanın, elektronikte uğraşmanın, spor yapmanın ve bunun gibi programcılıkla ilgisi olmayan birçok aktivitenin gerekli olduğunu anlamaya başlamıştım. Bunları 20 ve 30 yaşındaki benlere anlatmanın faydası olmayacağını biliyordum, çünkü onlar tek bir programlama dilinin insanlarıydı.

Ben ne düşünüyordum onlar hakkında;

35 yaşım yazılımda bir üst seviyeye çıkmak için çaba sarfediyor, ama lazım gelen kapıları keşfetmekte zorlanıyordu. Ama programcılığın diğer yönleriyle de ilgilenmeye başlamıştı. Er ya da geç o kapıları keşfedecekti. Bunun için çok çalışması gerektiğini biliyor muydu?

30 yaşım yazılımın sadece kod yazmaktan ibaret olduğunu zannediyordu. Tek bir dille işi götürebileceğini düşünmesi, onu sadece uzmanlığa mı götürecekti?

20 yaşım programcılığa en uzak mesafede olandı. Bu kafayla iyi bir programcı olabilecek miydi?

Peki kendi hakkımda ne düşünüyorum;

Bildiğim bir şey varsa o da hiç bir şey bilmediğimdir (Sokrates).

Çok değişik düşüncelere sahip dört insan bir araya gelmişti. Programcılığın ne olduğu konusunda herkesin fikri farklıydı. Onları çağırdığıma pişman mı olmuştum. Belki de….

Bende kabahat.
Ne çağırıyorsun tanımadığın adamları evine …

(Not: Son iki cümle Can Yücel ustanın şiirinden alıntıdır.)


EOF (End Of Fun)
Özcan Acar

JVM Nasıl Çalışır Yazı Serisi – JVM Stack Nedir ve Nasıl Çalışır?

Java sanal makine bünyesinde (JVM – Java Virtual Machine) vücut bulan her thread ile birlikte thread e özel ve stack ismini taşıyan bir hafıza alanı oluşturulur. Stack bünyesinde thread in içinde bulunduğu (koşturduğu) metotlardaki lokal değişkenler, yapılan işlemler için gerekli parametreler (operand), işlem sonuçları ve metot giriş (invocation) ve çıkış (return) bilgileri yer alır.

Bu bilgiler frame ismini taşıyan yapılar içinde tutulurlar. Her metot girişinde yeni bir frame oluşturulur. Aşağıda yer alan resimde program akışı main()->metot1()->metot2()-metot3() şeklindedir. Program metot3() bünyesinde bir breakpoint yardımı ile durdurulduğunda, main thread için dört adet frame in oluşturulduğu görülmektedir.

Aşağıdaki resimde stack yapısını görselleştirmeye çalıştım.

Güncel (current) thread in girdiği her yeni metot ile yeni bir frame oluşturulur demiştim. Her frame bünyesinde metot değişkenlerinin yer aldığı bir array, işlem parametrelerini (operand) ve sonuçlarını bünyesinde tutan bir LIFO (last in, first out) stack ve sınıfın runtime constant pool (sınıf bünyesinde kullanılan değişken isimleri, metot ve diğer sınıf referanslarının yer aldığı bir veri yapısı) yapısına bir referans yer alır.

JBE (Java Bytecode Editor) ile oluşturduğumuz Java sınıfını decompile ettiğimizde, lokal değişkenlerin derlenen sınıf bünyesinde bir array içinde tutuldukları ve index numarası üzerinden stack işlemlerinde bu lokal değişkenlere erişilebileceğini görmekteyiz.

Şimdi bir metot bünyesinde stack işlemlerinin nasıl işlediğini bir örnek üzerinde birlikte inceleyelim. Stack işlemlerini incelediğimiz sınıf aşağıda yer almaktadır:

[sourcecode language=’java’]
public class Main {

public static void main(final String[] args) {

metot1();
}

private static void metot1() {
metot2();
}

private static void metot2() {
final int x = 1;
metot3(x);
}

private static void metot3(final int x) {
final int y = 6;
final int i = x + y;
System.out.println(i);
}

}
[/sourcecode]

Bu sınıfı decompile ettiğimizde, aşağıdaki bytecode yapısını elde edebiliriz:

[sourcecode language=’java’]
* ******************************************** */
/* Generated by Dr. Garbage Bytecode Visualizer */
/* http://www.drgarbage.com */
/* Version: 4.4.1.201408050542 */
/* Class retrieved from: Filesystem */
/* Retrieved on: 2015-01-08 12:30:25.220 */
/* ******************************************** */

/* class file format version 50.0 (java 1.6) */
package com.kodkata.kata.jvm;

public class Main {

/* compiled from Main.java */

public Main() {
/* L3 */
0 aload_0; /* this */
1 invokespecial 8; /* java.lang.Object() */
4 return;
}

public static void main(java.lang.String[] args) {
/* L7 */
0 invokestatic 16; /* void com.kodkata.kata.jvm.Main.metot1() */
/* L8 */
3 return;
}

private static void metot1() {
/* L11 */
0 invokestatic 21; /* void com.kodkata.kata.jvm.Main.metot2() */
/* L12 */
3 return;
}

private static void metot2() {
/* L15 */
0 iconst_1;
1 istore_0; /* x */
/* L16 */
2 iconst_1;
3 invokestatic 24; /* void com.kodkata.kata.jvm.Main.metot3(int arg0) */
/* L17 */
6 return;
}

private static void metot3(int x) {
/* L20 */
0 bipush 6;
2 istore_1; /* y */
/* L21 */
3 iload_0; /* x */
4 bipush 6;
6 iadd;
7 istore_2; /* i */
/* L22 */
8 getstatic 30; /* java.lang.System.out */
11 iload_2; /* i */
12 invokevirtual 36; /* void println(int i) */
/* L23 */
15 return;
}
}
[/sourcecode]

metot3() bünyesinde yer alan sıfırıncı satırda bipush 6 komutu yer almaktadır. Bu komut operand stack e 6 değerini ekler (push). Frame içinde yer alan operand stack ve lokal değişkenlerin yapısı bu işlemden sonra aşağıdaki şekildedir:

İkinci satırda istore_1 komutu yer almaktadır. Bu komut lokal değişken tablosunda 1 numaralı index değerine sahip y değişkenine operand stack bünyesinde yer alan 6 rakamını pop işlemini gerçekleştirerek, atar. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Üçüncü satırda yer alan iload_0 komutu ile 0 index numarasına sahip değişkenin, bu x isimli metot parametresidir, değeri push işlemi ile operand stack e eklenir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Operand stack bünyesinde 1 degeri yer almaktadir, çünkü bu deger metot2() bünyesinde olusturulup, metot3() metoduna parametre olarak verilmiştir.

Dördüncü satırda bipush 6 komutu ile y isimli değişkenin değeri push işlemi ile operand stack e eklenmektedir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Altıncı satırda yer alan iadd komutu operand stack bünyesinde yer alan iki değeri pop komutu ile alarak, toplama işlemini gerçekleştirdikten sonra işlem sonucunu push komutu ile tekrar operand stack e eklemektedir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Yedinci satırda yer alan istore_2 ile operand stack in en tepesinde yer alan değer 2 index numarasına sahip değişkene, yani i değişkenine atanmaktadır. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Sekizinci satırda yer alan getstatic ile runtime constant pool bünyesinde yer alan 30 numaralı değişmezin (constant) değeri operand stack e eklenmektedir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

On birinci satırda yer alan iload_2 komutu ile i isimli değişkenin sahip olduğu değer operand stack e eklenmektedir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

On ikinci satırda yer alan invokevirtual runtime constant pool icinde yer alan 36 numaralı değişmeze işaret etmektir. Bu java.io.PrintStream sınıfında yer alan println metodudur. invokevirtual komutu ard, arda iki sefer pop işlemini gerçekleştirerek, operand stack üzerinde yer alan değerleri alır ve System.out.println(7) ile istenilen işlemi gerçekleştirir. Bu işlemin ardından frame aşağıdaki yapıya sahiptir.

Görüldüğü gibi JVM bir stack makinesidir, çünkü bytecode komutları ihtiyaç duydukları değerleri operand stack aracılığı ile elde etmektedirler. Bu komutların sade bir yapıda olmalarını mümkün kılmaktadır.

On beşinci satırda return komutunu görmekteyiz. Bu komut ile metot son bulmakta ve oluşturulan frame yok edilmektedir. Eğer metot herhangi bir değer geri veriyorsa, bu değer yine metodu koşturan diğer metodun operand stack ine eklenmektedir. Bu şekilde koşturucu metot koşturduğu metot bünyesinde oluşan değere kendi operand stack i aracılığı ile erişebilmektedir.

Her thread için oluşturulan stack belli bir hafıza alanını işgal etmektedir. Bu hafıza alanının büyüklüğü mikro işlemci mimarisine ve bus genişliğine göre 320K ile 1024K arasında değişmektedir.

Stack büyüklüğü şu şekilde ayarlanabilmektedir:

[sourcecode language=’java’]
java -server -Xss64k
[/sourcecode]

Stack için hafıza alanı yetmediği durumlarda StackOverflowException hatası oluşmaktadır. Bu tür hatalar genelde rekursiv çalışan ve son bulmayan metotlarda meydana gelmektedir. Eğer stack için bir hafıza limiti konulmamışsa, mevcut hafıza tüketilene kadar frame yapıları oluşturulabilir. Hafıza kalmaması durumunda OutOfMemoryError hatası oluşur.

Java dili call by value mantığı ile çalışmaktadır. Herhangi bir metot koşturulmadan önce, metoda gönderilen tüm parametreler koşturulacak yeni metodun stack ine kopyalanır. Bu işlem hem basit veri tipleri (int, long..) hem de referans veril tipleri için geçerlidir. Çoğu zaman Java dili için bir metoda parametre olarak gönderilen bir nesne referansının kopyalanmadığı, call by reference mantığı ile metoda aktarıldığı düşünülmektedir. Java’da referanslar bile metot girişlerinde kopyalanmaktadır.

Java’da referansların bile call by value mantığı ile işlediğini aşağıdaki örnekte görmekteyiz.

[sourcecode language=’java’]
private static void metot2() {
final Main m1 = new Main();
System.out.println(m1);
metot3(m1);
System.out.println(m1);
}

private static void metot3(Main m) {
m = new Main();
System.out.println(m);
}
[/sourcecode]

Bu metotları koşturduğumuzda, ekran çıktısı aşağıdaki şekilde olacaktır:

[sourcecode language=’java’]
com.kodkata.kata.jvm.Main@7150bd4d
com.kodkata.kata.jvm.Main@6bbc4459
com.kodkata.kata.jvm.Main@7150bd4d
[/sourcecode]

Eğer Java call by reference mantığı ile çalışmış olsaydı, ekran çıktısının şu şekilde olması gerekirdi, çünkü metot3() bünyesinde metot parametresi olan m isimli referansa yeni bir nesne atadık:

[sourcecode language=’java’]
com.kodkata.kata.jvm.Main@7150bd4d
com.kodkata.kata.jvm.Main@6bbc4459
com.kodkata.kata.jvm.Main@6bbc4459
[/sourcecode]

Lakin metot2() bünyesinde oluşturulan m1 isimli referans metot3() stack ine kopyalandığı için, metot3() bünyesinde bu referansa yaptığımız yeni bir atama metot2() bünyesindeki m1 referansını etkilememektedir.

Java uygulamalarında oluşan hataları java.lang.Throwable ya da türevleri temsil eder. Bu sınıf bünyesinde yer alan printStackTrace() metodu aracılığı ile hata oluşan yere kadar gelinen stack yapısına ulaşmak mümkündür. Bunun bir örneğini aşağıda görmekteyiz.

[sourcecode language=’java’]
java.lang.ArrayIndexOutOfBoundsException: 3
at com.kodkata.jvm.TestTry.execute(Testtry.java:17)
at com.kodkata.jvm.TestTry.main(Testtry.java:11)
[/sourcecode]

Bu stacktrace örneğinde iki stack frame yer almaktadır. İlk frame TEstTry sınıfının main() metodu için, ikinci frame bu metot bünyesinde execute() metodu koşturulduğu için oluşturulmuştur. Stacktrace bünyesinde oluşturulan her iki frame yer almaktadır. execute() metodunun on yedinci satırında ArrayIndexOutOfBoundsException hatasının oluştuğunu görmekteyiz.


EOF (End Of Fun)
Özcan Acar

JVM Nasıl Çalışır Yazı Serisi – Java Dilinde Neden Göstergeçler (Pointer) Yok?

Java’da göstergeçler var, ama C dilinde olduğu şekilde işlemiyorlar. Bu yazımda Java’da göstergeçlerin neden olmadığını aktarmaya çalışacağım.

C dilinde aşağıdaki şekilde bir göstergeç tanımlaması yapilabilmektedir:

[sourcecode language=”java”]

int i, *ptr;
ptr = &i;
*ptr=10;
[/sourcecode]

Bu örnekte i isminde ve int veri tipinde bir değişken ve yine int veri tipine sahip ve ptr isminde bir göstergeç tanımladık. C dilinde göstergeçler bir hafıza alanına işaret ederler. Yukarıdaki örnekte ptr in sahip olduğu değer i değişkeninin hafızadaki adresidir. ptr üzerinde yapılan her işlem i değişkenini etkileyebilir, çünkü ptr dolaylı olarak i değişkeninin sahip olduğu değere işaret etmektedir. Göstergeç aritmetiği sayesinde hafıza alanlarını doğrudan adreslemek ve o hafıza alanlarında yer alan değerler üzerinde işlem yapmak mümkündür.

Java’da göstergeç tanımlamaları ve hafıza alanları üzerinden doğrudan işlem yapmak mümkün değildir, çünkü:

  • Java sanal makinesi (Java Virtual Machine) hafıza alanını kendisi yönetir.
  • Java’da array ve list nesnelerinin uzunlukları JVM tarafından kontrol edilir. Olmayan array elemanları üzerinde işlem yapmaya çalışmak ArrayIndexOutOfBoundsException hatasını oluşturur.
  • Java dilinde array dilin bir parçasıdır ve string işlemleri için String sınıfı kullanılır. C dilinde göstergeçler aracılığı ile bu yapılar oluşturulur ve dilin bir parçası değildirler.
  • Java nesne referanslarını JVM bünyesinde yer alan garbage collector aracılığı ile yönetir.

Java dilinde göstergeçler sadece nesnelere işaret eden değişken isimleri olarak kullanılırlar, ama:

  • Bu değişkenler üzerinde göstergeç aritmetiği yapılamaz
  • Sadece nesnelere işaret ederler, adres alanlarına değil

Java’da bu nesne göstergeçlerine referans ismi verilmektedir.

Aşağıdaki Java kodunu koşturduğunuzda NullPointerExceptin oluşmaktadır:

[sourcecode language=”java”]

Nesne nesne = null;
nesne.birMethod();
[/sourcecode]

Peki neden oluşan hatanın ismi NullPointerException seklindedir de NullReferenceException değildir? Bu sorunun cevabını bende bilmiyorum. Java’da NullReferenceException olmalıydı. NullPointerException sınıfının kaynak koduna baktığımızda, sınıfın kim tarafından geliştirildiği görememekle birlikte, JDK1.0 dan beri Java’nın bir parçası olduğunu görmekteyiz. Bu sanırım Java dilini oluşturanların C/C++ kökenli olmalarından kaynaklanıyor. Ama C# dilinde NullReferenceException isminde ve aynı vazifeyi gören bir sınıf mevcut. C# dil geliştiricileri aynı hatayı yapmamış.

[sourcecode language=”java”]

/**
* @author unascribed
* @version %I%, %G%
* @since JDK1.0
*/
public
class NullPointerException extends RuntimeException {
}
[/sourcecode]

Şüphesiz Java dilinde göstergeçlerin ve göstergeç aritmetiğinin olmaması Java’da geliştirilen kodun daha kolay anlaşılır olmasını sağlamaktadır. C kodu çok kısa zamanda göstergeçlerin yerli, yersiz kullanılmalarından dolayı kaotik bir yapıya bürünebilir. Ama bunun karşılığında göstergeçler yardımı ile donanıma çok yakın kalınarak, uygulama geliştirmek mümkündür. Java’da bu ne yazık ki mümkün değil. Sizinle donanım arasında her zaman JVM olacak.


EOF (End Of Fun)
Özcan Acar

Neden Kafam Bu Kadar Rahat?

Kişisel Gelişim başlıklı yazımda kullandığım metot ve araçlardan bahsetmiştim. Bu yazımda çok faydalı bulduğum bir aracı daha tanıtmak istiyorum.

Yazının devamını okumadan önce sizden şunu yapmanızı rica ediyorum: Kağıt kalem kullanarak ya da bilgisayarınızda bir editör açarak yapmanız gereken şeylerin listesini çıkarın. Bu liste içinde mesai bitiminde sonra bakkaldan alacağınız ekmekten, iki hafta sonraki diş hekimi randevunuza kadar her şey yer alabilir. Önemli olan aklınızda tutmaya çalıştıklarınızı listeye dökmenizdir. Siz listenizi oluştururken, ben bekliyor olacağım…..

Listenizi görmedim, lakin listenizde en az 5, en kötü ihtimalle 20 başlığın olduğunu tahmin edebiliyorum. Aynı şeyi ben de üç gün önce tekrar yaptım ve kafamda 15 değişik şeyin yapılmak üzere beklediğini gördüm ve şok oldum.

Her insan kafasında ortalama 10-15 şeyle gün içinde yapması gereken işlerin üstesinden gelmeye çalışıyor. Bunun ne kadar verimsiz bir girişim olduğunu kendi edindiğim tecrübelerden biliyorum. Kafaların böyle yapılacak işlerle yüklü olması, verimli bir şekilde bir işe odaklanılmasının önündeki en büyük engellerden birisi. Çoğu zaman nedense akla gelen daha önemli bir işten dolayı üzerinde çalışılan iş yarıda bırakılıyor. Bunun tek sebebini kafada yapılacak işlerin cirit atması olarak görüyorum.

Her aktiviteyi akılda tutmak, ne zaman neyin yapılması gerektiğini organize etmeye çalışmak beyin için çok yorucu bir işlem. Beyin devamlı böyle yarım kalmış aktivitelerle haşır neşir ve onların nasıl üstesinden geleceğinin planını yapıyor. Bundan kurtulmanın ve beyni rahatlatmanın en verimli yöntemi, yapılacak işlerin bir listesini oluşturmak ve beyinden bu organizasyon yükünü uzaklaştırmak.

Ben bu işlem için daha önce Kanban panonsu kullanıyordum. Geçenlerde Any.do isminde yeni bir uygulama keşfettim. Bu yazımda bu uygulamayı size tanıtmak istiyorum.

bbbb

Any.do hem web üzerinden hem de akıllı telefonda app olarak kullanabileceğiniz basit bir “yapılacak şeylerin listesi” uygulaması. Oluşturduğum listeye internet erişimi olan herhangi bir bilgisayardan ulaşabiliyorum. Bu şekilde listemi bir bilgisayardan başka bir bilgisayara taşımam gerekmiyor. Özellikle cep telefonları için hazırlamış uygulamanın çok faydalı olduğunu gördüm. Kanban panosunu kullanırken bunun eksikliğini hissetmiştim. Cep telefonumda bir app olmadığı için kanban panomda olup, bitenleri takip etmem zor oluyordu. Gözden ırak olan, gönülden ırak olur misali bir zaman sonra kanban panosunun bir kopyasını kafamda oluşturup, işlerimi yine eski yöntemlerimle takip etmeye çalışıyordum. Taaki Any.do ile tanışana kadar.

Aklıma yapmam gereken bir şeyler geldiğinde, hemen cep telefonum aracılığı ile aktivite listemde yeni bir kayıt oluşturuyorum. Cep telefonum devamlı yanımda olduğu için olup, bitenleri takim etmem kolaylaşıyor. Any.do gerekli uyarıları yaparak, işleri takip etmemi kolaylaştırıyor.

Any.do nun birde Chrom eklentisi mevcut. Özellikle gelen e-postaların başlıklarını yapılacak iş listesine kaydetmek birçok yazışmadan dogan işleri takip etmeyi çok kolaylaştırıyor.

aaa

Aklımda artık yapmam gereken şeyler konusunda hiçbir kayıt yok. Yapmam gereken işlerin izini beynim değil, cep telefonum ve kullandığım bilgisayarlar sürüyor. “Kafam çok rahat“ sözünün tam olarak ne anlama geldiğini şimdi anlamış oldum :)

Kendinize bir iyilik yapmak istiyorsanız, Any.do uygulamasana bir göz atmanızı tavsiye ederim. Bu konudaki tecrübelerinizi bizimle paylaşın.


EOF (End Of Fun)
Özcan Acar

Java Şampiyonluğu Nedir ve Nasıl Olunur?

Ben 2009 senesinde Java sampiyonu olarak seçildim. Bilişim sohbetleri söyleşimde Java şampiyonluğu hakkında bilgi vermeye çalıştım. Bu yazımda kısaca Java şampiyonluğunun ne olduğunu tekrarlamak ve nasıl Java şampiyonu olunabileceği konusuna değinmek istiyorum.

Dünya çapında yüz otuza yakın Java şampiyonu bulunuyor. Bu rakam aslında yüz altmış civarında, lakin bazı Java şampiyonları Oracle firması için çalışmaya başladıklarından dolayı, bu unvanı bırakmak zorunda kaldılar.

Java şampiyonları proje sayfasına baktığımızda, Java şampiyonu olabilmek için bir adayda aranan özellikleri görmekteyiz. Bunlar:

  • Liderlik vasfı: Java şampiyonları JUG (Java User Group) kurucusu ve yöneticisidirler ya da Java ile ilgili projelere katkıda bulunurlar.
  • Teknik bilgi: Java şampiyonları senior seviyesinde yazılımcı ya da mimardırlar. Java platformu hakkında geniş kapsamlı bilgi ve tecrübeye sahiptirler.
  • Bağımsız ve güvenilir: Java şampiyonlarının Oracle firmasına bağımlılığı yoktur. Bağımsız bir şekilde fikirlerini beyan ederler. Söyledikleri ciddiye alınır ve topluluk bünyesinde güvenilir şahıslar olarak tanınırlar.
  • Yazarlık ve egitmenlik: Java şampiyonları Java ve diğer yazılım konularında kitap ya da blog yazarlar, konferanslara konuşmacı olarak katılıp, üniversitelerde Java eğitimleri verirler.
  • Örnek olma: Java şampiyonları yaptıkları çalışmalar aracılığı (consulting, eğitmenlik, yazarlık ve konferans konuşmacılığı) ile sektör çalışanlarına örnek olurlar.

Türkiye’de bu özelliklere sahip birçok yazılımcı tanıyorum. Bunlardan bir tanesi Mert Çalışkan. Kendisi AnkaraJUG kurucusu ve bir senior Java developer. Daha önce Mert ile birlikte birçok konferansa konuşmacı olarak katıldık. Kendisi “bana Java şampiyonu olmak için sponsor olur musun” diye sorduğunda, tereddüt etmeden evet dedim. Bu şekilde Mert’in Java şampiyonu olma süreci başladı ve kısa bir zaman önce Java şampiyonu olarak seçildi.

Şimdi kısaca bu sürecin nasıl işlediğinden bahsetmek istiyorum. Yeni kurallara göre bir şahsın Java şampiyonu olabilmesi için mevcut bir Java şampiyonun bu şahsa sponsor olması gerekiyor. Sponsorun görevi Java şampiyonu adayını Java şampiyonları topluluğuna tanıtmak ve adaya bu süreçte destek olmak. Sponsor olan Java şampiyonunun bu desteği önemli, çünkü topluluk kendi içlerinden birisinin bu şahsa kefil olduğunu görmek istiyor. Eğer aday gerekli vasıflara sahip ise, yapılan oylama sonucunda adaylığı kabul görüyor ve Java şampiyonu olarak seçiliyor ya da adaylığı reddediliyor. Reddedilmenin başlıca sebebi, aranan vasıfların adayda olmaması. Ama aday olarak gösterilip de, Java şampiyonu seçilmeyeni görmedim.

Java şampiyonluğunu Microsoft firmasının MVP programı ile kıyasladığımızda, bu iki programın farklılıklar taşıdığını görmekteyiz. Java şampiyonu unvanını alan bir şahıs, bu unvanı Oracle firması için çalışmadığı ve Java alanında aktif kaldığı sürece bir ömür boyu taşıyabilir. MVP ler her sene yeniden seçilmektedirler. MVP ler daha ziyada Microsoft firmasının ürünlerine konsantre olurken, Java şampiyonları Java platformunun tümünü kapsayabilecek çalışmalar yaparlar. 2014 senesi itibariyle dünya çapında 4000 e yakın MVP bulunuyor. Aynı rakam Java şampiyonları için 130 civarında ve bu rakamın 1000 ile sınırlanması planlanıyor. İki program arasındaki en belirgin özellik ise şu: MVP leri doğrudan Microsoft firması seçerken, Java şampiyonlarını bu unvanı taşıyan topluluk üyeleri seçmektedir. Oracle firmasının Java şampiyonu seçimlerinde söz hakkı bulunmamaktadır. Ayrıca Oracle firması için çalışmaya başlayan bir Java şampiyonunun Java şampiyonluğu son bulmaktadır.

Buraya kadar Java şampiyonluğunun ne olduğu konusunda değindim. Yazımın bundan sonraki bölümünde nasıl Java şampiyonu olunur sorusuna cevap vermek istiyorum.

Java şampiyonluğuna giden yol uzun olabilir. Bu yolu çok kısa bir zamanda kat etmenin tek yolu, Java ve yazılım konusunda tutku sahibi olmaktır. Bu ve aşağıdaki özellikler bir araya geldiğinde, Java şampiyonu adayı olmak için fazla bir engel kalmamış demektir.

  • Java şampiyonu adayı mutlaka Java konularını işleyen bir blog sahibi olmalıdır. Bu adayın bilgi paylaşımına değer verdiğini gösteren en belirgin özelliktir. Aday düzenli aralıklarla blog yazarak, bilgi paylaşımını istikrarlı bir şekilde sürdürmelidir.
  • Aday edindiği tecrübeleri bir kitap haline getirip, yayımlanmasını sağlamalıdır. Bu onun uzmanlık alanına işaret eden ve tecrübe seviyesini gösteren bir durumdur.
  • Aday bulunduğu şehirde bir JUG (Java User Group) kurmalı ve düzenli aralıklarla toplantılar düzenleyerek, topluluğa önderlik etmelidir. Bu onun Java’nın tanıtılması ve yayılmasına verdiği önemi gösterir. Eğer bulunduğu şehirde bir JUG varsa, aday yeni toplantıların düzenlenmesine yardımcı olarak, topluluğun büyümesine katkıda bulunmalıdır.
  • Aday konferans ve üniversite toplantılarına konuşmacı olarak katılarak, Java’nın tanıtılmasına katkıda bulunmalıdır. Sadece bir toplum önünde bir konuyu sunabilen, bu konu hakkında derin bilgi sahibi olabilir.
  • Aday okul ve üniversitelerde seminerler düzenleyerek, ilgi duyanlara Java dilini ve platformunu öğretmelidir. Adayın Java ve yazılım konusunda eğitime verdiği destek, onun bu konuda ne kadar tutku sahibi olduğunun göstergesidir.
  • Aday herhangi bir açık kaynaklı Java projesine yazılımcı olarak katkıda bulunmalıdır. Bu onun açık kaynaklı Java projelerine verdiği önemi gösterir.

Bu listede yer alan kriterleri yerine getirmek için beş ila on senelik bir çalışma söz konusu olabilir. Kanımca usta bir yazılımcı olmak için gerekli süre de on sene gibi bir zaman dilimidir.

Yazımın başında da belirttiğim gibi, aday olabilmek için bir Java şampiyonunun sponsor olarak adayı desteklemesi gerekmektedir. Şimdi bana yüzlerce “bana da sponsor olur musunuz” e-postası gelmeden önce, benim bir adayda aradığım özelliklere kısaca değinmek istiyorum.

Benim için en önemli kriterden birisi, benim adayı şahsen tanıyor olmamdır. Sadece bu şekilde aday hakkında bir fikir sahibi olabilirim. Diğer önemli bir kriter, adayın Java ve yazılım konusunda ne kadar üretken olduğu konusudur. Bir adayın üretkenlik seviyesini düzenli olarak yazdığı bloglarda görmek mümkündür. Ayrıca adayın bir ya da birden fazla kitap yazmış olması, adayın yazılımı ne kadar ciddiye aldığının göstergesidir. Diğer önemli bir kriter, adayın hangi yazılım projelerinde yer aldığı ve hangi pozisyonda kariyerine devam ettiğidir. Adayın geniş çaplı yazılım projelerinde yer almış olması, yazılım konusunda geniş tecrübe sahibi olması anlamına gelir. Adayın benlik gütmeyen yazılımcı olması da diğer önemli bir konu.

Java şampiyonu olmak zor değil. Bu konuda istikrarlı bir çalışma gerekiyor. Java şampiyonluğunu üstadlık ya da ustalık olarak görmemek lazım. Java şampiyonları daha ziyada yazılıma tutkuyla bağlı kişilerdir. Bu tutku onların üretken ve topluluğa iyi örnek olmalarını sağlayan en belirgin özellikleridir.

Ben şahsen Türkiye’den çok daha fazla Java şampiyonu çıkmasını istiyorum. Bu konuda üzerime düşeni yapacağım. Aklımda destek vermek istediğim birkaç isim var. Genç yazılımcıların da gelişim süreçlerine katkıda bulunarak, geleceğin Java şampiyonları olmalarını sağlayabilirsek, ne mutlu bize…

Güncelleme (16.4.2015):

Mert’in sponsor ve benim oylamada destek olduğum Murat Yener 5.4.2015 tarahinde Java Champion olarak seçildi. Kendisini tebrik ediyorum.


EOF (End OF Fun)
Özcan Acar

Kokan Kod Yazı Serisi – Giriş

Söz konusu yemek olduğunda, insanlar kötü kokuları yemeğin formda olmadığının ibaresi olarak algılarlar. Aç kalmadıkça kötü koku saçan bir yemeği kimse yemez. Kötü kok yemeğin hangi durumda olduğunu gösteren bir işarettir. İnsanlar kötü kokan bir yemeğin yenmemesi gerektiğini bilirler.

Yazılımda da koku (smell) metaforu yeniden yapılandırılmaya ihtiyaç duyan kodlar için kullanılmaktadır. Kent Beck tarafından ilk kullanılan koku terimi Martin Fowler tarafından yazılan Refoctoring kitabıyla yazılım camiasında yapısal deformasyon içinde olan ve programcılar tarafından anlaşılması zor kod birimleri için kullanılır hale gelmiştir.

Kokan kod ilk etapta program hatalarına işaret etmez. Daha ziyada kokan kod terimi çalışır durumda olmasına rağmen anlaşılması ve geliştirilmesi çok zor kod birimleri için kullanılır. Kokan kod birimlerinde yapılan her yeni ekleme ile hata (bug) ihtimali yükselir. Kokan kod genelde kodun daha derinlerdeki yapısal problemlerini perdeler. Bu problemleri kesletmek için kodun yeniden yapılandırılması zaruridir.

Tasarım şablonlarında (design patterns) olduğu gibi kod kokularına isim vermek, bu konuda ortak bir kelime hazinesi oluşmasını kolaylaştıracaktır. Kod kokusunun oluştuğu durumları şu şekilde sıralayabiliriz:

  • Kopyala/yapıştır yöntemiyle oluşmuş kod tekrarları (Duplicated Code)
  • Uzun metotlar (Long Method)
  • Birçok şeyden sorumlu büyük sınıflar (Large Class)
  • Uzun metot parametre listeleri (Too Many Parameters)
  • Temel veri tiplerine eğilim (Primitive Obsession)
  • İç içe geçmiş karmaşık if/else/for/do/while direktifleri (Cyclomatic Complexity)
  • Her şeyi yapmaya çalışan callback metotları (Ubercallback)
  • Aynı sınıf bünyesinde farklı değişiklikler yapma zorunluluğu (Divergent Changes)
  • Bir sınıf için diğer sınıflar bünyesinde farklı değişiklikler yapma zorunluluğu (Shotgun Surgery)
  • Bir metodun kendi sınıfındaki değişkenler yerine başka bir sınıfın değişkenleri ile ilgilenmesi (Feature Envy)
  • Switch komutunun kullanılması (Switch Statements)
  • Paralel kalıtım hiyerarşileri oluşturulması (Parallel Inheritance Hierarchies)
  • Sınıf olmayı hak etmeyen sınıflar (Lazy Class)
  • Birgün belki işimize yarar sendromu (Speculative Generality)
  • Belli şartlarda kullanılan sınıf değişkenleri (Temporary Field)
  • Kendisine gelen metot çağrılarını başka sınıf metotlarına delege eden aracı sınıflar (Middle Man)
  • Çok sıkı, fıkı olan sınıflar (Inappropriate Intimacy)
  • Aynı şeyi yapan, ama değişik arayüzlere sahip olan sınıflar (Alternative Classes with Different Interfaces)
  • İşini yapabilmek için değişikliğe ihtiyaç duyan kütüphane (3rd library) sınıfları (Incomplete Library Class)
  • Demeter kuralına ters düşen sınıflar (Message Chains)
  • Fonksiyon sahibi olmayan ve sadece veri tutan sınıflar (Data Class)
  • Miras aldıkları metot ve değişkenlerin tümüne ihtiyaç duymayan alt sınıflar (Refused Bequest)
  • Kötü durumdaki bir kod birimini açıklamak için oluşturulmuş yorumlar (Comments)
  • İç dünyasını arayüzünün (interface) parçası olarak dışa açan sınıflar (Indecent Exposure)
  • Bir şey ifade etmeyen sınıf, metot ya da değişken isimleri (Uncommunicative Name)
  • Uzun sınıf, metot ya da değişken isimleri (Long name)
  • Kısa sınıf, metot ya da değişken isimleri (Short name)
  • Gereksiz tasarım şablonu (design pattern) kullanımı (Contrived complexity)
  • Grup halinde sınıf değişkeni ya da metot parametresi olarak kullanılan nesneler (Data Clumps)

Kod kokuları ilk etapta teknik borçlanmaya işaret ederler. Kod kokularını yok etmek ve teknik borçlanmayı azaltmak için kullanılabilecek yöntemleri bu yazı dizisinde sizlerle paylaşmak istiyorum. Önümüzdeki günlerde listede yer alan her koku türünü inceleyen bir blog yazısı hazırlayacağım. Bu yazı dizisine giriş noktası olarak birinci bölüm niteliğinde olan bu blog yazısını kullanabilirsiniz.


EOF (End Of Fun)
Özcan Acar

Yazılımda Geviş Getirme Taktiği Nasıl Uygulanır?

Koyun, keçi, deve gibi hayvanlar otları çiğnemeden yutarlar. Daha sonra dinlenme esnasında yuttukları otları ağızlarına getirerek, çiğnerler. Buna işleme geviş getirme denir.

Geviş getirme hayvanların evrim sürecinde düşmanlarına karşı geliştirdikleri bir savunma mekanizmasıdır. Bu tür hayvanlar düşmanlarından kaçabilmek için buldukları besinleri çiğnemden yutarlar. Daha sonra kendilerini güvende hissettikleri bir yer ve anda çiğnemeden yuttukları bu besinleri geviş getirme yöntemiyle tekrar çiğnerler.

Bu yazımda geviş getirme mekanizmasının yazılımda nasıl kullanılabileceğini göstermek istiyorum.

Öncelikle yazdığımız kodların yazıcıdan çıktısını almamız gerekiyor. Daha sonra kağıtları küçük parçalara bölerek, çiğnemeden, yutuyoruz….. :)

İşin şakasını yaptıktan sonra, geviş taktiğine tekrar geri dönelim. Geviş getirme mekanizması iki kademeli işlemektedir. Birinci kademede hayvan çok hızlı bir şekilde otları dişleriyle kopararak, çiğnemeden yutar. İkinci kademede hayvan geviş getirerek, daha önce çiğnemeye zaman bulamadığı besinleri çiğner.

Şimdi kod yazma sürecini de bu iki kademeyle şekillendirmeye çalışalım. Birinci kademenin geviş getirme mekanizmasına göre hızlı bir şekilde, kodun hangi yapıda olduğu düşünülmeden yazılması gerekiyor. Ben bu kademede test güdümlü yazılım yaparak, kodu oluşturmayı tercih ediyorum. Amacım çalışır durumda olan testleri ve kodları oluşturmak. Birinci kademede kodun hangi durumda olduğu beni ilgilendirmiyor. Ot yiyen hayvanın yaptığı gibi kodu yazıyor ve kaçıyorum. Kaçıyorum, çünkü ikinci kademede kodu tekrar çiğneyerek, yani kodu yeniden yapılandırarak (refactoring), tekrar elden geçireceğim. Kaçabilmem için testlerin ve kodun çalışır durumda olması gerekiyor.

Şimdi bunun nasıl yapıldığını bir örnek üzerinde inceleyelim. Bir dosyada yer alan tüm kelimeleri, kaç kez kullanıldıklarını görecek şekilde bir dosyaya eklememiz gerekiyor. Örneğin dosyamız içerisinde şu satır olsun:

[sourcecode language=’java’]
bu bir test
[/sourcecode]

Elde edeceğimiz dosyanın içeriği şu şekilde olmalı:

[sourcecode language=’java’]
test: 1
bir: 1
bu: 1
[/sourcecode]

Her kelimenin dosya içinde kaç kez kullanıldığını görüyoruz. Bu kodu test güdümlü şu şekilde yazdım:

[sourcecode language=’java’]
package test.the.west;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.hamcrest.CoreMatchers;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class WordSorterTest {

private class FileNotFound extends RuntimeException {

private static final long serialVersionUID = 1L;

}

private class FileIsEmpty extends RuntimeException {

private static final long serialVersionUID = 1L;

}

private class SortError extends RuntimeException {

public SortError(final Exception e) {
super(e);
}

private static final long serialVersionUID = 1L;

}

private class WordSorter {

public void sort(final File file) {

if (!file.exists()) {
throw new FileNotFound();
}

List lines = null;

try {
lines = FileUtils.readLines(file);

if (lines.size() == 0) {
throw new FileIsEmpty();
}
} catch (final IOException e) {
throw new SortError(e);
}

final Map values = new HashMap<>();

for (final String line : lines) {

final String[] words = line.split(” “);

for (final String word : words) {

Integer value = values.get(word);
if (value == null) {
values.put(word, new Integer(1));
} else {
values.put(word, ++value);
}
}
}

final StringBuilder newContent = new StringBuilder();

final Set> entrySet = values.entrySet();
for (final Entry entry : entrySet) {
newContent.append(entry.getKey() + “:\t” + entry.getValue()).append(“\n”);
}

System.out.println(newContent);

try {
FileUtils.write(file, newContent);
} catch (final IOException e) {
throw new SortError(e);
}
}
}

@Test(expected = FileNotFound.class)
public void when_file_does_not_exits_error_is_thrown() throws Exception {

final WordSorter sorter = new WordSorter();
sorter.sort(new File(“xx”));

}

@Test(expected = FileIsEmpty.class)
public void when_file_is_emptry_error_is_thrown() throws Exception {

final File file = File.createTempFile(“myfile”, “.txt”);

final WordSorter sorter = new WordSorter();
sorter.sort(file);

}

@Test
public void words_are_sorted_by_count_and_written_to_the_same_file() throws Exception {

final File file = File.createTempFile(“myfile”, “.txt”);

FileUtils.write(file, getDummyContent());

final WordSorter sorter = new WordSorter();
sorter.sort(file);

final List lines = FileUtils.readLines(file);

assertThat(lines.size(), is(3));
assertThat(lines.get(0), is(CoreMatchers.equalTo(“test:\t1”)));
assertThat(lines.get(1), is(CoreMatchers.equalTo(“bir:\t1”)));
assertThat(lines.get(2), is(CoreMatchers.equalTo(“bu:\t1”)));

}

private String getDummyContent() {
return “bu bir test”;
}
}
[/sourcecode]

İşletme mantığı WordSorter sınıfının sort() isimli metodunda yer alıyor. İlk bakışta bu metot bünyesinde ne olup, bittiğini anlamak mümkün değil, çünkü kod testleri tatmin edecek seviyeden ileri gitmiyor, yani metot temiz kod (clean code) prensiplerine uygun değil. Tüm testlerimiz çalışır durumda ise, o zaman birinci kademeyi tamamlamış oluyoruz. Bu kademede gerisi bizi ilgilendirmiyor. Şimdi tekrar geviş getiren hayvanı hatırlayalım. O da ilk kademede dişleriyle otları koparıp, yuttuktan sonra geviş getirebileceği bir yere gider. Hayvan ilk kademede otun çiğnenmesi ile ilgilenmez. Biz de ilk kademede kodun çiğnenmesi, yani yeniden yapılandırılarak, daha okunur hale gelmesi ile ilgilenmiyoruz.

Şimdi geviş getirme zamanı. İkinci kademede kodu yeniden yapılandırarak (çiğneyerek), daha okunur hale getiriyoruz. Kodun yeni hali aşağıda yer almaktadır.

[sourcecode language=’java’]
private class WordSorter {

public void sort(final File file) {

checkFile(file);

final List lines = getLines(file);

final Map sortedKeys = new HashMap<>();

sortWords(lines, sortedKeys);

final String newContent = buildSortedContent(sortedKeys);

logSortedContent(newContent);

writeSortedContentToFile(file, newContent);
}

private List getLines(final File file) {
List lines = null;

try {
lines = FileUtils.readLines(file);
checkLines(lines);
} catch (final IOException e) {
throw new SortError(e);
}
return lines;
}

private void checkLines(final List lines) {
if (noLines(lines)) {
throw new FileIsEmpty();
}
}

private void checkFile(final File file) {
if (!file.exists()) {
throw new FileNotFound();
}
}

private void writeSortedContentToFile(final File file, final String newContent) {
try {
writeFile(file, newContent);
} catch (final IOException e) {
throw new SortError(e);
}
}

private void writeFile(final File file, final String newContent) throws IOException {
FileUtils.write(file, newContent);
}

private void logSortedContent(final String newContent) {
System.out.println(newContent);
}

private String buildSortedContent(final Map values) {
final StringBuilder content = new StringBuilder();

final Set> entrySet = values.entrySet();

for (final Entry entry : entrySet) {
content.append(entry.getKey() + “:\t” + entry.getValue()).append(“\n”);
}
return content.toString();
}

private void sortWords(final List lines, final Map values) {
for (final String line : lines) {

final String[] words = line.split(” “);
handleWords(values, words);
}
}

private void handleWords(final Map values, final String[] words) {
for (final String word : words) {

handleWord(values, word);
}
}

private void handleWord(final Map values, final String word) {
Integer value = values.get(word);
if (valueNull(value)) {
putWith(values, word, new Integer(1));
} else {
putWith(values, word, ++value);
}
}

private boolean valueNull(final Integer value) {
return value == null;
}

private void putWith(final Map values, final String word, final Integer value) {
values.put(word, value);
}

private boolean noLines(final List lines) {
return lines.size() == 0;
}
}
[/sourcecode]

Yeni yapılandırma kademesinde metotların 3-5 satırdan fazla olmamasına dikkat ediyorum. Bir metot bünyesinde ne kadar az satır varsa, kodu kavrama oranı o oranda artacaktır. sort() metodu şimdi okuyucusuna hangi işlemi gerçekleştirdiğini daha iyi anlatmaktadır.

Birinci kademede işletme mantığını çalışır hale getirmeye çalışırken, ikinci kademede kodu refactoring yöntemleriyle daha okunur hale getirmeye odaklanıyorum. Eğer birinci kademede kodu yazarken aynı zamanda okunur hale getirmeye çalışsaydım, o zaman tek sorumluluk prensibine ters düşmüş olurdum. Aynı zamanda sadece bir işlemle ilgilenmeliyim. Geviş getiren bir hayvan da geviş getirirken kalkıp, tekrar ot yemiyor. Hayvan geviş getirirken sadece bu işe konsantre oluyor ve başka bir işle uğraşmıyor. Bu onun ikinci kademede yuttuğu otları verimli bir şekilde çiğnemesini sağlıyor ki bizde aynı şekilde her iki kademede yapılan işlemleri birbirlerinden ayırt ederek, belli safhalarda belli işleri yapmaya odaklanıyoruz. Bu bizim yaptığımız işte verimliliğimizi artırıcı bir durum.

Ben bu şekilde çalışmayı tercih ediyorum. İki kademeli çalışmak, çok hızlı bir şekilde işletme mantığını kodlamamı sağlıyor. İkinci kademede testlerin bana verdiği öz güvenle birlikte kodu istediğim şekilde yeniden yoğurabiliyor ve istediğim şekle sokabiliyorum. Buradan da anlaşılabileceği gibi yazılımda geviş getirme taktiğini uygulayabilmek için ikinci kademede yeniden yapılandırmak istediğim kodun tümünü kapsayan birim testlerine ihtiyaç duymaktayım. Eğer testlerim yoksa, ikinci kademede kodu yeniden yapılandırmaya cesaret etmem mümkün değil, çünkü elden yapılan yeniden yapılandırma işlemleri testler olmadan rus ruletinden farksızdır. Nasıl bir netice alacağınızdan emin olamazsınız ve yapılan değişikliklerin yan etkilerini ölçmeniz mümkün değildir. Bu yüzden geviş getirme taktiği test yazma zorunluluğunu beraberinde getirir.


EOF (End Of Fun)
Özcan Acar