Kurumsal Java Yazılımı |
|
|
Posted: 26 Oct 2014 09:05 AM PDT Mimar Aslan ve Orhan Eripek tarafından sunulan Bilişim Sohbetleri‘ne konuk oldum. |
|
Teknik Borç Nedir ve Nasıl Ödenir? Posted: 19 Oct 2014 03:08 AM PDT Teknik Borç Nedir ve Nasıl Ödenir? başlıklı yazım. |
|
Başkalarının Kodu Okunarak Daha İyi Programcı Olunabilir mi? Posted: 30 Sep 2014 01:39 PM PDT Başkalarının Kodu Okunarak Daha İyi Programcı Olunabilir mi? başlıklı yazım. |
|
Java String Nesnelerinin Hafıza Kullanımı Nasıl Azaltılır? Posted: 18 Sep 2014 10:51 AM PDT Java String Nesnelerinin Hafıza Kullanımı Nasıl Azaltılır? başlıklı yazım. |
|
Çok Gezen mi Bilir, Çok Okuyan mı? Posted: 11 Sep 2014 02:00 PM PDT Çok Gezen mi Bilir, Çok Okuyan mı? başlıklı yazım. |
|
Yazılımda Geviş Getirme Taktiği Nasıl Uygulanır? Posted: 05 Sep 2014 10:09 AM PDT 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: bu bir test Elde edeceğimiz dosyanın içeriği şu şekilde olmalı: test: 1 bir: 1 bu: 1 Her kelimenin dosya içinde kaç kez kullanıldığını görüyoruz. Bu kodu test güdümlü şu şekilde yazdım: 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<String> lines = null; try { lines = FileUtils.readLines(file); if (lines.size() == 0) { throw new FileIsEmpty(); } } catch (final IOException e) { throw new SortError(e); } final Map<String, Integer> 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<Entry<String, Integer>> entrySet = values.entrySet(); for (final Entry<String, Integer> 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<String> 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"; } } İş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.
private class WordSorter {
public void sort(final File file) {
checkFile(file);
final List<String> lines = getLines(file);
final Map<String, Integer> sortedKeys = new HashMap<>();
sortWords(lines, sortedKeys);
final String newContent = buildSortedContent(sortedKeys);
logSortedContent(newContent);
writeSortedContentToFile(file, newContent);
}
private List<String> getLines(final File file) {
List<String> lines = null;
try {
lines = FileUtils.readLines(file);
checkLines(lines);
} catch (final IOException e) {
throw new SortError(e);
}
return lines;
}
private void checkLines(final List<String> 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<String, Integer> values) {
final StringBuilder content = new StringBuilder();
final Set<Entry<String, Integer>> entrySet = values.entrySet();
for (final Entry<String, Integer> entry : entrySet) {
content.append(entry.getKey() + ":\t" + entry.getValue()).append("\n");
}
return content.toString();
}
private void sortWords(final List<String> lines, final Map<String, Integer> values) {
for (final String line : lines) {
final String[] words = line.split(" ");
handleWords(values, words);
}
}
private void handleWords(final Map<String, Integer> values, final String[] words) {
for (final String word : words) {
handleWord(values, word);
}
}
private void handleWord(final Map<String, Integer> 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<String, Integer> values, final String word, final Integer value) {
values.put(word, value);
}
private boolean noLines(final List<String> lines) {
return lines.size() == 0;
}
}
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.
|
|
Yazılımcılığın Ne Olduğunu Anlamamış Bilgisayar Mühendisi Posted: 03 Sep 2014 01:15 PM PDT Bu yazıyı okuduğumda, “yazılım kimlere kalmış” dedim ve bu yazıyı kaleme almaya karar verdim.
Şimdi bu yazıyı alıntılar vererek, analiz etmek istiyorum. Kendimi bunu yapmak zorunda hissediyorum, çünkü yazı tam bir kargaşa ve tutarsızlıklar abidesi. Amacım kimseyi rencide etmek değil. Bu arkadaşımızın yazmış olduğu tezlerin mevcut ve müstakbel yazılımcılara ve yazılım sektörüne zarar verdiğini ya da verecegini düşünüyorum. Bu işi severek yapan birisi olarak, buna izin vermem düşünülemez. Bunun yanı sıra yazıyı okuyan birisi olarak, cevap verme ve yanlışlıkları düzeltme hakkımı kullanıyorum. Yazının ana konusu mühendlislik okumadan, yazılımcı olunup, olunamayacağı şeklinde. Yazı sahibinin görüşü bu işi sadece bilgisayar ya da yazılım mühendisi olanların yapabileceği yönünde ve şöyle bir çelişki ihtiva ediyor: Yazılım üretmek bir sanattır, aksi iddia edilemez. Sizin bu sanata yatkın olup olmadığınızı ortaya çıkartacak çok kolay bir testi aşağıda açıkladım. Yazar ne yazık ki burada sanatı kastetmekle birlikte sanat ile zanaati karıştırmış durumda. Kurmuş olduğu cümle çelişkili, çünkü savunduğu mühendislik olgusu ile sanatın yakından ve uzaktan hiçbir ilgisi olamaz. Yazılım mühendislik işidir kanısını savunurken, yazılım sanattır diyor. Programcılık sanat mı, zanaat mı? başlıklı yazımda bu konuya değindim. Programcılık sanat değildir. Programcılığın bir kısmı mühendislik bir kısmı da zanaattir. Uygulamalar bir mühendis kafasıyla tasarlanır, bir zanaat ustası gibi koda dökülür. Yazar bir yazılımcı olup, olamayacağımızı anlayabilmemiz için bir test hazırlamış. Testi inceleyelim: Tek yapmanız gereken başlamak istediğiniz programlama veya uygulama dilinin kitabını alıp okumaya ve uygulamaya başlamak. Şayet okuduğunuzu anlayabiliyor ve uygulayabiliyorsanız, gayet güzel! Şayet okuduğunuzu anlayamıyor, küçük uygulamalar geliştirirken dahi kitabı açıp bakma ihtiyacı hissediyorsanız, bu durumda sizden yazılımcı olmaz. Programcı olmaya hevesli ama bu konuda hiçbir bilgisi olmayan bir şahsın bu testi geçmesi imkansız, çünkü test çok absürd. Yeni bir alana ayak bastığınızda, o alana has dili bilmediğiniz için başlangıçta bocalamanız çok doğal. Her çalışma sahasının kendine has zorlukları olur ve bu zorluklar çalışma sahasına aşina olundukça aşılır. Eğer herkes yeni bir sahaya adım atarken böyle bir test yapmak zorunda kalsaydı, o zaman bu dünyada kalifiye bir eleman bulmak mümkün olmazdı. İşte çok cesur ortaya atılmış bir tez daha… Mühendis ile yazılımcı arasındaki fark, maaş konusunda keskinleşir. Yazar burada ne yazık ki genel değil, yerel bir bakış sergilemektedir. Türkiye çok genç bir nüfusa sahip olan bir ülke. Bunun yanı sıra yazılım sektörü Türkiyemizde çocuk ayakkabılarıyla geziniyor. Yazılımın gerçek anlamda nasıl yapılması gerektiği konusunda bilgili insan sayısı parmakla sayılacak kadar az. Çoğu bilgisayar mühendisi de bu işi bilmiyor, sonradan bu işi öğrenmiş şahıslar da. Bilgisayar ya da yazılım mühendisi ile alaylı yazılımcı arasındaki fark kesinlikle maaş olamaz. Türkiye küçük bir piyasa olduğundan ve çok sayıda yazılımcı bu piyasada iş bulmaya çalıştığı için firmalar seçici olma ayrıcalığına sahipler. Doğal olarak arz edilen iş gücünün talep edilenden fazla olması, firmaların mühendis sıfatını taşıyan yazılımcıları tercih etmesini kolaylaştırıyor. Almanya gibi bir piyasada Türkiye’ye nazaran arz talep dengesi tam tersine olduğundan, mühendis ile alaylı yazılımcı arasında hiçbir fark yok. Fark olsa bile bu farklılık tecrübe bazındadır. Kim daha tecrübeli ise, daha çok maaş alır. Hesap bu kadar kolay. Mühendis, mühendis olduğu için daha çok para kazanır ya da kazanmalıdır demek, abesle iştigalden başka bir şey olamaz. Ayrıca bu bazı kafaların nasıl çalıştığının da çok iyi bir ispatıdır. Bir iş gücünü kıymetli kılan, iş konusunda ne kadar kalifiye olduğudur. Üniversite bitirerek hiçbir kimse kalifiye elemanım diye ortalıkta gezinemez. İnsan iş başında, öğrendiklerini uygulayarak pekişir ve kalifiye bir eleman haline gelir. Bu açıdan bakıldığında bir bilgisayar mühendisi ile kendini çok iyi yetiştirmiş bir alaylı yazılımcı arasında hiçbir fark yoktur. Nice fizik ve kimya mühendisleri tanıyorum. Yıllarca programcı olarak çalışmışlar ve benim diyen birçok bilgisayar ya da yazılım mühendisini ceplerinden çıkartırlar. Yazarın bir sonraki tezine bir göz atalım: Mühendisler, kurumsal yapılı şirketlerde daima yazılımcıların patronu olarak çalışır (başlangıçta böyle olmayabilir, ama yazılımcının 5 yıldır beklediği terfiyi mühendis en çok 2 yılda alır. Ben yazılımcı olsam ve bu şekilde hakkım yense, böyle bir firmada beş dakika bile durmazdım. Çalışanlarına sarf ettikleri emeğe göre değil de, sahip oldukları bir takim mühendislik sıfatlarına göre öncelik tanıyan firmalar, bu hallerini piyasada eleman bulamayacak hale geldiklerinde çok çabuk değiştirirler. Yazarın tezinden de anlaşıldığı gibi Türkiye’de yazılım sektörü yok kadar küçüktür ve bu durumu kendi avantajları yönünde kullanan kurumsal firmalar, şimdilik alaylı programcıların haklarını yemektedirler. Yarın, bir gün piyasa büyüdüğünde ve programcılara olan talep arttığında, kim daha çabuk terfi alıyormuş, göreceğiz. Kim daha çok çalışıyor ve tecrübe sahibi ise, terfiyi o almalı. Mühendisi alaylının önünde gören zihniyet, iş gücüne ihtiyaç duyduğunda, o alaylının önünde eğilir. Bu kaypaklık zaten mühendisler önceliklidir zihniyetinin ne kadar kokuşmuş olduğunun ve dikkate ve ciddiye alınmaması gerektiğinin ibaresidir. Devam edelim: Bazen kurumsal şirketler de "uzman" alabilir, ama bu durumda "mühendisin" yanında takılan bir eleman gibi olursunuz, tavsiye etmem. Yazar ne yazık ki mühendis dediği şahısları cebinden çıkaran alaylılarla çalışma fırsatı bulamamış. Alaylı konumu itibari ile zaten mühendisin bildiğinin iki katını, çalıştığının iki katını, öğrendiğinin iki katını öğrenme eğilimi gösterir, çünkü kendisini mühendis ünvanı olmadan ispatlamak zorundadır. Bu şartlar altında boynuz kulağı çok çabuk geçer. İngilizce olmadan sıradan “programcı” olmanın ötesine geçemezsiniz. Yazara bu konuda haklıdır. Türkçe kaynakların artması ile ingilizce bilmenin de önemini yitireceğini düşünmekle birlikte, ingilizce bilmenin yazılımda şimdilik bir gereklilik olduğunu düşünüyorum. Üniversite okumadan, "yazılım uzmanı" olmaktan öteye geçemezsiniz... Bu işin en doğru yolu mühendis olmaktır. Eğer kendim de bir bilgisayar mühendisliği okumuş bir yazılımcı olmasaydım, bu üniversitede ne öğretildiğini gerçekten çok merak ederdim. Ben size üniversitede ne öğretildiğini kısaca özetleyeyim: biraz matematik, biraz algoritma, biraz ondan, biraz bundan ve dört sene sonra diplomasını alıp, mühendis oldum diye böbürlenen, ama gerçek yazılımın nasıl yapıldığından ışık yılları uzaklıkta şahıslar. Bu mühendislik olayını büyütmenin gerçekten bir anlamı yok. Makine ve elektronik mühendisleri bile biz bilgisayar mühendislerinden mezun olduklarında çok daha donanımlı mühendisler. Bunun yanı sıra en doğru yolu kavramı göreceli bir kavramdır. Yolların hepsi Roma’ya çıkar diye bir söz vardır. İyi bir yazılımcı olmak için bilgisayar ya da yazılım mühendisi olma zorunluluğu var diye bir şey olamaz. Şu anda bir işveren olarak da "yazılım uzmanı" hiçbir arkadaşımızı işe almadım, hep "yazılım veya bilgisayar mühendisi" işe aldım. Çünkü bizim ürettiğimiz yazılımlarda, analiz ve matematiksel yetenekler gerekiyor. Eğer uzay mekiği ya da bilgisayar oyunları için program yazmıyorsanız, yazdığınız kodun %99’unda toplama, çıkarma gibi basit matematiksel işlemler yaparsınız. En son yaptığım matematiksek işlem x++ idi. Programcılığın matematikle çok ilgisi yok. Soyutlayabilme yeteneğiniz varsa, programcı olmak için en önemli yeteneğe sahipsiniz demektir. Geriye kalan her şeyi kitaplardan ve bu işin ustalarına bakarak, öğrenebilirsiniz. Bu işi kendi kendine veya kursta öğrenen hiç kimse de kalkıp, "Yahu ben cebir, analiz, algoritma vb. de öğreneyim" demiyor. Mühendislerin çoğu ilerde mühendis ünvanım ile daha iyi bir yere gelir, daha fazla para kazanırım diye mühendis olurlar. Alaylı programcıların çoğu bu işe gerçekten gönül verdikleri için programcı olurlar. Bu işe gönül veren bir insan Java gibi mainstream olan diller haricindeki dilleri de öğrenir, çünkü kendini geliştirme yolunda her türlü bilgiyi sünger gibi emer. Kim Daha İyi Programcı? başlıklı yazımda bu konuya değindim. Verdiğim eğitimlerde bazı alaylı olan katılımcıların mühendislere nazaran daha geniş bir teknik altyapıya sahip olduklarına, kendi uzmanlık alanları haricindeki alanlarla da ilgilendiklerine ve yenilikleri yakından takip ettiklerine şahit oldum. Bunun sebebi bu işe gerçekten gönül vermiş olmaları ve severek yapıyor olmalarıdır. Ben bu tür adamları işe alırdım, çünkü projeyi uçuracak olanlar bu tür yazılımcılardır. Eğer bir yazılım mühendisi de bu tür özelliklere sahip ise, o zaman onun da başımızın üzerinde yeri var. Hindistan’da insanları sınıflandırmak ve toplumu ayrıştırmak için kutu sistemi oluşturulmuş. Bir kutuya ait insanların başka bir kutuya geçmeleri mümkün değil. Yazar da yazılımcılar için böyle bir kutu sistemi oluşturmaya çalışmaktadır. Bunun kimseye bir faydası olmadığı ortadadır, çünkü oluşturulan bu kutu sistemi maddi olarak daha fazlasını hak eden alaylıların kötü şartlarda sömürülmelerine sebep vermektedir. Bir işin üniversite eğitimini almış olmak şüphesiz iyi bir teknik altyapının oluşturulmasında faydalıdır. Lakin üniversitelerin ne kadar kolay ve uyduruk bir şekilde bitirilebileceğini hepimiz biliyoruz. Hele hele Türkiye gibi ezberciliğe dayanan bir eğitim sisteminin olduğu bir ülkede alınan üniversite eğitimini bir ayrıcalık olarak satmaya kalkmak, bu işin hakkını vererek çalışmaya çalışanlara yapılmış büyük haksızlıktır. Gerçek öğrenim süreci üniversite bittikten sonra başlar. Alaylı olsun, mühendis olsun, iyi bir programcı ile vasat bir programcının arasındaki farklılık da burada belirir. Kim her daim yeni bir şeyler öğrenme eğilimi gösterirse, öne geçer. Yazılım tutku işidir. O tutkuya sahip olmayanı mühendis ünvanı bile kurtaramaz. Benlik gütmeyelim.
|
|
Posted: 10 Aug 2014 11:09 AM PDT Başlama ve Bitirme Kriterleri başlıklı yazım. |
|
5 Adımda Daha Kaliteli Yazılım Testleri Posted: 29 Jul 2014 11:13 AM PDT 5 Adımda Daha Kaliteli Yazılım Testleri başlıklı yazım. |
|
Müşteri Gereksinimlerini Anladığımızdan Nasıl Emin Olabiliriz? Posted: 19 Jul 2014 05:26 AM PDT Müşteri Gereksinimlerini Anladığımızdan Nasıl Emin Olabiliriz? başlıklı yazım. |
|
Programcının Hayatını Kolaylaştıran 18 Alışkanlık Posted: 05 Jul 2014 05:21 AM PDT Programcının Hayatını Kolaylaştıran 18 Alışkanlık başlıklı yazım. |
|
Birim Testlerinde Beklentilerimi Daha Net Nasıl İfade Edebilirim? Posted: 09 Jun 2014 01:10 AM PDT |
|
Posted: 04 Jun 2014 01:33 PM PDT Sözde Lean! başlıklı yazım. |
|
En Basit Çözümü Oluşturma Yetisi Nasıl Kazanılır? Posted: 03 Jun 2014 06:39 AM PDT En Basit Çözümü Oluşturma Yetisi Nasıl Kazanılır? başlıklı yazım. |
|
Posted: 30 Apr 2014 04:12 PM PDT Yeni kitabım Pratik Agile Pratik Programcı Yayınları tarafından yayımlandı. Detayları burada bulabilirsiniz.
|
|
Dağın Ayağına Gelmesini Bekleyen Birisi Posted: 09 Apr 2014 02:17 AM PDT Bir varmış, bir yokmuş. Evvel zaman içinde, kalbur saman içinde birisi varmış. Bu birisi çok inatçıymış. Birgün bir arkadaşı ile “ben dağa gitmem, dağı ayağıma getiririm” diye iddiaya girmiş.
Başlamış dağın ayağına gelmesini beklemeye. Bir zaman beklemiş, bakmış dağın geldiği falan yok. Bu eninde sonunda ayağıma gelecek diye beklemeye devam etmiş. Yine aradan bir zaman geçmiş. Bu durum dağın umurunda bile değilmiş. Birisi o zaman ben dağa biraz daha yaklaşayım, bakarsın dağ fikrini değiştirir ve ayağıma gelir demiş. Dağa doğru biraz ilerlemiş ve beklemeye başlamış. Gel zaman, git zaman dağdan ses yok, ne gelen varmış, ne giden. Galiba birisi dağı hala beklemeye devam ediyor… Bu kısa hikayecikte dağın insanın ayağına gelmesini beklemeyi hayattan olan beklentilerimizi simgeleyen metafor olarak seçtim. İnsanlar hayattan her zaman büyük beklentiler içindedirler. Beklentinin fiil hali beklemektir. Beklemek pasif bir durumdur. Oturup pasif olarak bir şeyleri bekleriz, hikayedeki birisi misali. Zamanla beklenti içinde olmakla hedef sahibi olmak durumsal olarak birbirine karışabilir. Birey beklentilerini düşünerek hedef sahibi olduğunu zanneder. Örneğin okulu bitirdikten sonra iş hayatına atılarak iyi bir gelecek sahibi olma beklentisine sahip oluruz. Bu beklenti zaman içinde hedef kılığına bürünür ve iyi bir gelecek sahibi olmayı hedeflediğimizi zannederiz. Oysaki pasif bir şekilde her şeyin beklediğimiz şekilde yolunda gitmesini ümit ederiz. Bu kendimize koyduğumuz bir hedef değil, bir beklentidir. Pasif bir şekilde bir şeylerin olmasını bekleriz. Pasif olmak faydalıdır, çünkü boşuna enerji harcanmasını engeller. Tek faydası da budur. Dağı ayağına getirmeye çalışmak ya da iyi bir geleceğe sahip olmak beklentiden başka bir şey olamaz. Beklemek ve pasif olmak yerine hedef güdümlü olmak gerekir. Hedef bireyi mıknatıs gibi çekerken, beklenti hedefi mıknatıs gibi çekmeye çalışır. Hedefi kendine çekmenin ümitsiz bir vaka olduğunu hikayede gördük. Koca dağı kendine çekmeye çalışan birisi bu işte pek başarılı olamadı. Başarılı olmak için bir yerlere doğru, yani hedefe doğru çekilmemiz gerekir. Hedef bireyi kendine doğru çekebilmek için bir şeye daha ihtiyaç duyar. Bu motivasyonun kendisi ya da motive edici sözler değildir. Bu daha ziyade hedefe doğru giderken nitrojen vazifesi gören, bir an önce hedefe ulaşılmayı isteyen bir şeydir. Bu şey tutkudur (passion). Hedefe ulaşmak dağa tırmanmak gibi zahmetli bir uğraştır. Düşer, yara alırız. Tutku aldığımız yaralardan doğan acıları dindirir ve bizi tekrar ayağa kaldırır. Yola devam etmemizi sağlar. Tutku olmadan hedefe giden yollar kat edilemez, yollar yarıda bırakılır. Hedefe ulaşılsa bile yolda alınan darbeler o kadar büyüktür ki insan yola çıktığına bin pişman olur. Tutku ile yola çıkan ilacını yanına almıştır. Aldığı darbelerden gelen acıları hissetmez. Tutku bize bir zaman sonra bir şeyi daha gösterir. Bu şey bize bir zaman sonra gidilecek yerin aslında hedef değil, hedefe giden yolun kendisi olduğudur. Beklenti motoru olmayan bir gemi gibidir. Buna karşın tutku insanı rotasında tutar. Yolculuğun nereye gittiğine o karar verir, sahibini de pesinde sürükleyip götürür. Peşinde sürüklenme de pasif bir durumdur. Demek oluyor ki tutku gemisine binen belki büyük emekler sarf ederek hedefine ulaşır, ama yolculuk ona elini, kolunu sallayıp, ıslık çalarak yaptığı bir sabah yürüyüşü gibi gelir. İçinde tutkunun olduğu iş aslında iş değildir, eğlencedir. Bu yüzden hedefe ulaşmak kolaydır. Emeği eğlenceye dönüştüren tutkudur. Demek ki hedefi keşfetmeden tutkuyu keşfetmek gerekiyor. Onu keşfetmek için kalbinizi dinleyin. En çok neyi yapmayı seviyorsanız, tutku onun içinde gizlidir. Onu keşfettikten sonra hedefleriniz sizi bulur.
|
|
Standart Java API’ler Neden Tercih Edilmeli? Posted: 05 Apr 2014 05:43 PM PDT Standart Java API’ler Neden Tercih Edilmeli başlıklı yazıma bu link üzerinden ulaşabilirsiniz. |
|
Posted: 02 Apr 2014 07:24 AM PDT Java dilini temelinden öğreten sanal bir kurs hazırladım. Bu kurs hakkındaki detaylı bilgiyi bu yazımda bulabilirsiniz.
|
|
Ne Zaman Test Güdümlü Yazılım Yapmalıyım? Posted: 27 Mar 2014 09:21 AM PDT Ne Zaman Test Güdümlü Yazılım Yapmalıyım? başlıklı yazım. |
|
java.lang.IncompatibleClassChangeError: Implementing class Nedir ve Analizi Nasıl Yapılır? Posted: 15 Mar 2014 04:05 AM PDT Bu hata genelde bir interface ya da üst sınıf değişikliğe uğradıktan sonra, classpath içinde bu sınıfı eski haliyle implemente etmiş/genişletmiş bir sınıfın, üst sınıf tekrar kullanılarak derlenmeden kullanılması sonucu ortaya çıkan bir hata türüdür.
Çalıştığım projede bir web uygulamasını deploy ederken hatanın şu hali ile karşılaştım: javax.servlet.ServletException: Error instantiating servlet class xxx.xxx.emobility.webapi.EMobilityUMAdminServlet org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) com.vw.mbbc.dispatcher.access.webapi.WebServerSimulator.invoke(WebServerSimulator.java:140) org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:744) root cause java.lang.IncompatibleClassChangeError: Implementing class java.lang.ClassLoader.defineClass1(Native Method) java.lang.ClassLoader.defineClass(ClassLoader.java:800) java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2918) org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1174) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1669) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547) org.hibernate.ejb.Ejb3Configuration.<clinit>(Ejb3Configuration.java:107) org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:124) javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48) javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32) Bu hatayı analiz etmek için HibernatePersistence sınıfının 124. satırından yola çıkıyoruz. Bu satırda Ejb3Configuration sınıfından bir nesne oluşturuluyor. Ejb3Configuration sınıfının 107. satırına göz attığımızda AnnotationConfiguration sınıfından bir nesne oluşturulduğunu görüyoruz. Stacktrace e göz attığımızda sınıf yükleyicisinin (classloader) bu sınıfı classpath içinde bulmak üzere işlem yaptığını görüyoruz. AnnotationConfiguration sınıfı Configuration sınıfını genişletiyor. IncompatibleClassChangeError hatasının oluşmasının ana sebebi ise şu: org.hibernate.cfg.Configuration sınıfı AnnotationConfiguration sınıfı ile aynı jar dosyası içinde değil. Eğer aynı jar dosyasında olsalardı Classloader doğru Configuration sınıfını bulurdu ve IncompatibleClassChangeError hatası oluşmazdı. Benim ortamında org.hibernate.cfg.Configuration sınıfının iki değişik sürümü iki değişik jar dosyasındaydı ve classloader tesadüf eseri kullanımda olan AnnotationConfiguration sınıfı için değişikliğe uğramış olan Configuration sınıfını önce buldu ve yükledi. Classloader uyumsuzluğun farkına vardığı için IncompatibleClassChangeError hatası oluştu. Bu sorunu çözmek için değişikliğe uğramış olan üst sınıfı bulup, classpath dan uzaklaştırmak gerekiyor. Benim örneğimde iki Configuration sınıfı bulunmakta. Çoğu zaman classpath içinde değişikliğe uğramış bir üst sınıf yer alacaktır. Bu durumda üst sınıfı genişleten alt sınıfı tekrar derleyerek bu sorun çözülebilir.
|
|
Posted: 08 Mar 2014 01:41 PM PST İdeal şartlar altında bir programcının savaş verdiği tek bir cephe vardır, o da müşteri gereksinimlerini önemlilik sırasına göre kodlamak. Çevik süreçlerde müşteriye 2-4 hafta süren çalışmalar ardından çalışır bir uygulama prototipi sunulur. Bu prototip müşteriye uygulamanın hangi seviyeye geldiğini, isteklerinin doğru uygulanıp, uygulanmadığını ve hangi değişikliklerin gerekli olduğunu anlama fırsatı verir. Buradan change request olarak bilinen ve müşteri gereksinimlerine daha yerinde cevap verebilmek için atılması gereken adımları tanımlayan değişiklikler doğabilir. Bu değişiklikler bir sonraki 2-4 haftalık çalışma sürecinde kullanıcı hikayesi (user story) olarak programcıya yansır. Bu değişikliklere rağmen programcının savaşı hala bir cephede devam etmektedir.
Eğer proje bünyesinde kodun kalitesine dikkat edilmiyorsa, programcı için ikinci bir savaş cephesi Buraya kadar tanımladığım yazılım geliştirme süreci başın, ayakların ve kolların nerede olduğunu belli olan bir süreçtir. Ekip neyin, ne zaman yapılması gerektiğini bilir ve çalışmalarını ona göre yönlendirir. Başın, ayakların ve kolların nerede olduğu bilinmeyen ve en kötü ihtimalle bu uzuvların yerlerinin devamlı değiştiği projelere de rastlamak mümkündür. Bu tür projelerde programcı birçok cephede savaş verir. Programcının kendisini en çaresiz hissettiği ve cephanesinin ve motivasyonunun en çabuk azaldığı projeler bu tür projelerdir. Bu tür projelerde change request çok başka bir kimliğe bürünerek programcılara musallat olur: acil iş! Proje yönetimi acil iş olarak gelen talepleri hemen ekibe delege ederek, sonuç almak ister. Hadi gelin birlikte acil iş olarak gelen bir işin programcı için nelere sebep verdiğine bir göz atalım. Acil işi öncelikle olağan üstü hal olarak tanımlayabiliriz. Normal proje akışı yanı sıra programcı bu olağan üstü hali normale dönüştürebilmek için birçok adım atmak zorundadır. Genel olarak acil değişiklik isteklerinin trunk olarak isimlendirilen ana kod dalında yapılması mümkün değildir. Bu yüzden programcı yan dal, yani branch oluşturmak zorundadır. Acil iş için gerekli kod değişikliği bu yan kod dalında yapılır ve değişiklikler daha sonra ana kod dalı ile birleştirilir. Bu işleme merge ismi verilmektedir. Her geçen gün ile merge işlemi daha komplike bir hal alır. İki hafta branch üzerinde çalıştıktan sonra muazzam emek ve dikkat sarf etmeden kodun ana dal ile birleştirilebildiğini hiçbir projede görmedim. Branch ve merge işlemleri başlı başına ağır işler olup, programcıyı çok yorarlar. Bunun yanı sıra yapılan değişikliklerin staging olarak isimlendirilen değişik türdeki test sunucularında test edilmesi gerekmektedir. Bu da tüm ekibi rehin alabilecek türde bir işlemdir. Test sürecinden sonra yeni bir sürüm oluşturulup, bu sürümün müşteri için çalışır hale getirilmesi gerekir. Şimdi çok acil iş, çok acil iş diye devamlı ekibin çalışma sepetine dökülen bu incilerin, programcıları ruhen ve bedenen ne kadar çok tıkadıklarını anlayabiliyoruz sanırım. Ben programcıları 500 beygir gücünde yarış arabaları ile kıyaslıyorum. Verin kullanıcı hikayelerini programcının eline ve bir zamanlar Formel 1 yarışlarının olduğu İstanbul Parka salın. Nasıl son speed turladığını göreceksiniz. Temcit pilavı gibi programcının devamlı acil iş, acil iş diye başına ekşirseniz, o zaman 500 beygirlik bu gücü birinci viteste parkurda koşturuyorsunuz demektir. Bu programcı bir gün ikinci vitese takar ve İstanbul Park’ın arka çıkış kapısından kaçar gider. Programcılar sahip oldukları potansiyeli kullanarak, işlerini düzenli bir gidişat çerçevesinde en iyi şekilde yapmak isterler. Rahatsız edildikleri bir ortamda tam randımanlı çalışamazlar. Doğrusunu söylemek gerekirse, canları çok sıkıldığında çekip giderler. Bunun önüne geçmek için şu acil iş ismi altında deklare edilmiş kime ne faydası olduğu belli olmayan anormal durumların bir son bulması gerekiyor. Aksi taktirde 500 beygir gücündeki spor arabalar Edirme-Kars arasında birinci viteste gidip, gelmeye devam ederler. İstediğinizin bu olduğunu zannetmiyorum.
|
|
Posted: 21 Feb 2014 01:17 PM PST Geçenlerde öğrenci bir arkadaş fikrimi almak için bana bir soru sordu. Belli bir meblağ için iki ay boyunca fulltime bir yazılım evi için çalışmasının doğru olup, olmayacağı hakkında fikrimi sordu. Bu okulunu aksatır mı diye sordum. Cevabı evet oldu. Benim de cevabım belliydi.
Yazılımda tek sorumluluk ismini taşıyan bir yazılım prensibi var. Birden fazla sorumluluğu olan sınıflar er ya da geç devrilirler. Aynı şey öğrenciler için de geçerlidir. Okulu yanı sıra iki ay gibi uzun bir süre iş peşinde koşturan iki karpuzu tek kolunda taşıyamaz. Ben 1996 yılından beri freelancer olarak çalışıyorum. Üniversite yıllarında freelance programcı olarak çalışmaya başladım. İki binli yılların dotcom furyasını hatırlayanlar çok iyi bilirler. O zamanlar Java kelimesini yazanlar bile programcı olarak işe alınırdı. Para kazanmanın tadına varanların çoğu üniversiteyi bitiremediler. Ha bugün, ha yarın derken öğrenim yolda kaldı, business ve para kazanmak öne çıktı. Paranın en büyük özelliği devamlı harcanmak isteyişidir. Para kazanan harcar ve belli bir zaman sonra belli bir hayat standardına kavuşur ve bu standardı korumak ister. Bu yüzden iki binli yıllarda programcı olarak çalışmaya başlayan bilgisayar mühendisi öğrencilerin çoğu bugün diplomasız hayatlarını sürdürüyorlar. Ayakta kalabildilerse ne mutlu onlara. 2005 senesinden sonra gördüğüm iş ilanlarının hepsinde istinasız bilgisayar ya da benzeri bir mühendislikten mezun programcılar arandığına şahit oluyorum. Ben şahsen diplomanın şahsın yetenekleri hakkında çok fazla bir şey ifade etmediği kanaatindeyim. İnsan üniversiteden sonra kendi yaptığı işi öğrenmeye başlıyor. Lakin iş verenlerde ne yazık ki bir diploma takıntısı var. Bu sebepten dolayı insan eğer okumak üzere yola çıktı ise, o zaman bu işe diploma alarak bir nokta koymalıdır. Sonuç itibari ile çalışan ve tüketen toplumun ödediği vergilerle devlet üniversiteleri işliyor. Öğrenci bir yerde diplomasını alarak topluma da kullandığı kaynakların hesabını vermek zorunda. İşi yarıda bırakmak olmaz. Bu topluma ihanet olur. O yüzden diploma alınacak, bundan kaçış yok. Şimdi tekrar yazımın başında bahsettiğim öğrenci arkadaşa geri dönmek istiyorum. Bu arkadaş iki aylık fulltime iş için 700 TL talep ettiğini söyledi. Öncelikle şunu söyleyeyim. Bu rakamın freelance bir iş için 700 değil, 7000 TL olması gerekirdi. Sahip oldukları değeri kestiremedikleri için çoğu öğrenci arkadaş böyle kendisini kullandırıyor. Eğer size teklif edilen işin hakkından geleceğinizden eminseniz, o zaman değeriniz ne ise, onu talep edin. İkinci husus ise kazanılacak 700 TL için koca bir gelecek vaat eden öğrencilik statüsünün rizikoya sokulmasıdır. 700 TL insanın başını göğe erdirmez, ama diploma erdirir. Öğrenci arkadaşlar bunu unutmamalı. Para her zaman kazanılır, diploma bir kere. EOF (End Of Fun) |
|
Posted: 18 Jan 2014 01:04 PM PST |
|
Spring 3.x İle Gelen Yenilikler Posted: 12 Jan 2014 08:38 AM PST Spring 3.x İle Gelen Yenilikler başlıklı yazım… |
|
Posted: 03 Jan 2014 05:43 AM PST Coca Cola’nın kaç türü var, bilirsiniz… Cola light, Cola zero, Cola classic…. Çevik süreçler için de aynı şey geçerli. Ben çevik süreçleri agile zero, agile light ve hardcore agile ya da classic agile olarak üç bölüme ayırıyorum.
Agile ZeroÇalışma ortamında çevikliğe dair hiçbir ibare yoktur. Belirtileri
Agile LightScrum gibi bir çevik süreç kullanılıyordur. Belirtileri
Agile ClassicEkip katıksız olarak çevik süreç metotlarını uygular. Belirtileri
Ne zaman çevik oluruz? Kodu hamur gibi yoğurabildiğimizde!
|
|
Posted: 02 Jan 2014 01:00 PM PST Spring’in Varoluş Nedeni başlıklı yazım… |
|
Yazılımcının Verimliliğini Artıran Eclipse Ayarları Posted: 01 Jan 2014 03:21 AM PST Çoğu projede yazılım geliştirme ortamı olarak Eclipse’i kullanıyorum. Bu yazımda yazılım yaparken verimliliğimi artırdığını düşündüğüm Eclipse ayarlarını sizinle paylaşmak istiyorum. Kullandığım Eclipse sürümü 4.3.1 Kepler.
Hiç sevmediğim bir şey varsa, o da formatsız koddur. Bir Eclipse editöründeki bir kodu CTRL+SHIFT+F ile formatlamak mümkün. Ama bunu yazılımcı değil, Eclipse otomatik olarak yapmalı. Bu ayarı Window –> Preferences –> Java –> Editor –> Save Actions bölümünde yapabiliriz. Prefecences panelini açtıktan sonra üst kısımda yer alan arama alanına save kelimesini girmeniz, resim 1 de yer alan ekrana ulaşmanızı sağlayacaktır. Kodu otomatik formatlamak için Perform the selected actions on save kutusunun işaretli olması gerekiyor. Bu kutuyu işaretledikten sonra diğer opsiyonlar aktif hale gelir. Kodu formatlamak için Format source code kutusunu işaretlemek yeterli olacaktır. Save Actions panelindeyken diğer gerekli gördüğüm ayarları da yapalım isterim. Bu paneldeki Additional actions kutusunu işaretleyelim ve Configure butonuna tıklayalım. Karşımıza resim 2 de yer alan panel çıkacaktır. Code Style tabındayız. Ben burada Use blocks in if/while/for/do statmens –> Always ayarını kullanıyorum. Şu şekilde bir if bloğu oluşturduğumda
if(i>0)
doIt();
bu Eclipse tarafından otomatik olarak şu şekilde formatlanır.
if(i>0){
doIt();
}
Buradaki maksat tek satırlık if/while/for/do yapılarını küme parantezi kullanarak bir blok haline getirmektir. İlk başlarda ben bu tür tek satırlık yapılarda küme parantezlerini kullanmazdım, çünkü bu bana koddan bir satır tasarruf etmemi sağlardı. Lakin sonraları çalıştığım birçok projede firma yazılım geliştirme politikası olarak küme parantezlerinin kullanılması koşulunu takip etme zorunluluğu doğduğundan ve Sonar gibi statik kod analizi araçlarının küme parantezi olmayan bloklardan hoşlanmadıklarından, ben de tek satırlık yapılarda küme parantezleri kullanmaya başladım. Bu bir alışkanlık meselesi. Çok kısa zamanda insan kullandığı yeni yapılara alışıyor ve bırakmak istemiyor. Bunu bir mezhep kavgasına dönüştürmenin bir anlamı yok anlayacağınız ;-) Yazdığım kodda sınıfların, metotların, değişkenlerin, metot parametrelerinin ve metot gövdesinde kullanılan değişkenlerin mümkün mertebe final olmalarına dikkat ederim. Final olan yapılar genel olarak değiştirilemez olduklarından, istenmeden değiştirilmeleri mümkün değildir. Örneğin final olan bir değişkene yeniden bir değer atanamaz. Paralel çalışan (multi threaded) bir uygulamada final olan yapılar threadlar arasında sıkıntı olmadan paylaşılabilir. Ben eğer şöyle bir metot yazdı isem
public void doIt(int i) {
int y = i;
System.out.println(y);
}
bu metodun Eclipse tarafından şu şekilde formatlanmasını arzularım:
public void doIt(final int i) {
final int y = i;
System.out.println(y);
}
Bunun Eclipse tarafından yapılması sağlamak için resim 2 de yer alan Use modifier final where possible kutusunun işaretlenmesi gerekmektedir. Burada üç seçenek bulunmaktadır. Eğer private olan değişkenlerin final olmasını istiyorsak Private fields, metot parametrelerinin final olmasını istiyorsak Parameter ve metot gövdesindeki değişkenlerin final olmasını istiyorsak Local variables seçeneğini aktif hale getiriyoruz. Resim 3,4 ve 5 de Additional Save Actions panelindeki ayarlarım yer alıyor. Resim 5 de yer alan Remove unused imports seçeneği koddan kullanılmayan import direktiflerinin silinmesini sağlamaktadır. Bir Java sınıfı bünyesinde zaman içinde import direktifleri ile birçok sınıf kullanılır. Bu sınıf yeniden yapılandırıldığında kullanımda olan bazı sınıflar ihtiyaç dışı kalabilir. Yazılımcılar genelde bu şekilde kullanım dışı kalan import direktiflerini görmediklerinden koddan uzaklaştırmazlar. Ben böyle bir sınıfı editörüme yüklediğimde yaptığım ilk kayıt işlemi (save) ile kullanım dışı olan tüm import direktifleri koddan silinir ve kod formatlanır. Faydalı bulduğum diğer bir ayar ise, hangi küme parantezi bloğunda olduğumu doğrudan görebilmektir. Resim 6 da hangi blok içinde isem, o bloğun küme parantezleri öne çıkmış durumda. Bu ayarı yapmak için resim 7 de görülen Editor paneline gitmemiz ve orada Bracket highlighting seçeneklerinden Enclosing bracket seçeneğini seçmemiz gerekiyor. Bu bizim içinde bulunduğumuz kod bloğunu doğrudan görmemizi sağlayacaktır. Java’da biliyorsunuz satır sonunda ; işaretini kullanma zorunluluğu var. Bunu da Eclipse bizim için yapabilir. Resim 8 de yer alan Automatically insert at correct position {} ve ; işaretlerinin doğru yerlerde Eclipse tarafından koda eklenmesini sağlamaktadır. Eclipse altında CTRL+F11 tuşlarına bastığınızda bir uygulamayı koşturabilirsiniz. Genelde ben bir uygulamayı test ederken bu tuş kombinasyonunu şıkça kullanıyorum. Lakin kaynak kod üzerinde yaptığım değişiklikler CTRL+F11 tuşlarına bastığımda otomatik olarak kaydedilmiyor ve böylece yaptığım değişiklikler koşturduğum uygulamaya yansımıyor. Ayrıca CTRL+F11 her zaman en son koşturduğum uygulamayı koşturmayabilir. Uygulamayı koşturmadan önce değişiklikleri kaydetmek ve uygulamayı yeniden derlemek için resim 9 da yer alan ayarların yapılmasında fayda vardır. Ne zaman iyi bir yazılımcı oluruz? Kullandığımız yazılım geliştirme ortamına sunduğu birçok kısa yol tuşlarıyla hakim olduğumuz zaman, çünkü bu bizim yazılımcı olarak verimliliğimizi artırır.
|
|
Spring Çatısının Yazılım Geliştirme Filozofisi Posted: 31 Dec 2013 01:14 AM PST Özellikle nesneye yönelik programlama teknikleri kullanıldığında, nesneler arasında var alan bağımlılıklar çok karmaşık bir yapının oluşmasına neden olabilmektedir. Uygulama geliştirme esnasında bağımlılıkların kontrol altına alınmasına dair bir çalışma yapılmadığı taktirde, yazılımcının verimliliği ve uygulamanın kod kalitesi düşecektir. Kaliteyi artırmanın ve yazılımcının daha verimli olmasını sağlamanın bir yöntemi, tüm bağımlılıkların ve oluşan karmaşık yapının dış bir uygulama çatışı (framework) tarafından yönetilmesini sağlamak olabilir. Bu bağımlılıkların uygulama tarafından değil, kullanılan uygulama çatışı tarafından yönetilmesi anlamana gelmektir. Bu yazılım filozofisine kontrolün tersine çevrilmesi ya da Inversion of Control (IoC) ismi verilmektedir. Spring çatısının var oluşu ve çalışma prensipleri bu filozofiye dayanmaktadır.
Dependency InjectionJava gibi nesneye yönelik bir programlama dili ile geliştirilen uygulamalar ideal şartlarda kodun tekrar kullanıldığı modüler bir yapıdadır. Modüller birbirlerini kullanarak, yapmaları gereken işlemleri gerçekleştirirler. Bu modüller arası bağımlılıkların oluşmasını sağlar.
class RentalController {
private RentalService service = new RentalServiceImpl();
}
Yukarıda yer alan RentalController sınıfı/modülü bunun güzel bir örneğini teşkil etmektedir. RentalController sınıfı RentalService interface sınıfına bağımlıdır. Böyle bir bağımlılık modüler bir yapının oluşturulması ve kodun tekrar kullanımını sağlamak açısından zaruridir. Lakin RentalController bünyesinde somut bir implementasyon sınıfı olan RentalServiceImpl sınıfından new operatörü ile yeni bir nesne oluşturulması, mevcut bağımlılığın değiştirilemez ve tek bir tipte olması gerektiği anlamına gelmektedir. Bağımlılığı yeniden yapılandırabilmek için kodu değiştirmek ve yeniden derlemek gerekmektedir. Bağımlılıklarını kendisi yöneten bir uygulamada kod kalitesini düşüren bu tür bağımlılıkların oluşturulmasıdır. Oysaki bağımlılıkların tersine çevrilmesi prensibine (DIP; Dependendy Inversion Principle) göre bağımlığın yönü somut değil, soyut sınıflara doğru olmalıdır.
class RentalController {
private RentalService service = rentalServiceFactory.instance();
}
Somut bir sınıfa olan bağımlılığı yok etmek için rentalServiceFactory gibi bir fabrika (factory) sınıfından faydalanabiliriz. Fabrika tasarım şablonunu simgeleyen rentalServiceFactory bünyesinde hangi somut RentalService implementasyonunun kullanıldığını gizlemekte ve RentalController sınıfını bahsettiğim somut bağımlılıktan kurtarmaktadır. Lakin buradaki sorun rentalServiceFactory nesnesine olan bağımlılıktır. Bu nesnenin de bir şekilde new operatörü ile oluşturulması gerekmektedir.
class RentalController {
private RentalService service;
public void setService(RentalService service){
this.service = service;
}
}
Bağımlılıkları oluşturma işlemi ile hiç uğraşmasak, bunu başka birisi bizim için yapsa nasıl olurdu? Yukarıda yer alan kod örneğinde service değişkenine gerekli değer setService() metodu aracılığı atanmaktadır. Biran için setService() metodunun dış bir mekanizma tarafından koşturulduğunu düşünelim. Bu mekanizma setService() metodunu kullanarak herhangi bir RentalService implementasyonunu RentalController sınıfına enjekte edebilir. Bu işleme bağımlılıkların enjekte edilmesi yani dependency injection (DI) ismi verilmektedir. Bu işlemi yapan da Spring çatışıdır. Aşağıda tipik bir Spring XML konfigürasyon örneği yer almaktadır. Yönetimi Spring’e devredilen bağımlılıklar için bu tarz konfigürasyon dosyaları oluşturulur. Spring bu konfigürayon dosyalarını kullanarak nesnelere arası gerekli bağımlılıkları oluşturur, yani bağımlılıkları enjekte eder.
<bean id="rentalController"
class="com.kurumsaljava.spring.RentalController>
<property name="service" ref="rentalService"/>
</bean>
<bean id="rentalService"
class="com.kurumsaljava.spring.RentalServiceImpl/>
Bağımlılıkların enjekte edilmesi prensibi ile çok sade yapıda olan sınıflar oluşturabiliriz. Kendi bağımlılıklarını yönetmek zorunda olmayan bir sınıf asıl işi olan işletme mantığına konsantre olabilir. Tek sorumluluk prensibi açışından bakıldığında da bu bir gerekliliktir. Hollywood PrensibiVIP (Very Important Person) olan şahıslara erişmek zordur. Onlar genelde “bizi aramayın, biz sizi ararız” şeklinde iletişimi tercih ederler. Holywood prensibi olarak bilinen bu prensibi IoC konseptini açıklamak için kullanabiliriz. Bağımlılıkların enjekte edilmesi Hollywood prensibine göre çalışmaktadır. RentalController sınıfı kendi başına bir konstrüktör ya da fabrika metodu koşturarak bir service nesnesi edinmeye çalışmaz. Bunu yapsaydı eğer, o zaman bu VIP şahsı telefonda aramak ve benim service nesnesine ihtiyacım var demek gibi bir şey olurdu. Bunun yerine VIP şahıs, yani Spring RentalController sınıfında yer alan setService() metodunu kullanarak RentalController sınıfıyla iletişime geçmektedir. Spring RentalController sınıfına bir RentalService nesnesi enjekte edebilmek için setService() metodunu koşturmaktadır. Spring sınıfların set() metotlarını ya da konstrüktörlerini kullanarak gerek duyulan bağımlılıkları enjekte etmektedir. Bağımlılığı enjekte edebilmek için bu metotları koşturması, yani sınıfı araması gerekmektedir. Spring koşturulacak metodun seçiminde konstrüktör ya da set() metoduyla sınırlı değildir. Hollywood prensibi sınıf bünyesinde yer alan sınıf metotları üzerinde de kullanılabilir. Bu Spring’in konfigürasyon dosyasında belirlenen herhangi bir sınıf metodunu koşturulabileceği anlamına gelmektedir. Kitabın on yedinci bölümünde inceleyeceğimiz Spring Task ve Scheduling modülünde herhangi bir POJO (Plain Old Java Object) sınıfın herhangi bir metodunu şu şekilde koşturmak mümkündür: <task:scheduled ref="rentalDownloader" method="download" cron="*/5 * 9-17 * * MON-FRI"/> Spring tarafından koşturulması gereken metodun ismi method element özelliğinde yer almaktadır. Görüldüğü gibi rentalDownloader nesnesi görevini yerine getirmek için hangi metodun koşturulması gerektiğini bilme sorumluluğundan arındırılmaktadır. Sadece konfigürasyon dosyası üzerinde değişiklik yaparak, POJO sınıfın çalışma tarzı adapte edilebilmektedir. Spring birçok modülünde bu mekanizmadan faydalanmaktadır.
|
|
Posted: 27 Dec 2013 01:26 AM PST Bir sene süren çalışmalarım ardından Pratik Spring isimli yeni kitabımı tamamladım. Yeni kitabım Pratik Programcı Yayınları tarafından e-kitap olarak PDF formatında satışa sunuldu.
Kitap 18 bölümden oluşuyor. Ana bölüm başlıkları şunlar:
Tüm içerik başlıklarını görmek için aşağıda yer alan linke tıklayınız. Kitabın birinci ve ikinci bölümlerini aşağıdaki linklerden edinebilirsiniz. |
|
Posted: 20 Dec 2013 02:27 AM PST Şimdi size sorsam, en çok sevdiğiniz üç yazılım kitabını yazarları ile sayabilir misiniz? Sayabilmeniz lehinize olurdu, çünkü bir sonraki iş görüşmenizde bu soruyla karşılaşma şansınız yüksek. Başıma geldiği için söylüyorum :)
Bana son on beş yıllık yazılımcı iş hayatımda sorulan en ilginç soru buydu. Cevabım şu şekilde olabilir. Bu sorunun altında yatan mentalite çok başka türden. Beni şaşırttı açıkçası. Artık eskisi gibi anlatın bakalım, şimdiye kadar neler yaptınız demiyorlar. Size belli bir zamanda çözmeniz gereken bir problem de sunmuyorlar. Bilginizin hangi temellere dayandığını anlamak için derin sondaj çekiyorlar. Bu soru benim için neden bu kadar ilginç, açıklamaya çalışayım. Google, Stackoverflow ya da BTSoru.com gibi sistemlerin var olduğu bir dünyada yazılımcılar belli bir sorunun çözümüne birkaç tık sonra ulaşabiliyorlar. Copy/Paste Programcı başlıklı yazımda bunu dile getirmeye çalışmıştım. Çoğu zaman copy/paste yaparak bir takım sorunları çözüp, işi geçiştiriyoruz. Çoğu zaman ne yazık ki çözümün temelinde yatan konseptleri anlamadan… Eğri oturalım, doğru konuşalım. Programcılık bilim ve mühendislikle alakalı bir meslek. Bu işin temelindeki bilimi kavrayabilmek için bu işin eğitimini almak gerekiyor. Ben üniversitedeki eğitimden bahsetmiyorum. O da önemli, lakin üniversiteden sonra bu bilim dalında kendimizi ne kadar ve nasıl geliştirdiğimiz çok daha önemli. Programcılık bir ömür boyu öğrenim görmeyi gerektiren bir meslek. Bu eğitimi bize başkaları veremez. Kendi, kendimizi eğitmemiz gerekiyor. Bunun en başlıca yolu da bol, bol kitap okumaktan geçer. Günümüzde programcı olarak bizim eğitimimize en büyük sekteyi yazılım çatıları (framework) vuruyorlar, çünkü sundukları imkanlarla çalışma sahalarındaki zorlukları ve bilgileri maskeliyorlar. Bu çatıları kullanmak programcı olarak bizim işimizi kolaylaştırmakla birlikte, temelde ne olup, bittiğini tam olarak anlamadığımız sürece yeni bilgi edinmek güçleşiyor. Bizim ana sorumluluğumuz sorun çözmek değil, sorunları çözebilmek için bilgi ve bilim taşıyıcısı olmaktır. İnternetten birkaç satırlık kodu alıp, sorun çözmek matah değildir. Asıl önemli olan kısa yolu değil, uzun ve acılı yolu seçip, belli bir öğrenim sürecinden sonra gerekli temel prensipleri kavramaktır. Kendimizi ciddi anlamda geliştirmek istiyorsak, ilgi duyduğumuz alanlarda elimize geçen her türlü yazılı kaynağı sistematik bir şekilde tüketmeliyiz. İçimizde işin temellerine indiğimiz hissi uyanmadıysa, yılmadan daha çok kaynak kitap tükerek, bu hisse sahip olana kadar devam etmeliyiz. Yılmamalıyız! İlim ve bilim, ilim ve bilim peşinde koşturanındır. En son okuduğunuz üç kitabı yorumlarda görmek ümidiyle…
|
|
Spring Core Sertifika Sınavı Ardından Posted: 20 Dec 2013 01:06 AM PST Geçen sene katıldığım Spring Integration ve Spring Core kurslarının ardından bu senenin mayıs ayında Spring Integration sertifikasını almıştım. Katıldığım kurslardan sonra aklımda Pratik Spring Core kitabını yazma fikri oluştu. Kitabı tamamladım ve yakında pragmatikprogramci.com adresi üzerinden pdf formatında e-kitap olarak satışa sunulacak. Bu arada iki gün önce Spring Core 3.2 sertifika sınavına katıldım ve sınavı kazanarak bu sertifikayı almayı hak ettim.
Sınava hazırlık olması amacıyla bu ve bu mock sınav sorularını inceledim. Burada sınavın içeriği hakkında bilgiler yer alıyor. Burada da sınava hazırlanış hakkında detaylı bilgi bulmak mevcut. Sınavda Spring sunucusu (container) ve Spring bean konfigürasyonu hakkındaki sorular çoğunlutaydı. JMX hakkında iki soru hatırlıyorum. Bunun yanı sıra sınavda Spring MVC, JMS, AOP, Spring Remoting ve Spring Security konularında sorularlar yer aldı. Sertifika sınavına girebilmek için SpringSource tarafından sunulan 5 günlük kursa katılmak gerekiyor. Bu hem Spring Integration, hem de Spring Core 3.2 sertifikası için geçerli.
|
|
Posted: 06 Dec 2013 10:35 PM PST KurumsalJava.com ve diğer blog sayfalarımda yazdığım yazıları bu e-kitapta bir araya getirdim. Beğeninize sunarım.
|
|
Posted: 12 Nov 2013 10:42 PM PST Bu anketi hazırlayarak, yazılım ve yazılımcılık hakkındaki düşüncelerinizi öğrenmek istedim. Yeterli veri elde ettikten sonra Yazılımcı 2013 Raporu’nu sizinle paylaşacağım. Türkiye Yazılımcı Raporu 2012’ye buradan ulaşabilirsiniz.
Bu anket son bulmuştur. Türkiye Yazılımcı Raporu 2013’e buradan ulaşabilirsiniz. Loading… |
|
Java Hotspot, Assembler Kod, Hafıza Bariyerleri ve Volatile Analizi Posted: 08 Nov 2013 07:30 AM PST Java kodu Java derleyicisi javac (compiler) tarafından byte koduna dönüştürülür. Bu makina kodu Assembler değildir. Bu yüzden Java byte kodunu mikroişlemci koşturamaz. Java kodunu koşturabilmek için mikroişlemci ile Java byte kodu arasında, byte kodunu mikroişlemci koduna dönüştürebilecek bir ara katmana daha ihtiyaç duyulmaktadır. Bu JVM (Java Virtual Machine) ismini taşıyan sanal makinadır. Sanal makina Java byte kodunu mikroişlemi gibi koşturur. Java byte kodu için mikroişlemci sanal makinadır. JVM bünyesinde Java byte kodu makina koduna dönüştürülerek mikroişlemci üzerinde koşturulur. Bu yazımda Assembler makina koduna dönüştürülmüş Java kodundan örnek sunmak istiyorum.
JVM bünyesinde byte kodu doğrudan Assembler makina koduna çeviren birim Hotspot JIT (Just In Time) derleyicisidir. JIT olmadan JVM sadece Java byte kodu yorumlayıcısıdır (interpreter). Hotspot çok sık kullanılan kod bölümlerini, mikroişlemci üzerinde daha hızlı koşturulabilmeleri için doğrudan Assembler koduna dönüştürür. Bu işlemi yapabilmesi için belli bir süre kodu analiz etmesi gerekmektedir. Bu sebepten dolayı Java uygulamaları belli bir ısınma aşamasından sonra daha hızlı çalışmaya başlarlar, çünkü byte kodun belli bir kısmı ya da hepsi JIT tarafından Assembler makina koduna dönüştürülmüştür. JIT tarafından oluşturulan makina kodunun çıktısını alabilmek için bir Hotspot Disassembler plugin kullanmamız gerekmektedir. Kenai base-hsdis projesi bünyesinde böyle bir plugin yer almaktaktadır. Ben bu yazımdaki örnekler için Linux altında 64 bit JDK7 (jdk1.7.0_45) kullandım. Kullandığım disassembler plugin linux-hsdis-amd64.so ismini taşıyor. Bu dosyanın libhsdis-amd64.so ismini taşıyacak şekilde jdk1.7.0_45/jre/lib/amd64/server dizinine kopyalanması gerekiyor. Bu işlem ardından çalışan bir Java uygulamasının makina kodu çıktısını alabiliriz. Önce koşturmak istediğimiz Java koduna bir göz atalım. Main sınıfı bünyesinde volatile olan volatileCounter ve counter değişkenleri yer almaktadır. count() metodu bünyesinde bir for döngüsünde bu değişkenlerin değerleri artırılmaktadır. For döngüsü volatileCounter değişkeni 100000 değerine ulaştığında son bulmaktadır. Hotspot JIT kodu 100000 sefer koşturulduğundan dolayı makina koduna dönüştürmektedir. Bu uygulamanın çok sıkca kullanılan alanlarının (hotspot; sıcak alan anlamında) makina koduna dönüştürülerek, daha da hızlı koşturulabilmeleri için gerekli bir işlemdir. Hotspot sadece sıkca koşturulan kodları makina koduna dönüştürür. Sıkça kullanılmayan kod bloklarının JVM tarafından yorumlanma hızı yeterlidir. Bu tür kodlar için makina kodunun oluşturulması çok maliyetli bir işlemdir. Elde edilecek kazanç çok az olacağından, sıkça kullanılmayan kod blokları makina koduna dönüştürülmez. Eğer count() metodunda bir for döngüsü kullanılmasaydı, JIT tarafından daha sonra göreceğimiz Assembler kodu oluşturulmazdı. Bunu sağlayan döngünün 100000 adet olmasıdır. JIT hangi kod bloğunun ne kadar koşturulduğunu takip ettiği için hangi kod birimi için makina kodu oluşturacağına karar verebilmektedir.
public class Main {
private volatile int volatileCounter;
private int counter;
public static void main(final String[] args) {
new Main().count();
}
private void count() {
for (; this.volatileCounter < 100000;) {
this.volatileCounter++;
synchronized (this) {
this.counter++;
}
}
}
}
Java derleyicisi (javac) tarafından oluşturulan byte kodunu aşağıdaki resimde görmekteyiz. Şimdi gelelim Hotspot JIT tarafından oluşturulan makina koduna. Makina kodunu görebilmek için JVM’i aşağıdaki şekilde çalıştırmamız gerekiyor: java -cp . -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Main Aşağıda oluşan Assembler makina kodu yer almaktadır:
1 0x00007f206906015c: jne 0x00007f2069060202 ;*monitorenter
; - Main::count@16 (line 16)
2 0x00007f2069060162: incl 0x10(%r13)
3 0x00007f2069060166: mov $0x7,%r10d
4 0x00007f206906016c: and 0x0(%r13),%r10
5 0x00007f2069060170: cmp $0x5,%r10
6 0x00007f2069060174: jne 0x00007f2069060226 ;*monitorexit
; - Main::count@28 (line 16)
7 0x00007f206906017a: mov 0xc(%r13),%r11d ; OopMap{rbp=NarrowOop r13=Oop r14=Oop off=222}
;*if_icmplt
; - Main::count@41 (line 13)
8 0x00007f206906017e: test %eax,0xa91ee7c(%rip) # 0x00007f207397f000
; {poll}
9 0x00007f2069060184: cmp $0x186a0,%r11d
10 0x00007f206906018b: jge 0x00007f20690602ef ;*aload_0
; - Main::count@3 (line 14)
11 0x00007f2069060191: mov 0xc(%r13),%r11d
12 0x00007f2069060195: inc %r11d
13 0x00007f2069060198: mov %r11d,0xc(%r13)
14 0x00007f206906019c: lock addl $0x0,(%rsp) ;*putfield volatileCounter
; - Main::count@10 (line 14)
15 0x00007f20690601a1: mov 0x0(%r13),%rax
16 0x00007f20690601a5: mov %rax,%r10
17 0x00007f20690601a8: and $0x7,%r10
18 0x00007f20690601ac: cmp $0x5,%r10
19 0x00007f20690601b0: jne 0x00007f2069060106
20 0x00007f20690601b6: mov 0xb0(%r12,%rbp,8),%r10
21 0x00007f20690601be: mov %r10,%r11
22 0x00007f20690601c1: or %r15,%r11
23 0x00007f20690601c4: mov %r11,%r8
24 0x00007f20690601c7: xor %rax,%r8
25 0x00007f20690601ca: test $0xffffffffffffff87,%r8
26 0x00007f20690601d1: je 0x00007f2069060162
27 0x00007f20690601d3: test $0x7,%r8
28 0x00007f20690601da: jne 0x00007f2069060100
29 0x00007f20690601e0: test $0x300,%r8
30 0x00007f20690601e7: jne 0x00007f20690601f6
31 0x00007f20690601e9: and $0x37f,%rax
32 0x00007f20690601f0: mov %rax,%r11
33 0x00007f20690601f3: or %r15,%r11
34 0x00007f20690601f6: lock cmpxchg %r11,0x0(%r13)
35 0x00007f20690601fc: je 0x00007f2069060162
36 0x00007f2069060202: mov %r14,0x8(%rsp)
37 0x00007f2069060207: mov %r13,(%rsp)
38 0x00007f206906020b: mov %r14,%rsi
39 0x00007f206906020e: lea 0x10(%rsp),%rdx
40 0x00007f2069060213: callq 0x00007f206905e320 ; OopMap{rbp=NarrowOop [0]=Oop [8]=Oop off=376}
;*monitorenter
; - Main::count@16 (line 16)
; {runtime_call}
Java kodu sequentially consistent değildir, yani Java byte kodu sahip olduğu sıraya göre satır satır koşturulmaz. Hem Java derleyicisi hem de mikroişlemci performansı artırmak için birbirine bağımlı olmayan kod satırlarının yerlerini değiştirerek işlem yapabilirler. Bu aslında oluşan Java kodunun programcının yazdığı şekilde koşturulmadığı anlamına gelmektedir. Paralel çalışan programlarda bunun çok ilginç bir yan etkisi var: bir threadin bir değişken üzerinde yaptığı değişikliği başka bir çekirdek üzerinde koşan başka bir thread göremeyebilir. Örneğin t1 (thread 1) a isimli değişkenin değerini bir artırdı ise ve t2 a belli bir değere sahip iken bir for döngüsünü terk etmek istiyorsa, t2 belki bu for döngüsünden hiçbir zaman çıkamayabilir, çünkü a üzerinde t1 tarafından yapılan değişiklikleri göremeyebilir. Bunun sebebi t1 in üzerinde çalıştığı çekirdeğin (core) a üzerinde yaptığı değişiklikleri kendi ön belleginde (L1/L2 cache) tutmasıdır. Mikroişlemciler yüksek performansta çalışabilmek için hazıfa alanları üzerinde yaptıkları değişiklikleri ilk etapta kendi ön belleklerinde tutarlar. Gerek duymadıkça da bu değişiklikleri diğer çekirdeklerle paylaşmazlar. Her çekirdek sahip olduğu önbelliği tüm hafiza alanıymış (RAM) gibi gördüğü için kendi performansını artırmak adına kod sırasını değiştirebilir. Bu belli bir sıraya bağımlı olan diğer threadlerin düşünüldükleri şekilde çalışmalarını engelleyebilir. Bu genelde paralel programlarda program hatası olarak dışarıya yansır. Bu tür sorunları aşmanın bir yolu volatile tipinde değişkenler kullanmaktır. Volatile tipinde olan değişkenler üzerinde işlem yapıldığında mikroişlemci kodu programcının yazdığı sırada koşturmaya zorlanır. Bunu gerçekleştirmek için hafıza bariyerleri (memory barrier) kullanılır. Mikroişlemci bir hazıfa bariyeri ile karşılaştığında bir çekirdeğin sahip olduğu önbellekteki değişiklikleri doğrudan hafızaya geri yazar (write back) ve bu hazıfa alanını (cache line) kendi önbelleklerinde tutan diğer çekirdeklere mesaj göndererek bu hafıza alanını silmelerini (cache invalidate) talep eder. Böylece herhangi bir çekirdek üzerinde koşan bir threadin yaptığı değişiklik hemen hafızaya, oradan da diğer çekirdeklerin önbelleklerine yansır. Bu t1 tarafından a üzerinde yapılan bir değişikliğin t2 tarafından anında görülmesi anlamına gelmektedir. Bunun gerçekleşmesi için a isimli değişkenin volatile olması gerekmektedir. Main sınıfında yer alan this.volatileCounter++; satırı ile bahsettiğim hafıza bariyerinin kullanımı gerekmektedir. JVM bunu sağlamak için makina kodunun 14. satırında mikroişlemci için lock addl komutunu kullanmaktadır. 13. satırda yer alan mov komutuyla %r11d registerinde yer alan volatileCounter isimli değişkenin değeri doğrudan hafızaya (RAM) aktarılır. Hafiza alanının adresi %r13 registerinde yer almaktadır. 14. satırda yer alan lock addl ile tüm mikroişlemci bünyesinde global bir hafıza transaksiyonu gerçekleştirilir. Atomik olan bu işlem ile tüm çekirdeklerin üzerinde değişiklik yapılan hafiza alanını önbelleklerinden silmeleri ve yeniden yüklemelerini sağlanır. Böylece diğer threadler yapılan değişiklikleri anında görmüş olurlar. Java kodunu sequentially consistent yapmanın diğer bir yolu synchronized kelimesinin kullanımıdır. Synchronized kullanılması durumunda mikroişlemci değişikliğe uğrayan değeri önbellekten alıp hafızaya geri aktararak, diğer çekirdeklerin kendi önbelleklerini tazelemelerini sağlar. Main sınıfında yer alan counter isimli değişken volatile olmadığı için üzerinde yapılan değişiklikler diğer çekirdeklere yansımaz. Bunu sağlamak için count() metodunda değer atamasını synchronized bloğunda yaptım. Bu JVM tarafından tekrar bir hafıza bariyeri kullanımı gerektiren bir işlemdir. JIT makina kodunun 34. satırında lock cmpxchg ile gerekli hafıza bariyerini oluşturmaktadır. Bu üzerinde işlem yapılan çekirdeğin counter isimli değişkenin değerini tekrar hafızaya geri aktarmasını ve diğer çekirdeklerin bu değeri tekrar hafızadan kendi önbelleklerine çekmelerini sağlamaktadır.
|
|
Posted: 14 Oct 2013 07:09 AM PDT Uğur Umutluoğlu‘nun daveti üzerine aşağıdaki söyleşiyi gerçekleştirdik. |
|
Posted: 03 Oct 2013 05:44 AM PDT Mikrodevre.com adresi altında elektronik ve mikro denetleyiciler hakkında edindiğim bilgileri paylaşacağım yeni bir blog oluşturdum. Bilginize… Özcan Acar |
|
Posted: 09 Sep 2013 05:51 AM PDT Bir senior ve bir junior arasında yapılan konuşmaya kulak misafiri olalım: Senior: Sakın başkasının kodunu değiştirme! Ufak bir değişiklik ummadığın hataların oluşmasına sebep olabilir. Yaptığın değişiklik sonucu bir hata oluşmadı ise, kimse seni övmez. Ama hata olursa, herkes başına üşüşür. Bunu istediğini zannetmiyorum.
Bahsi geçen projenin kod tabanı çok geniş. Gerçekten de en ufak bir değişiklik beklenmeyen neticeler doğurabiliyor. Bu sebepten dolayı ekipte genelde “do not touch a running system (çalışan bir sistemi değiştirme)” mentalitesi oturmuş durumda. Hepsinin temelinde korku var: programcılar koddan korkuyor. Tecrübeli bir kuaför saçtan korksa idi, saça form verebilir miydi? Usta bir fırıncı hamurdan korksaydı ekmek yapabilir miydi? İyi bir futbolcu toptan korksaydı gol atabilir miydi? Peki koddan korkan bir programcı iyi kod yazabilir mi? Koddan korkan, kodun hakkını veremez! Koddan korkmanın tek nedeni, yapılan değişikliklerin meydana getirebileceği yan etkileri kestirememektir. Ama programcının yaptığı her türlü değişikliğin yan etkilerini ölçmek için bir aracı olsa korkusu azalır mıydı? Evet, azalırdı ya da tamamen yok olurdu. Böyle bir araç var mı? Evet, var. Peki bu aracın ismi nedir? Birim testi, entegrasyon testi, onay/kabul testi. Her türlü test makbuldür, yeterki değişikliklerin yan etkilerini tespit etmekte yardımcı olabilsiler. Test yazılmayan projelerde er ya da geç kod korku oranı artar. Belki de projelerin gidişatını tahmin etmekte bu bahsettiğim korku oranı bir metrik olarak kullanılabilir. Bu metrik örneğin şu şekilde tespit edilebilir: Yönetici: Bah, burada bir parça kod var. Bah baalım! Bakınca ne kadar korktun, süle bakim? Şu sunumumda çevikliğin temelinde sürekli değişim için cesaretin olduğundan bahsetmiştim. Test yazmak yazılımcının cesaretini ve koda karşı özgüvenini artırır. Test yazılmadığında bunun tersi olur. Veresiye satan yazılımcı olmak ya da olmamak yazılımcının kendi elinde. EOF (End Of Fun) |
|
Posted: 25 Jun 2013 03:19 AM PDT Hiç diyet yaptınız mı? O zaman her diyetten sonra tekrar kiloları fazlasıyla geriye aldığınıza diyet sayınız adedince şahit olmuşsunuzdur. Yeme alışkanlıkları değiştirilmeden hiçbir diyetin başarılı olduğunu görmedim. Bu konuda epeyce bir tecrübeliyim diyebilirim. Tipik bir iş gününün yüzde %90 ından fazlasını masa başında geçiren birisi olarak, kilolarımı dengede tutmak için yapmadığım diyet ve spor türü kalmadı. Ama bu konuda Karatay diyeti ile tanışana kadar kesinlikle başarılı olamadım. Verdiğim kiloları her zaman faylasıyla geriye aldım. Kilo vermek değil, kiloyu tutabilmek marifetmiş, bunu yaptığım bunca diyetten sonra çok iyi anladım.
Bir diyetin kısa bir analizini yapıp, neden başarılı olmadığını inceleyelim isterseniz. Her diyetin başında niyet vardır: kilo verme niyeti. Bu niyet olmadan diyetin başarılı olma ihtimali %0 dır. Hadi niyet sahibiyiz diyelim. İkinci adım hangi diyetin yapılacağının tespitidir. Hadi Diyet A yı yapmaya karar verdiğimizi düsünelim. Diyet A müthiş bir diyet ve bir ay içinde 10 kilo verdirebiliyor. Vay canına deyip, bu diyet ile sağlığımızı ne kadar büyük bir rizikoya soktuğumuzu göz ardı ederek diyete başlıyoruz. 10 kilo muazzam bir rakam. Ne yapıp, edip bu kilolardan kurtulmamız gerekiyor. Bu yüzden vücudumuzu darp edecek bu diyete başlıyoruz…. Bir ay sonra gerçekten 10 kilo verdiğimizi düşünelim. Bu utopik rakamı seçtim, çünkü insanların diyetlerden olan uçuk beklentilerini ifade etmek istiyorum. On senede 10 kilo alıp, bu kiloları bir ayda vermeyi istemek biraz absürd bir düşünme tarzı degil mi? Diyet yapanların %99.9999 kısmı diyet sona erdikten sonra eski yeme alışkanlıklarına geri dönerler. Bu onların altı ay içinde 20 kilo geri almaları anlamına gelir. Eğer sağlıklı yeme alışkanlarına sahip olsalardı, zaten diyet yapmalarına gerek kalmazdı. Kiloların geri alınmasının tek sebebi, diyet yapanın sahip olduğu yeme alışkanlıklarıdır. Yapılan diyet bireyin yeme alışkanlıklarını değiştirmez. Sadece kilo kaybına sebep olur. İnsanlar kolay kolay sahip oldukları alışkanlıkları terk edemezler, çünkü onlar beyinde yerleşmiş aksiyon şablonlarıdır ve beyin tanıdığı şablonların dışına çıkmayı sevmez. Bu sebepten dolayı hiçbir diyet başarılı olamaz, ta ki diyet sonrası yeme alışkanlarıkları gözden geçirilip, adapte edilene kadar. İnsan yaradılışı gereği her zaman kendi alışkanlıklarına sadıktır. Yeni alışkanları farkına varmadan edinir, mevcut alışkanlıklarının üzerine titrer. İnsanı comfort zone da tutan ve ilerlemesini engelleyen mevcut alışkanlıklarıdır. Beyinde çalışan ve alışkanlık olarak ifade ettiğim bu subrutinler değişikliğe karşı çok dayanıklıdır. Modifikasyona izin vermezler. Tek amaçları bir döngü içinde görevlerini yerine getirmektir. Bunun böyle kalması için beyin onları devamlı korur ve onlara runtime hizmetleri sunar. Onlara ne kadar çok etki etmeye çalışsak bile bunda çogu zaman başarılı olamayız. Bu yüzden silinmeleri gerekir. Bu onları değiştirmeye çalışmaktan daha zor bir iştir. Ama bunu yapmadığımız taktirde alışkanlıklarımızın rehini olmaya devam ederiz. Alışkanlıklar organizma için otomatik pilot vazifesi görürler. Beyin onların yardımı ile zor olan “düşün-karar ver-uygula” döngüsüne girmeden aksiyon alabilir. Bu günlük hayata adapte olmamızı kolaylaştırır. Bu şekilde hiç farkında bile olmadan debriyaja basıp, vites değiştirerek, şehrin bir ucundan diğer ucuna araba ile gidebiliriz. Her vites değiştirmek istediğimizde bu işlemi nasıl yapacağımızı uzun, uzun düşünmek zorunda kalsaydık, araba sürmenin ne kadar zahmetli bir iş olacağını düşünebilirsiniz. Bu açıdan bakıldığında alışkanlıkların olumlu tarafları da yok değil, çünkü günlük hayatımızı kolaylaştırabiliyorlar. Yazılımcılar da gün içinde, sahip oldukları birçok alışkanlığa maruz kalırlar. Örneğin uzun metotlar yazmak, metot, değişken, sınıf isimlerini özenle seçmemek, birim testi yazmamak, mevcut kod birimlerini tekrar kullanılabilir hale getirmeden copy/paste yapmak gibi. Bu şekilde çalışanlar, devamlı bu şekilde çalışmaya devam ederler, çünkü bu şekilde çalışmaya alışmışlardır. Bu şablonun neden dışına çıkmaları gerektigini sorgulamazlar. Bunu sorgulayanlar belli bir zaman dilimi için bu kötü alışkanlarıklarindan vaz geçmeye çalışırlar. Kısa metotlar oluştururlar, kullandıkları isimlere özen gösterirler, belki birim testi bile yazmaya başlarlar, ta ki sürüm zamanı yaklaşıp, günü kurtarmak zorunda kalıncaya kadar. O zaman ne olur? Edinmeye çalıştıkları yeni alışkanları top yekün terk ederek, eski alışkanlıklarına hemen geri dönüverirler, çünkü en hızlı o modda çalışabilirler ya da çalıştıklarını düşünürler. Bu aslinda programcının verdiği bir karar değildir. Daha ziyada beyin otomatik olarak eski şablonları çekmeceden çıkartarak, uygulamaya başlar. Beyin için içinde bulunulan stresli durumdan çıkmanın en kolay yolu mevcut alışkanlıklardan faydalanmaktır, yani işleri otomatiğe bağlamaktır. Aynı şey Organizasyonel Değişim başlıklı yazımda değindiğim gibi yazılım ekiplerine yeni davranış biçimleri kazandırmak için verilen ugraşılar için de geçerlidir. Yeni bir eğitimden geçmiş bir ekibin, eğitim sonrasında yeni öğrenilen yetilerle yola devam etmesi beklenemez, çünkü ilk fırsatta ekip çalışanları kendi alışkanlıklarına geri dönerler. Sahip olduğumuz alışkanları bir torbanın içinde toplanmış olarak hayal edelim. Beynimiz herhangi bir işlem gerçekleştirmek istediğinde bu torbaya elini sokup doğru olduğunu düşündüğü alışkanlığımızı seçerek, işletmeye başlayacaktır. Şimdi bu torbayı boşaltıp, yerine yeni alışkanlılar koyduğumuzu farz edelim. Bu durumda yeni alışkanlıklar devreye girecektir, çünkü torbada eski alışkanlıklar bulunmamaktadır. Beyin aslında yeni ya da eski alışkanlıklar arasında ayrım yapmadan, uygun bulduğu ilk alışkanlığı koşturacaktır. Demek oluyor ki torbadaki alışkanlıkları boşaltıp, yerine yeni alışkanlıklar koymamız gerekiyor. Bize faydası olmayan alışkanlıklardan nasıl kurtulabiliriz yani torbayı nasıl boşaltabiliriz? Yeni alışkanlıklar edinerek. Bilinçli olarak yeni alışkanlık nasıl edinebiliriz? Pratik yaparak. Kod Kata ve Pratik Yapmanın Önemi başlıklı yazımda bu konuyu açıklamaya çalıştım. Bu arada yazımın başında bahsettiğim Karatay diyetini merak ediyor olabilirsiniz. Bu bir diyet değil, sayın Prof. Doktor Canan Efendigil Karatay hanımefendi tarafından geliştirilmiş bir sağlıklı beslenme metodudur. Diyet olarak da uygulanabilmektedir, lakin ana amacı uzun vadede sağlıklı beslemek ve sağlıklı beslenme alışkanlıkları edindirmektir. Sağlıklı beslenmek isteyenlere şiddetle tavdiye edilir.
|
|
Posted: 21 Jun 2013 05:45 AM PDT İlk bakışta bir programcıyı senior yapan teknik bilgisidir. Yüksek seviyede teknik bilgiye sahip olmak için çok tecrübe sahibi olmak gerekir. Yüksek seviyede teknik bilgiye sahip bir şahsın senior olarak algılandığını düşünebiliriz. Lakin teknik bilgi senior olmanın sadece bir boyutudur. Senior mozaiğinin tamamlanması için birçok parçanın bir araya gelmesi gerekir.
Kendisini senior olarak tanımlayan birçok programcı gördüm. Benim açımdan bu programcıların çok büyük bir kısmı senior değil. Şu sebeplerden dolayı:
Bu listeyi sayfalar dolduracak sekilde genişletmek zor değil. Benim senior olabilmek için iki kriterim var. Yukarıda saydığım durumları zaman içinde aşıp, senior olmak zor değil. Lakin aşağıda sıraladığım iki kriter olmadan senior olmak hemen hemen imkansız. Bu kriterler:
Program yazmak çok maliyetli bir iş. Yazılan her satır kod para demek. Eğer bu satır gereksiz bir satır ise, o zaman bu para pencereden dışarıya atılmış para demektir. Programcı yazdığı her satırın gerekli olup, olmadığını nasıl anlayabilir? Müşteriye ne istediğini sorarak. Müşterinin ne istediğini anlamadan yazdığı kodun gerekliliğini bilemez. Müşteri ne istediğini tam olarak ifade eder ya da etmek zorundadır. Ben şunu istiyorum dediği zaman, programcının müşterinin şu olarak ifade ettiği şeyi koda dökmesi gerekir. Bundan fazlası zarardır, maddi kayıptır. Ne yazık ki kendisini senior olarak gören birçok programcı müşterinin ne istediğini anlamadan oturup, günlerce, haftalarca ya da aylarca program yazabiliyor. Müşterinin ne istediğini tam olarak anlamadan kod yazan, senior programcı olamaz, çünkü kendisini işverenine karşı sorumlu olarak gören bir programcının gönlü kendi yaptığı hatanın ya da keyfi çalışmalarının parasal zarar olarak işverenine yansımasına razı gelmez. Senior programcı bu dengeleri hep kafasında kendisiyle birlikte taşıyan programcıdır. Başarıyla birçok projeyi tamamlayarak sınır koymanın, sadece gerekeni yapmanın, müşteriyi memnun etmenin, işvereni mutlu etmenin ne demek olduğunu bilmeyen programcı senior ünvanını hak etmez. Başarıyla tamamlanan her proje programcıyı senior olma yolunda ilerlerken pekiştirir, tecrübelendirir. Başarıyla bir proje tamamlamak demek, müşterinin isteklerini tatmin eden bir ürün ortaya koymak demektir. Bu başarıyı birkaç kere tatmadan gerçek anlamda senior olmak çok güçtür ya da imkansızdır. Senior olmayı bazıları sadece bilgi küpü olmak ve daha fazla maaş almak olarak tanımlıyor demiştim geçenlerde attığım bir tweetde. Senior olmayı sadece bilgili olmak ve fazla maaş almak gibi iki zayıf sıfat ile tanımlamaya kalkmak gerçek senior programcılarin hakkını yemek olur. Bu iki şey gerçek senior programcılar için çok öncelikli olmayan şeylerdir. Onlar müşteriyi dinlemeyi, onun isteklerini tatmin etmeyi yeğlerler.
|
|
Posted: 25 May 2013 10:58 PM PDT Şüphesiz açık kaynak (open source) filozofisi biz programcıların hayatını derinden etkiledi. Açık kaynağın bize sağladığı bariz iki avantaj var. Bunlar:
İkincisi çok bariz olarak karşılaştığımız bir durum. Hibernate, Spring, Eclipse.. bu meşhur açık kaynaklı programların kullanılmadığı proje sayısı çok az ya da yok gibi. Birileri, bir yerlerde oturmuşlar, bizim hayatımızı kolaylaştırmak için gece, gündüz demeden açık kaynaklı programlar oluşturmak için çalışıyorlar. Neden bunu yapıyorlar acaba? Bunun çeşitli sebepleri olabilir. Örneğin:
Bu listeye eklenti yapmak zor değil. Benim ilk düşündügümde aklıma gelen noktalar bunlar. Peki bize bu kadar muazzam getirisi olan bir akım için biz programcılar neler yapıyoruz ya da yapabiliriz? Sayalım:
Eclipse benim yıllardan beri kullandığım açık kaynaklı bir araç. Bugün Eclipse Foundation için belli bir miktarda bağış yaptım. Ne kadar bağışladığım önemli değil. Önemli olan bağışın kendisi. Yıllardan beri freelancer olarak çalıştığım birçok projede Eclipse’i kullandım. Eclipse olmasaydı başka bir ürünü kullanırdım. Ama Eclipse vardı ve onu kullandım. Bunun karşılığında hiçbir bedel ödemedim. Eclipse bana programcı olarak çok şeyler kattı. Para kazanıp, hayatımı sürdürmeme destek oldu. Yaptığım bağış okyanusta bir damla bile olsa, damlaya, damlaya göl olur sözünü unutmamak lazım. Bağışı yaptıktan sonra Friend Of Eclipse ünvanina sahip oldum :) Ne güzel…. Çoğu programcı kullandıkları araçların açık kaynaklı olduklarına pek kafa yormadan hergün bu araçları kullanarak, işlerini yapıyorlar. Aynı şekilde çoğu programcının kullandıkları açık kaynaklı programların kodlarını inceleyip, daha iyi bir programcı olmak için çaba sarfettiklerini düşünmüyorum. Çoğu programcı bu ürünleri kullanırken keşfettikleri hataları bir yerlere bildirmiyorlar, çünkü birileri bunu yapar diye düşünüyorlar. Çoğu programcı commiter olarak açık kaynaklı projelere destek vermiyor, çünkü bunun için zamanları yok ya da zamanlarının olmadığını düşünüyorlar. Çoğu programcı lisans bedeli ödememek için bahsettiğim açık kaynaklı programları kullanıyorlar. Hepsini topladığımızda açık kaynaklı akımı desteklemeyen, egoistçe kullanan ve hiçbir şey geriye vermeyen bir programcı kitlesi oluşuyor. Ama üzülmesinler, yapabilecekleri bir şey var. Hiçbir şey yapamıyorlarsa, o zaman açık kaynaklı projelere bağış yapabilirler. Bu şekilde fayda sağladıkları projelerin devam etmelerini sağlayabilirler. Kullanıkları açık kaynaklı ürünler olmasaydı hayatlarının programcı olarak ne kadar daha zor olacağını düşünmeleri yeterli. Her programcıyı kullandığı açık kaynaklı araçları desteklemek için bağış yapmaya davet ediyorum. Bağış bir Lira’da olsa damlaya damlaya göl olur. Not: Bağış yapanlar bu yazıya yorum bırakarak, hangi açık kaynaklı projeyi desteklediklerini bildirebilirler.
|
|
SpringSource Certified Integration Specialist Sertifikası Posted: 20 May 2013 03:41 AM PDT Eylül 2012 de katıldığım SpringSource Certified Enterprise Integration Specialist kursunun ardından geçen hafta katıldığım imtihanı kazanarak SpringSource Certified Integration Specialist sertifikasını almaya hak kazandım.
![]() Resim 1: Kursa katılım belgesi Dört gün süren kursun içeriği aşağıdaki konulardan oluşuyor:
Spring bünyesinde Spring Integration ismini taşıyan ve EDA (Event Driven Architecture) ve SEDA (Staged EDA) mimarileri oluşturmak için kullanılabilecek bir modül yer alıyor. Certified Integration Specialist ifadesini duyanlar kursun ve imtihanın tamamen Spring Integration yapıları üzerine olduğunu düşünebilir. Ama ne yazık ki öyle değil. Certified Integration Specialist başlığı altında yukarıda saymış olduğum ve daha ziyada sistem entegrasyonu için kullanılabilecek konular yer alıyor. SpringSource tarafından sunulan kursa katılmadan sertifika imtihanına katılmak mümkün değil. Bu diğer SpringSource sertikaları için de geçerli. Bu sertifiyı edinmenin önünde bulunan bir bariyer. Buradaki amaç belli: Kurslar aracılığı ile para kazanmak. Gönül isterdi ki ilgi duyan herkes engel olmadan bu sertifikayı edinebilsin. Ne yazık ki internet ortamında imtihan ile ilgili kaynak bulmak kolay değil. Bu serfikanın çok yaygın olmadığının bir göstergesi. Ben bu örnek soruları keşfettim. İmtihan hakkında fikir sahibi olmak için faydalı bir kaynak. İmtihanda başarı sağlamak için SpringSource tarafından hazırlanan Spring Integration Certification Guide‘da yer alan konulara çalışmak yeterli. ![]() Resim 2: Sertifikanın kendisi Bu sertifika sahibi olanlar aşağıda yer alan logoyu kullanabiliyorlar.
|
|
Posted: 11 Apr 2013 06:02 AM PDT Yazılımcılar detaylara olan sevdaları ile tanınırlar. Bir yazılımcı kullanılmak üzere yeni bir API (Application Programming Interface) geliştirdi ise ve kendisinden bu yeni API’nin nasıl kullanıldığının açıklanması istenirse, size detaylar içinde kaybolmanız ve oturum sonunda API’nin nasıl kullanıldığını anlamamanız garantisini veririm.
Bir API’yi kullanmak nedir, önce bunun tanımlamasını yaparak başlamak istiyorum. Bir API ideal bir dünyada bir yazılım modülünün, kendisinin dış dünyadan kullamını mümkün kılmak için dış dünyaya açtığı pencere ya da pencerelerdir. Biliçli olarak bu tanımlamada kapı kelimesini kullanmadım, çünkü pencereyi kullanarak modülün iç dünyasında olup, bitenleri anlamak mümkün değildir. Ama kapı metaforunda kapıyı kullanarak içeri girmek ve olup bitenleri görmek ve anlamak mümkündür. Bu demek oluyor ki bir API kesinlikle modül içinde olup bitenlerin dış dünyaya sızmasına izin vermez ya da vermemelidir. Modülün nasıl çalıştığını API’ye bakarak anlamak mümkün değildir. API sadece modül ile bir kara kutuymuşcasına interaksiyona girmek için kullanılır. Bu API aracılığı ile sadece modülün sunmak istediği servislerin, izin verdiği ve kendisinin tanımladığı şekilde kullanılabilmesi anlamına gelmektedir. Modülün bu servis ya da servisleri kendi iç dünyasında nasıl hazırladığını anlamak, görmek ya da değiştirmeye kalkmak API aracılığı ile mümkün değildir. Tabi modül buna yine API’si aracılığıyla izin veriyorsa, durum değişik olacaktır. Bu tanımlamaya göre bir API kullanıcısı API’nin arkasındaki gizli dünyayı bir kara kutu olarak görür. Bu kara kutuyu programlamış programcı için ise durum farklıdır. Programcı tüm detayları bilir ve API’nin nasıl kullanıldığını tanıttığı bir oturumda bu detaylarda kaybolur gider. Bunun başlıca sebebi yazılımcının kendi geliştirdiği modüle kullanıcı gözlüğünü takarak bakamamasıdır. O her zaman modülü bir beyaz kutu (white box) olarak görür. Bu gözlüğü takarak modülü geliştirir. Bu aslında bir noktaya kadar yapması gereken bir seydir, lakin belli zamanlarda kullanıcı gözlüğünü takabilmelidir. Takamadığı taktirde kullanıcının anlamakta ve kullanmakta güçlük çektiği API’ler oluşabilir. Yazılımcı modül API’sini mutlaka kullanıcı gözlüğü ile tasarlamalıdır. Bunun için en ideal zaman modül için kod geliştirmeye başlamadan öncesidir. Ortada herhangi bir modül yokken, API’si vardır. Bu şekilde düşünmek bile birçok yazılımcıyı zorlar. Yazılımcı hemen oturur ve hayal ettiği şekilde modülü geliştirmeye başlar. Bu esnada kullanılmayan ya da gereksiz birçok metot ve sınıf oluşur. Bunun sebebi yazılımcının modülün nasıl kullanılacağını bilmemesinde yatmaktadır. En kötü ihtimalle yazılımcı modülün işleyiş tarzını kodladıktan sonra API’si hakkında kafa yormaya başlar. Bu API yırtık yamadan farksızdır ve modülün verimli bir şekilde kullanılmasının önünde engeldir. Bunun bir örneği aşağıda yer alan kodda yer almaktadır.
File metaFile = new File("abc");
Storage storage = StorageHelper.createStorage(StorageLoader.load(MetaHelper.createMetaFile(metaFile)));
Reader reader = storage.getReader();
Object obj = reader.getObjectById(1);
Bu kod parçasına ilk bakıldığında ne yaptığını anlamak zor olmamakla birlikte, kullanılması zor olan bir API’yi ihtiva etmektedir. Bu kod örneğinde Storage isminde bir modül mevcuttur. Kullanıcı bu modülü kullanabilmek için Storage, StorageHelper, StorageLoader, MetaHelper gibi aslında pek varlıklarından bile haberdar olmaması gereken sınıflarla boğuşmaktadır. Bunun sebebi bu modülün bir API’sinin olmaması ve geliştiricisinin geliştirme sürecinde bu modül nasıl en kolay bir şekilde kullanılır sorusuna cevap aramamış ya da bu soruya cevap bulamamış olmasıdır. Yazılımcı API zannettiği birçok sınıfı ortaya atmış, hem modülün basit bir API üzerinden kullanılmasını engellemiş, hem de modül icinde olup bitenleri herkesin göreceği şekilde açığa vurmuştur. Bu tarz bir modül ve API oluşturulması gereksiz bağımlılıkları beraberinde getireceğinden, kodun bakımını uzun vadede çıkmaza sokacaktır. Şöyle bir API işi çok daha kolay yapmaz mıydı?
Storage storage = Storage.create("abc");
Object obj = Storage.getObjectById(1);
ya da;
Object obj = Storage.instance("abc").getObjectById(1);
Yukarıda yer alan örneklerde veri tabanından belli bir nesneyi edinmek için önce bir Storage nesnesinin oluşturulması ve akabinde getObjectById() metodun kullanılması yeterli olmaktadır. Modülün API’si sadece bu ya da buna benzer metotlardan oluşmaktadır. Kullanıcının diğer örnekte görüldüğü gibi modül içinde kullanılan StorageLoader ya da MetaHelper gibi sınıfları tanıması ve kullanması şart değildir. Bu modülün kullanımını kolaylaştıran ve API’yi sadeleştiren bir durumdur. Böyle bir API’de kullanıcı sadece Storage sınıfına bağımlı olduğundan, modülün içinde yer alan sınıflar bünyesideki herhangi bir değişiklik kullanıcıyı etkilemeyecektir. Verdiğim ilk örnekte modülün sahip olduğu iç sınıflara bağımlılık doğrudan olduğu için, modülü kullanan kodun kırılganlığı modül üzerinde yapılan her değişiklik ile doğru orantıda artacaktır. API tasarımı modül için gerekli kodun yazılmasından sonrasına bırakılamayacak kadar ciddi bir konudur. Öyle ise API tasarımını kod yazmadan destekleyecek bir yöntemin kullanılmasında fayda vardır. Bu yöntemin ismi test güdümlü yazılımdır (TDD – Test Driven Development). Test güdümlü yazılımın en büyük avantajlarından birisi kod yazmadan, geliştirilmek istenen kod biriminin kullanıcı gözünden görülmesini sağlamasıdır, çünkü yazılan tesler oluşturulan kod birimleri için kullanıcı olma niteliğindedir. Yani aslında test sınıfları API’lere yönlendirilmiş kullanıcılardır. Bu sebepten dolayı testler bir kullanıcının ihtiyaçları doğrultusunda kod birimlerini test ederler. Durum böyle olunca test güdümlü yazılım yaparken oluşturlan testlerde akla gelen ilk soru “ihtiyacım nedir ve API bu ihtiyacımı nasıl giderir” şeklinde olmaktadır. API yoksa oluşturulur, varsa doğrudan kullanılır. Şimdi modül için gerekli kodu yazmadan testler aracılığı ile ihtiyaç duyduğumuz Storage API’sini nasıl oluşturabileceğimizi bir örnek üzerinde inceleyelim. Testler ile birlikte API ve akabinde modül için gerekli kod yavaş yavaş oluşmaya başlayacaktır. İhtiyaç duyduğumuz API’yi aşağıdaki şekilde test ederek başladığımızı farz edelim. Ortada henüz Storage isminde bir sınıf yok, dolayısıyla bu sınıfın getObjectById() isminde bir metodu da bulunmuyor. Ama biz ihtiyaç duyduğumuz API’yi hayal ederek, yani kullanıcı olarak yola çıkarak böyle bir sınıf ve böyle bir metot olsaydı, istediğimiz veriyi veri tabanından böyle edinebilirdik şeklinde hayal ettik ve bunu da test olarak ifade ettik.
@Test
public void
storage_should_deliver_the_value_1(){
Storage storage = new Storage("abc");
Object obj = storage.getObjectById(1);
assertThat(obj.getValue(), equalTo(1));
}
Oluşturduğumuz test bizi ister, istemez sade bir API oluşturmaya itmektedir. Bu tarz bir test yazmak bizi kesinlikle önce bir MetaHelper sınıfı, akabinde bir StorageLoader ve daha sonra veri tabanını kullanıma hazırlamak için kullanılan StorageHelper sınıfını oluşturmaya yönlendirmemektedir, çünkü kullanıcı olarak bunlar bizi ilgilendirmeyen, modülün kendi sorumluluğunda olan konulardır. Bizim kullanıcı olarak tek bir beklentimiz vardır, o da API üzerinden belli bir verinin veri tabanından en kolay şekilde nasıl edilenilebileceği konusudur. Testi oluştururken taktığımız kullanıcı gözlüğü bizi her zaman kullanıcının gereksinimlerini doğrudan tatmin eden bir API’yi oluşturmaya yönlendirmektedir. Kullanıcı gözlüğü modül içinde olup, bitenler ile ilgilenmemektedir, çünkü detayları bilmesi verdiğim ilk örnekteki gibi kafa karıştırıcı tarzda olacaktır. Bu yüzden kullanıcı her zaman en sade API’yi tercih etme iç güdüsüne sahiptir. Yazmış olduğum testin başarılı olması için Storage isminde bir sınıfın oluşturulması ve bu sınıfın getObjectById() isminde bir metodunun olması gerekmektedir. Bu noktadan itibaren detayları bilen yazılımcı gözlüğünü takarak modülü ve içeriğini düşündügüm şekilde geliştirebilirim. Böyle bir API oluştuktan sonra yazılımcı iç güdüsel olarak Storage modülünün tüm işleyiş tarzını kullanıcıdan saklamaya özen gösterecektir, çünkü kullanıcının getObjectById() metodu haricinde başka bir şeye ihtiyacı olmadığını bilmektedir. Bunu kendisine söyleyen yazdığı birim testidir. Birim testi modülün kullanılma şekillerinden birisini yazılımcıya göstermiştir. Storage modülü için yapılabilecek implementasyonlardan birisi şu şekilde olabilirdi.
public class Storage{
private static Storage storage;
private Reader reader;
public static Storage create(String dir){
if(storage == null){
storage = new Storage(dir);
}
return storage;
}
private Storage(String dir){
initStorage(dir);
}
private void initStorage(String dir){
File metaFile = new File(dir);
reader = StorageHelper.createStorage(StorageLoader.load(MetaHelper.createMetaFile(metaFile))).getReader();
}
public Object getObjectById(int i){
return reader.getObjectById(i);
}
class Reader{
...
}
class StorageHelper{
...
}
class StorageLoader{
...
}
class MetaHelper{
...
}
Storage modülünü bir singleton olarak implemente ettim. Görüldüğü gibi Storage.create() statik metodu ile singleton olan bir storage nesnesi edinmek mümkündür. Akabinde bu nesneyi kullanarak getObjectById() metodu aracılığı ile veri tabanından istediğim türde bir veriyi çekebilmekteyim. Bunun yanısıra modül bünyesinde kullanılan tüm sınıfları iç sınıf olarak tanımladım. Bu şekilde dış dünyadan hiç kimse bir Reader ya da StorageHelper nesnesi oluşturamaz ya da kullanamaz. Storage modülü bu şekilde iç dünyasında olup bitenleri tamamen dış dünyadan gizlemekte ve tanımladığı iki metot aracılığı ile kullanımını mümkün kılmaktadır. Bu iki metot Storage modülünün API’sidir. Storage modülünün kara bir kutu gibi işlev görmesi, kullanıcıları etkilemeden modül bünyesinde değişiklik yapılabilmesini mümkün kılmaktadır. Yapmış olduğumuz API tanımlamasına “API kullanıcı ve sunucu arasında bir kullanım sözleşmesidir” ibaresini ekleyebiliriz. Nitekim bir modül sahip olduğu API’si aracılığıyla dış dünyaya nasıl kullanılabileceğinin mesajını verir. Kullanıcılar istedikleri API metotlarını seçerek modülü kullanmaya başlarlar. Bu kullanıcı ve modül arasında bir bağ oluşturur. Kullanıcı her zaman oluşan bu bağın sağlam olmasını arzular. Değişikliğe uğrayan API kullanıcılarını kırılgan hale getirir. Bunu engellemek için API’nin sıkça değişmemesi şarttır. Durum böyle olunca bir defa kullanıma sunulan bir API’nin değiştirilmesi artık kolay değildir, çünkü sayısı bilinmeyen birçok kullanıcısı vardır. Bunu göz önünde bulundurduğumuzda API tasarımcısı olarak her zaman tutucu (konservatif) bir pozisyonda durarak oluşturduğumuz sınıfları öncelikle iç sınıf ya da package private olarak tanımlamamız gerekir. Sadece bu durumda hemen keşfedilerek kullanılmalarını engelleyebiliriz. Kullanımını engelleyemediğimiz sınıflardan her zaman sorumluyuzdur. Bir API kullanıma açıldığında o API’de ne kadar sınıf ve metot mevcutsa, modülün ömrü boyunca bu sınıflara ve metotlara destek vermek zorunda kalırız, çünkü kullanıcılarını API’yi değiştirerek sinirlendirmemiz gerekir. Bu yüzden API’nin çapı ne kadar küçükse, sorumluluk ve verdiğimiz desteğin oranı o oranda küçük olacaktır. API’lerin kullanıcılara hitap edecek ve onların gereksinimleri doğrudan ve kolay bir şekilde tatmin edecek şekilde tasarlanmaları şarttır. Windows ortamında C/C++ ile Windows API’sini kullanarak uygulama geliştirenler çok iyi bilirler. Bu API en zor API’lerden bir tanesidir. API sadece birkaç sınıf ya da metot birleşimi bir sey değildir. Aynı zamanda API’nin ihtiva ettiği sınıf ve metot isimlerinin ifade gücünün yüksek olması gerekir. Her sınıf ya da metot için seçilen isim bu API’nin arkasındaki gizli servisin nasıl kullanılabileceğini ifade edebilmelidir. Unutmayalım programlar bilgisayarlar için yazılıyor, lakin kodu okuyan insanlar. Bu yüzden ifade gücü yüksek olan isimlerin seçilmesi API’nin doğru kullanımı kolaylaştıracaktır. Bol TeletAPI’li günler diliyorum.
|
|
Posted: 03 Apr 2013 07:43 AM PDT Son zamanlarda en çok merak edip, öğrenmek istediğim diller arasında geliyor Ruby. Ruby herkesin dilinden düşürmediği, dinamik veri tipli ve her şeyin istisnasız nesne olduğu bir programlama dili. 1993 yılında Yukihiro Matsumoto tarafından geliştirilmiş. 2006 senesinde Ruby on Rails çatısının oluşturulmasıyla popüler bir web programcılığı dili olmuş.
Bu yazımda bir Java programcısının, yani benim Ruby dilini nasıl algıladığını, bu dilin kendisini nasıl hissettirdiğini sizlere aktarmak istiyorum. Java’da her zaman şöyle bir şey yapmak istemişimdir: throw new Exception() if(abc....); Böyle bir yapı Java’da mümkün değil. Onun yerine her zaman şöyle bir şey yazmak zorundasınız:
if(abc...)
{
throw new Exception();
}
Tek bir satır ile ifade etmek istediğim bir düşünce için Java’da dört satır kod yazmam gerekiyor. Java’nın en büyük sorunlarından birisi bu. Bu zamanla Java’da yazılan programların okunamaz hale gelmesine sebep oluyor. İşin içine birde hata yönetimi (Exception Handling) girdi mi, kodu anlayana aşk olsun. Ruby’de dikkatimi çeken ve Java’da hasretini çektiğim if ve unless yapıları oldu. Ruby’de şöyle bir şey mümkün: puts 'hello world' if x==4 puts 'hello world' unless x==4
unless x == 4
puts 'hello world'
end
order.calculate_tax unless order.nil? Belli bir düşünceyi Ruby’de olduğu gibi tek bir satır olarak ifade etmenin kodun okunabilirliği açısından çok büyük getirisi var. Bu satırı bir blok halinde yazmak elbette mümkün. Lakin bu ifade edilmek istenen düsüncenin kod arasında kaybolma rizikosunu artırıyor. Burada Ruby’nin takip ettigi filozofinin “ne kadar az kod, o kadar çok ifade edilmek istenen düsüncenin ön plana çıkması” olduğu belli oluyor. Aynı sadelik Ruby’nin while ve until döngüleri için de geçerli: x = x+1 while x < 10 x = x-1 until x == 10 Ruby’de her şey nesne: >> 4.class ==> Fixnum >> 4+4 ==> 8 >> 4.methods ==> ["inspect", "%", "<<", "singleton_method_added", "numerator","*","+","to_i"......] >> true.class ==> TrueClass >> false.class ==> FalseClass Java’da 4 rakamı int primitif tipine sahip iken, Ruby’de Fixnum tipinde bir nesne. 4.methods bu nesnenin sahip olduğu metotları sıralıyor. Ruby safkan bir nesneye yönelik programlama dili. Ruby’de duck typing ismi verilen ilginç bir konsept uygulanmış. Bunun ne olduğunu açıklamadan önce bir Java örneği sunmak istiyorum.
public void printFileName(File file)
{
if(file.exists())...
}
printFileName() ismini taşıyan metodun file isminde bir parametresi bulunuyor. Metot gövdesinde bu parametrenin sahip olduğu sınıf tipine göre değişik metotlar koşturabilirim. Bunlardan bir tanesi exists() metodu. Bu metodu kullanabilmem için file ismindeki metot parametresinin File sınıfından türetilmiş olması gerekiyor, aksi taktirde exists() metodunu kullanamam. Java’da bir nesnenin sahip olduğu tip o nesnenin sınıfı tarafından belirleniyor. File tipinde olan bir değişken sadece File sınıfının metotlarını kullanabilir. Ruby’de durum farklı. Aradaki farkı görmek için bir Ruby örneğini inceleyelim:
def copy_file(obj, dest_path)
src_path = obj.get_path
puts "Copying file \"#{src_path}\" to \"#{dest_path}\"."
end
copy_file() metodunun obj ve dest_path isminde iki tane parametresi bulunuyor. Bu iki parametrenin hangi tipte olduğu belli değil. Tipini, yani sınıfını tanımadığım bir nesnenin nasıl olurda obj.get_path() şeklinde bir metodunu koşturubilirim? obj nesnesinin ait olduğu sınıfta get_path() isminde bir metod olduğunu nereden biliyorum? Bilmiyorum, bilmek te zorunda değilim. Ruby’de her şey nesne. Durum böyle olunca Ruby’nin kontrol etmesi gereken tek şey, kullanmak istediğim metodun nesnede var olup, olmadığı. Eğer kullandığım metodu Ruby nesnede bulamassa NoMethodError hatası oluşturur. Bunu da yakalayıp, gerekli işlemi yapmak zor değil. Buradan çıkardığımız sonuç şu: Ruby nesneleri üzerinde istediğim herhangi bir metodu koşturabilirim. Eğer nesnenin böyle bir metodu varsa, nesne istediğim şekilde davranış sergileyecektir. Aksi taktirde NoMethodError hatası oluşacaktır. Bu Ruby’de nesne tipinin sahip olunan sınıfa göre değil, nesnenin ihtiva ettiği metotlara göre belirlendiği anlamına geliyor. Ruby’de Java ya da C# da olduğu gibi bir sınıf tanımlamak zorunda kalmadan şu şekilde bir metot tanımlanabiliyor:
def bugunHavaSicakMi
true
end
Ruby’de her fonksiyon bir değer geri veriyor. Eğer metodun son satırında return komutu kullanılmadı ise, o zaman son satırda yer alan değer geri veriliyor. Yukarıda yer alan örnekte geri verilen deger true olacaktır. Bir array şu şekilde tanımlanabiliyor: >> hayvanlar = ['Kedi', 'Fare', 'Yilan'] ==> ["Kedi", "Fare", "Yilan"] >> puts hayvanlar Kedi Fare Yilan >> hayvanlar[0] ==> Kedi >> hayvanlar[10] ==> nil >> hayvanlar[-1] ==> "Yilan" >> hayvanlar[0..1] ==> ["Kedi", "Fare"] Var olmayan bir array elementine erişmek istediğimizde Ruby nil değerini geri veriyor. Java’da bu IndexOutOfBoundException hatası olurdu. Index olarak eksi değerler kullanıldığında Ruby arrayi sondan başa doğru görmeye başlıyor. Örneğin hayvanlar[-1] “Yilan”, hayvanlar[-2] “Fare” değerini geri verir. Ruby ile ismi olmayan anonim fonksiyonlar tanımlanabiliyor. Bu anonim fonksiyon başka metotlara parametre olarak ta vermek mümkün. Aşağıdaki örnekte hello world ekranda üç sefer görüntülenir. Küme parantezi içinde yer alan kod aninim fonksiyondur.
3.times {puts 'hello world'}
Anonim fonksiyonları parametrelendirmek mümkündür. Aşağıdaki örnekte each bir for döngüsü oluşturup, hayvanlar isimli listenin her elementini döngü içinde anonim fonksiyona parametre olarak vermektedir. Tek satırlık kod ile tüm listenin içeriğini ekranda görüntülemek münkündür.
hayvanlar = ['Kedi', 'Fare', 'Yilan']
hayvanlar.each { |a| puts a}
Java’da anonim fonksiyon tanımlamak için bir anonim class implementasyonu oluşturmak gerekiyor. Bir listedeki değerleri anonim bir fonksiyon tarafından ekranda görüntülemek için aşağıdaki şekilde Java kodu yazardık:
package xxx;
import java.util.ArrayList;
import java.util.List;
public class Test {
final static List<String> animals = new ArrayList<String>() {
{
add("dog");
add("cat");
}
};
private static abstract class AnimalLister {
abstract void show(String animal);
}
public static void main(String[] args) {
each(new AnimalLister() {
@Override
void show(String animal) {
System.out.println(animal);
}
});
}
private static void each(AnimalLister animalLister) {
for (String animal : animals) {
animalLister.show(animal);
}
}
}
Java’da bir sınıfa yeni bir metot eklemek için ya o sınıfı yeniden derlemek, ya AOP kullanmak ya da sınıfı bytecode seviyesinde manipüle etmek gerekiyor. Ruby’de mevcut bir sınıfa yeni bir metot eklemek çocuk oyuncağı:
class Fixnum
def my_times
i=self
while i > 0
i = i-1
yield
end
end
end
3.my_times {puts 'hello world'}
Yukarıda yer alan Ruby örneğinde Fixnum isimli sınıfa my_times isminde yeni bir metot ekledik. Oluşturduğumuz bu yeni metodu 3.my_times şeklinde kullanabiliriz. JDK içindeki bir sınıfa doğrudan yeni bir metot eklemek istediginizi düşünün. Ruby’de mevcut her sınıfa yeni bir metot eklemek mümkün. Java gibi nesneye yönelik programlama yapılan dillerde ortak kullanılan metot ve değişkenler kalıtım yolu ile altsınıflara verilir. Ruby’de bu mekanizma modüller aracılığı ile işlemektedir. Bir modül içinde fonksiyonlar ve değişkenler yer alır. Herhangi bir Ruby sınıfı tanımlı bir modülü kendi bünyesine kattığında, modül içinde yer alan tüm metot ve değişkenler sınıf tarafından kullanılabilir. Aşağıda yer alan Ruby örneğinde ToFile isminde bir modül yer alıyor. to_f metodu to_s metodundan gelen değeri bir dosyaya ekliyor. to_s metodu modül bünyesinde tanımlı değil. Bu metodun modülü bünyesine katan sınıf tarafından implemente edilmiş olması gerekiyor. Ruby daha önce bahsettiğim duck typing yönetimi ile nesnenin to_s metoduna sahip olup, olmadığını kontrol ediyor. Person sınıfı include ToFile ile modülü bünyesine katıyor ve to_f metodunu kendi bünyesinde tanımlıymış gibi kullanabilir hale geliyor.
module ToFile
def filename
"object_#(self.object_id).txt"
end
def to_f
File.open(filename, 'w') {|f| f.write(to_s)}
end
end
class Person
include ToFile
attr_accessor :name
def initialize(name)
@name = name
end
def to_s
name
end
end
Person.new('özcan acar').to_f
Bu örneği Java’da aşağıdaki şekilde yazabilirdik. Java’da sadece tanımlı olan metotlar kullanılabilir. Ruby ToFile modülü örneğinde yer alan to_s metodunu kullanabilmek için bu metodun bir sınıf bünyesinde tanımlı olması gerekir. Aşağıda yer alan örnekte To_S isminde bir interface sınıf tanımlayarak, to_s metodunu bu sınıfa ekliyoruz. Soyut olan ToFile sınıfı bu interface sınıfı implemente ettiği için to_s metodunu tanır hale geliyor. Person sınıfı ToFile sınıfını genişlettiği için to_s metodunu implemente etmek zorunda. Person sınıfı ToFile sınıfını genişlettiği için to_f metoduna sahip oluyor. Person nesnesi tarafından to_f metodu koşturulduğunda, to_f metodu person nesnesi bünyesinde yer alan to_s implementasyona erişip, gerekli değeri edinebiliyor. Ruby ile Java arasındaki en büyük fark, Java’nın kullanılan metotların hangi sınıflara ait olduklarından haberdar olması zorunluluğu. Ruby duck typing mekanizması ile bu zorunluluğu ortadan kaldırıyor.
public interface To_S {
String to_s();
}
public abstract class ToFile implements To_S {
public String filename() {
return this.getClass().getSimpleName();
}
public void to_f() {
FileUtils.writeStringToFile(new File("xxx"), to_s());
}
}
public class Person extends ToFile{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String to_s() {
return this.name;
}
public static void main(String[] args) {
new Person("özcan acar").to_f();
}
}
Bu kısa tanıtım umarım Ruby diline ilginizi çekebilmiştir. Ruby dilinin ifade edilmek istenen düşüncenin çok az satır ile, düşüncenin kod kargaşası arasında kaybolmadan implemente edilmesini mümkün kılması beni etkileyen en önemli özelliği oldu. Benim için ön planda olan kodun nasıl yazıldığı değil, düşüncenin kod olarak nasıl ifade edildiğidir. Programcılıkta zamanla ilgi alanı problem çözme, algoritma kullanımı vs. gibi konulardan, düşüncelerin sade ve çok kısa kod parçası olarak kodlanabilmesi yönüne kaymaktadır. Bu en azından benim için geçerli olan bir durumdur. Bu sebepten dolayı Ruby gibi düşüncelerin sade ve yüksek bir soyutluk seviyesinde ifade edilmesine izin veren dilleri tercih ederim.
|
|
Posted: 22 Feb 2013 12:14 AM PST Birçok meslekte araç ve gereç sahibi olmadan iş yapmak mümkün değildir. Günlük hayatımızda da birçok araç ve gereci kullanırız. Örneğin bir resmi duvara asabilmek için bir çivi ve bir çekic kullanırız. Çoğu zaman işimizi gördükten sonra başarımız ile övünür, bir sonraki ihtiyacımıza kadar kullandığımız araçları hatırlamayız.
Araçlar zamandan tasarruf etmek ve işimizi kolaylaştırmak için vardır. Bazı araçlar olmasaydı, isteğimiz işi yapmamız bile mümkün olmazdı. Çekiç sahibi olmadan duvara çivi çakmak mümkün değildir. Bunun için bir taş parçası bile kullansak, kullandığımız bu taş parçası bir araç haline dönüşür. Yazılımcı olarak ta günlük hayatımızda birçok araç kullanırız. Bunların başında kod yazmak için kullandığımız IDE (Integrated Development Environment) araçları gelir. Ben günlük işlerimde genellikle Eclipse IDE’yi tercih ediyorum. Araçlar işimizi kolaylaştırmak ve zamandan tasarruf etmek için var demiştim. Ne yazık ki bazı yazılımcılar bunun bilincinde değiller. Gerekli araç ve gereçleri kullanmalarına rağmen, zamandan tasarruf ettikleri söylenemez, çünkü kullandıkları araçların hakkını veremiyorlar. Kod yazmak için kullanılan bir aracın en verimli kullanım şekli, bazı aksiyonlar (örneğin yeni bir sınıf oluşturma, yeni bir metot oluşturma) için kısa yol tuşlarını tanımak ve uygulamaktır. Örneğin bir interface sınıfını implemente etmek istediğimizde, implementasyon sınıfında tek tek metot gövdelerini oluşturmak yerine Eclipse altında STRG+1 tuşlarına baştıktan sonra Add unimplemented methods seçimini yapmak yeterlidir. IDE otomatik olarak interface sınıfı bünyesinde yer alan her metot için implementasyon sınıfında gerekli metot gövdesini oluşturacaktır. Bu işlemi elden yapmak çok büyük bir zaman kaybıdır. Bu aracı kullanıp, hakkını verememektir. Bir yazılımcının en önemli araçlarından birisi kullandığı IDE olduğuna göre, IDE’nin sundugu imkanlar doğrultusunda yaptığı işlemlerin sürelerini kısaltmanın yollarını aramalıdır. Bunun yolu kısa yol tuşu hakimiyetinden geçmektedir. Bunun yanısıra fare kullanımından sakınmalıdır. Fareye uzanan el bir thread context switch gibidir. İş akışını bir an için bile olsa böler. Programcının en önemli aracı klavyesidir. Klavyeye ek olarak fareyi kullanması, klavyenin hakkını veremediği anlamına gelir. Aynı işi tek bir araç ile yapabilecekken, iki değişik araç kullanarak zaman kaybeder. Bir başka zaman kaybı türü de yazılımcının on parmak kod yazamamasıdır. Yazılımcının kod kaynağına bakacağı yerde, x tuşu nerede diye klavyeye bakması context switch’dir. Devamlı bu şekilde çalışmak yazılımcıyı hem yorar, hem verimini düşürür hem de zaman kaybetmesine neden olur. Bu sebepten dolayı her yazılımcının on parmak kod yazabilme yetisini geliştirmek için çalışma yapması zaruridir. Yazılım otomasyon demektir. Otomasyonun olmadığı yerde zaman kaybı var demektir. Ant ya da Maven ile otomatik sürüm oluşturmak yerine, bu işi her defasında elden yapmaya çalışmak çok büyük zaman kaybıdır. Yazılımcının bu noktada zamanının belli bir kısmını sürüm oluşturma araçlarının kullanımını öğrenmek için harcaması, bu yatırımın sürümlerin daha hızlı ve otomatik oluşturularak amortize olmasını sağlayacaktır. Kullandığı araçlara hakim olmayan bir ustaya kim usta gözüyle bakar? Elimizin övünmesini istiyorsak, kullandığımız araçlara hakim olduğumuzu gösterelim. Eklenti (17:51 . 22.02.2013): Bir yazılımcı için kullandığı en önemli araç bilgisayarıdır. Klasik bir iş bilgisayarları okuma kafası dakikada 5000 ila 7000 arasında dönen sabit disklere sahiptir. Taş devrinden kalma bu teknoloji yüzünden yazılımcılar hergün dakikalar ya da saatlerle ölçülebilen zaman kaybına uğruyorlar. Windows gibi bir işletim sistemi ve yetersiz hazıfa (ram) kapasitesi buna eklendiğinde yazılımcıların mesaileri kabusa dönüşebiliyor. Bunun önüne geçmek için yazılımcını 64 bit genişliğinde, SSD sabit diskine ve en az 8 GB hazıfa alanına sahip bir bilgisayar kullanmalıdır. Bunun altindaki her konfigürasyon işletim sisteminin akışkanlığını bozarak, yazılımcının belli aktiviteler için bekleme süresini artıracaktır ve verimliliğini düşürecektir.
|
|
Posted: 18 Feb 2013 02:13 AM PST BTSoru.com‘da bir soru sorulmuş. Tüm soruları kontrol ederek ihtiyaç durumunda başlığı, içeriği ve etiketleri düzenlemeye çalışıyorum. Bu soruyu incelerken verilen kod örneğini Struts kodu zannettim ve sorunun etiketini Struts olarak değiştirdim.
Kısa bir zaman sonra soruya bir yorum yapan bir arkadaşımızdan bir uyari e-postası aldım. Arkadaş iletisinde etiketin asp.net-mvc olmasının daha anlamlı olacağını bildiriyordu. İlgisi için kendisine teşekkür ettim ve etiketi asp.net olarak değiştirdim. Bilmediğim bir şey vardı: asp.net ve asp.net-mvc iki değişik şeydi. Kısa bir zaman sonra aynı arkadaşımızdan tekrar bir uyarı iletisi aldım. Çok kibar bir şekilde asp.net ile asp.net-mvc’nin iki ayrı teknoloji olduğunu belirtiyordu. Özür dileyerek sorunun etiketini asp.net-mvc olarak değiştirdim. Buradan çıkarabileceğimiz bazı dersler var. Bunlar:
Hz. Ali’nin güzel bir sözü var: Bana bir harf öğretenin kırk yıl kölesi olurum… Ditto!
|
|
Posted: 17 Feb 2013 03:16 AM PST Programcı takım arkadaşı ile kodu gözden geçirme (code review) seansı yapıyor. Kodun içinde bulunduğu durumdan hoşnut değil, lakin bunu takım arkadaşına söylemiyor. Arkadaşının yanlış anlamasından mı korkuyor?
Tüm yazılımcılar iterasyon sonunda bir araya gelerek geri bakış (retrospective) seansı yapıyorlar. Bazı programcılar boğazlarına kadar dolmuşken, bunun nedenini takım arkadaşlarıyla paylaşmıyorlar. Çok mu sabırlılar? Günlük stand-up toplantı yapılırken bazı programcılar planın gerisinde kaldıklarını, üzerinde çalıştıkları kullanıcı hikayesinin zamanında tamamlanmasının münkün olmadığını söylemiyorlar. Ekip arkadaşları önünde küçük düşmekten mi korkuyorlar? Programcı başka bir ekip arkadaşının yazdığı kodu değiştirmeye çekiniyor. Aynı şeyin kendi yazdığı kodun başına gelmesini mi istemiyor? Verdiğim örneklerin hepsinde bir tikanıklık ve blokaj söz konusu. Bahsettiğim programcılar samimi ve açık sözlü değiller. Bu ne yazık ki çevikliği öldüren, şeker hastalığı gibi çok sinsi bir şey. Çevikliğin temel değer sistemini cesaret, iletişim, geribildirim ve basitlik oluşturuyor. Bunların olmadığı yerde çevik olunması imkansız. Kimsenin etlisine, sütlüsüne karışmamak çevik süreci sabote etmek gibi bir şeydir. Herkes açık sözlü olma cesaretine sahip olmayabilir. Ama bunun için gerekli ortam oluştuğunda açık sözlü olmamak için de bir sebep yoktur. Bir retrospektive seansında kimseyi suçlamadan ve küçük düşürmeden yanlış giden şeyler üzerinde konuşulabilir. Fikir birliği sağladıktan sonra yolunda gitmeyen şeyleri tekrar rayına sokmak zor değildir. Burada önemli olan samimi olmak ve yanlış giden şeyleri açık sözlülükle dile getirmektir. Yazılım bir ekip işidir. Bireyin başarısı takımın başarısıyla ya da başarısızlığıyla doğrudan orantılıdır. Tüm takım aynı bot içinde oturduğuna göre, botun ilerleyebilmesi için herkesin aynı şevk ile kürek çekmesi gerekir. “Bot su alıyor, ama bana ne“, “bazı arkadaşlar kürek çekmiyor, benim derdim değil“, “bazı arkadaşlar kürek çekme tekniğine tam hakim değiller, kim uğraşacak şimdi onlarla” gibi düsüncelere sahip olmak botu hedefine ulaştırmaz. Samimiyet ve açık sözlülük programcının takım içindeki saygınlığını artırır. Bunun tersi durumunda halka en zayıf yerinden kopacaktır. O en zayıf nokta samimi ve açık sözlü olmayan programcının kendisidir. Bunu herkes bilmesede kendisi çok iyi bilir.
|
|
Java Generics Get ve Put Prensibi Posted: 30 Jan 2013 05:00 AM PST Java Generics and Collections kitabında Get and Put isminde bir prensipden bahsediliyor. Java Generics wildcard kullanımı bu prensip takip edildiğinde daha kolaylaşıyor. Prensip şöyle:
Bunu bir örnek ile açıklayalım. Aşağıdaki metot bir listeden diğer bir listeye nesneleri kopyalamak için kullanılan jenerik bir metot.
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
copy metodunun ilk parametresi nesnelerin kopyalanacağı, yani put yapılacak olan liste, ikinci parametresi nesnelerin alındığı, yani get edildiği liste. Şu şekilde bir listeyi diğer bir listeye kopyalayabiliriz:
List<Object> objs = Arrays.<Object>asList("String", 1, 3.14);
List<Integer> ints = Arrays.<Integer>asList(1,2);
copy(objs, ints);
dest isimli parametre put işlemi yapıldığı için super wildcard’ını, src isimli parametre get yapıldığı için extends wildcard’ını kullanıyor. Bir iterator kullanıldığında bir yapıdan get işlemi ile değerler edinilir. Bu durumda extends wildcard kullanılmalı. Aşağıdaki örnekte sum metodu bir listede yer alan Number sınıfı ve altsınıflarından oluşan nesnelerin değerlerini topluyor. Buna göre bu metodu sum(Arrays. asList(1, 2)) ya da sum(Arrays. asList(1.1, 2.2)) şeklinde koşturmak mümkün. Eğer extends wildcard kullanılmasaydı Integer ya da Double nesneler ihtiva eden bir listeyi sum metoduna parametre olarak vermek mümkün olmazdı.
public static double sum(Collection<? extends Number> nums) {
double s = 0.0;
for (Number num : nums)
s += num.doubleValue();
return s;
}
EOF (End Of Fun) |
|
Programcılık Çıtası Yükseliyor Posted: 29 Jan 2013 10:21 AM PST Ben Java’nın ilk günlerinden beri bu dili kullanan bir programcıyım. 1998 senesinin bir günü kampüste laflarken bir arkadaşım yeni haberini aldığı Servlet teknolojisinden bahsetmişti. Bugün gibi hatırlıyorum: “vay… demek Java ile appletler harici web programcılığı yapılabiliyor….” demiştim. Java ile geçirdiğim ilk yıllarda hakim olmam gereken konular JDBC, Reflection, RMI ve Servlet gibi teknolojilerle sınırlı idi. On beş sene sonra durum çok farklı! Başlangıçta küçük bir havuzda yüzerken, şimdilerde kocaman bir okyanusun içindeyim ve bu okyanusun ucu, bucağı yok.
Java dili eklenen her yeni teknoloji ile günümüzde bir platform haline geldi. Artık Java dili bu platform üzerinde kullanılan dillerden sadece bir tanesi. JVM üzerinde koşturulabilen Scala, JRuby, Groovy, Clojure gibi birçok dil mevcut. Bundan beş, altı sene öncesine kadar tipik bir Java programcısı JMS, JTA, EJB, JAXB, JSF, JSP, JAX-WS gibi çeşitli Java çatılarına hakimliği ile övünebilirken, durum bugün çok daha değişik. İsmi geçen çatılar bugün bir Java programcısının repertuarında var olması gereken şeyler. Hepsine hakim olmak bir marifet değil, bir zorunluluk haline geldi, çünkü piyasa bir Java programcısından bunu bekliyor. Durum sadece bununlu sınırlı değil. Artık JVM üzerinde çalışabilen dört yüze yakın programlama dili mevcut. Bunların başında daha önce ismini verdiğim Scala, Clojure, JRuby, Groovy ve Jython geliyor. Doğal olarak piyasada bu dilleri kullanarak geliştirilen proje sayısı göz ardı edilmeyecek kadar fazla. Twitter, LinkedIn ve FourSquare Scala dili ile geliştiriliyor. Bu durumda klasik bir Java programcısının bu dillere yönelme haricinde fazla bir alternatifi kalmıyor. Bu ekstra öğrenim yükü demek. Bu durum sadece Java dilini kullanan programcılar için geçerli değil. Net platformunu kullanan programcılar da birçok değişilik ile baş etmek zorundalar. Aynı şey mobil programcılar için de geçerli. Programcılar gidişatı tam anlamıyla kavrayabilmek için ilgi alanlarına giren her şeyi tam teşekküllü takip etmek zorundalar. Bu programcının şimdilerde onlarca blog sitesini takip etmesi, yüzlerce kitap okuması, onlarca yeni programlama dili öğrenmesi anlamına geliyor. Programcının sahip olduğu bilgilerin yarı ömrü aylarla ölçülür oldu. Bugün profesyonel programcı olarak çalışan insanların çok büyük bir kesimi imperatif dilleri kullanıyor. Bu dillerde eğitim aldılar ve tüm düşünce yapıları bu dillerin sunduğu yapılar üzerine kurulu. Bu programcıların çoğu bugünlerde büyük bir kültür şoku yaşıyorlar. Son günlerde fonksiyonel dillere olan ilgi çok artmış durumda. İmperatif bir dünyadan gelen bir programcının fonksiyonel dillere adapte olması ve anlaması kolay değil. Closure, lambda, higher order functions, functions as first class citizen, recursiv programming, pure functions gibi terimler imperatif programcıların kafasını karıştırmış durumda. Ama piyasa bu konseptleri anlamış programcılar istiyor. Er ya da geç imperatifcilerin fonksiyonel programcılık konseptlerine vakif olmaları gerekiyor. Programcılara yönelik beklentiler yükselmiş durumda. İs görüşmelerinde de büyük bir değişimin yaşandığı bir gerçek. Eskiden piyasada programcı bulduklarında öpüp, başına koyan iş verenler, şimdilerde adayları intensif görüşmeler ve testler ardından işe alıyorlar ya da almıyorlar. İş bulmak zorlaştı, çünkü bir programcıda aranan vasıflar değişti. Artık CV’de yer alan kelimelere göre programcı alımları son bulmuş durumda. İsteyenin parmağını kaldırıp, “Java kelimesinin nasıl yazıldığını biliyorum, ben programcıyım” diyerek iş buldugu devir artık son buldu. Günümüzde programcı olmak ve kendisini programcı olarak yetiştirebilmek çok güç bir hale geldi. Vakıf olunması gereken çok çesitli konu ve konseptler mevcut. Bu yeni süreç artık işine muazzam derecede hakim programcılar doğuracak. Bu işi kıvıramayanlar daha işin başında iken elenip, piyasadan uzak tutulacaklar, çünkü piyasada onların yapabileceği iş olmayacak. Programcı olma çıtası on sene öncesine göre çok yükseldi ve yükselmeye devam ediyor. Bu güzel bir gelişme. Artık akla, karanın ayrıştığı bir dönemdeyiz. Programcılık hafife alınabilecek ve yarım yamalak bilgi ile yapılabilecek bir iş değil. Bu işi çalışma sahalarındaki her şeyi en ince detayına kadar anlamış, öğrenme kapasiteleri yüksek, değişikliklere adapte olan ve yeniliklerden çekinmeyen programcılar yapmalı. Çıtanın yükselmesi doğal bir seleksiyon yöntemi. Er ya da geç gerçekleşecekti. Bu işe gerçek anlamda gönül verenler ilerleyebilecek, para icin yapanlar ise çok kısa bir zamanda kendilerine baska çalışma alanları aramak zorunda kalacaklar, çünkü bu değişime ayak uydurmaları imkansız. İyi programcıların ağaçta yetişmediği bir gerçek. Bir programcının iyi bir programcı olabilmesi için çok okuması, öğrenmesi, öğrendiklerini uygulaması ve yenilikleri takip etmesi gerekiyor. Bu vasıflara sahip olan programcıları tanımak zor değil. Sahip olmayanları da tanımak zor değil. Bu işin başında olanlar iş bulamayacak tezini savunmuyorum. Yaşadığımız bu gezegende bugün düne nazaran çok daha fazla programcıya ihtiyaç var. Ama bu ihtiyacın gözü eskisi gibi kör değil. Neyi, hangi tip programcıyı aradığını iyi biliyor ve seçimini ona göre yapıyor. Bu işe gönül verenler, bu işin başlangıcında da olsalar bu ihtiyaca her zaman cevap verebilecek yeteneklere sahip olacaklar. Zaman içinde bilgilerine bilgi, tecrübelerine tecrübe katarak çok daha iyi programcılar olacaklar. Çatının yükselmesi onlar için bir sorun olmayacak, çünkü çatıyı yükselten kendileri olacak. Gelecekte bu tip programcılar ile çalışmak çok ama çok zevkli olacak. EOF (End Of Fun) |
|
Posted: 04 Jan 2013 08:00 AM PST Yazılım camiasında son zamanlarda dikkat çeken bir değişim furyası var. Cevabı aranan soru şu: Yazılım ekibi nasıl daha verimli hale getirilebilir? Bu aslında organizasyonel bir değişimin gerekli olduğu bilincinin oluştuğu anlamına geliyor. Yöneticiler ekiplerini daha çevik hale getirmek için çeşitli yöntemlere başvuruyorlar. Bunların başında örneğin ekibin topluca eğitilmesi geliyor.
Her eğitim şüphesiz ekibe ve bireylerine bir şeyler katıyor. Ama bunu ölçmek çok zor. Bunun yanı sıra cevaplanması gereken bir soru daha var: Eğitim ile istenilen değişiklik sağlanabilir mi? Alınan eğitimler değişimin sadece başlangıcı niteliğinde olabilir. Bunu yeni bir dil öğrenme süreci ile kıyaslayabiliriz. Öğrenilen her yeni kelime, kelime hazinesini genişletir. Pratik yapılarak kullanılmayan bir kelime hazinesi hiçbir şey ifade etmez ve değeri yoktur. Yazılım ekiplerinin tükettikleri eğitimler genelde sahip oldukları metot hazinelerini genişletmekle birlikte, bu metot hazinesinin günlük işlerde pratiğe dökülmemesi istenilen değişimi sağlayamamaktadır. Değişime gitme çabalarının son bulduğu nokta burasıdır. Değişim yönetilmesi gereken bir süreçtir. Tesadüflere ve ekibin inisiyatifine bırakılabilecek bir şey değildir. Bir yazılım ekibi rasyonel düşünebilen, soyutlayabilen, problem çözme yetisi gelişmiş bireylerden oluşabilir. Ama unutmamak gerekir ki ekibi oluşturan bireylerin kendilerine has alışkanlıkları vardır. Değişim için gerekli baskı azaldığında ya da ortadan kalktığında bireyler bu alışkanlıklarına geri dönerler. “Ekip belli bir eğitimi aldıktan sonra artık çalışmalarını bu yönde adapte edecektir, dışarıdan müdahale etmeye gerek yok” şeklinde düşünmek bir yanılgıdır. Bu şekilde ekibin inisiyatifine bırakılan değişim süreci başladığı gibi son bulur. Bunun örneklerini danışmanlık hizmeti verdiğim şirketlerde gördüm. Örneğin bir defasında yazılım sürecini daha çevik hale getirmek için ekibin test güdümlü yazılım eğitimi alması tavsiyesinde bulundum. Ekip gerekli eğitimi aldı. Ekip içinde test yazma duyarlılığı arttı, lakin ekibin hiç bir bireyi gerçek anlamda test güdümlü yazılım yapmadı. Kısa bir zaman sonra yazılımcılar eski davranış biçimlerine geri döndüler. Şimdi yine eskiden olduğu gibi kod yazıyorlar. İstenilen değişimin gerçekleşebilmesi için şu adımların atılması gerekir(di):
Yazılımcılar yetenekli insanlar. İstenilen bir şeyleri kavramaları uzun zaman almaz, lakin uygulamada zorluk çekebilirler. Yönetimin yazılım ekibine gerekli desteği sağlaması ve bu sorumluluğu onlarla paylaşması, değişimin daha kolay gerçekleşmesini kolaylaştıracaktır. EOF (End Of Fun) |
|
Posted: 01 Jan 2013 12:58 AM PST Bu rapor Kurumsaljava.com‘da hazırladığım yazılımcı anketinden edindiğim verileri ihtiva etmektedir. Ankete son beş ay içinde beş yüze yakın yazılımcı katıldı. Ankette yer alan soruları yazılımcıların çalışma yöntem ve süreçlerine, kullandıkları araçlara ve bu meslekten beklentilerine ışık tutacak şekilde şekillendirmeye çalıştım. Raporun bundan sonraki her sayfasında ankette yer alan bir soru ve bu soruya ankete katılan yazılımcıların verdiği cevaplar yer almaktadır.
Ankete katılıp, bu raporun oluşmasında emeği geçen yazılımcılara teşekkür ederim. Özcan Acar |
|
Neden Fonksiyonel Programlamayı Öğrenmek Zorundayız Posted: 30 Dec 2012 01:49 AM PST Daha dün gibi hatırlıyorum: Windows 95’in sahip olduğu işletim sistemi çekirdeğini (kernel) taskmanageri üzerinden şutlayabiliyordunuz. Akabinde tüm sistem çalışmaz hale geliyordu. Bu konularla ilgisi olmayanları kendine hayran bırakmak için fena bir yöntem değildi.
Bu bahsettiğim bilgisayarlar birkaç MB hafızası olan, tek bir işletim birimine (CPU) sahip, bir düğmesine basıldığında işletim biriminin çalışma hızını ikiye ya da üçe katlayan, bugünkü perspektiften bakıldığında taş devrinden kalma bilgisayarlardı. Geliştirilen uygulamalar da bu bilgisayarları limon gibi sıkıp, son hafıza hücresine ve işletim birimi döngüsüne (CPU cycle) kadar kullanmaya çalışırlar, ama kaynak yetersizliğinden dolayı bunda pek başarılı olamazlardı. Kısaca o zamanlarda kullanımda olan donanımın geliştirilen uygulamalar için yetersiz olduğunu söyleyebiliriz. Uygulamalar bir sonraki işletim birimi jenerasyonu ve daha fazla hafıza alanı ile hızlanır, bu donanımlarda geliştirilen uygulamalar aynı performansı göstermek için bir sonraki donanım jenerasyonunu beklemek zorunda kalırlardı. Yazılımın hep böyle donanımı köşeye sıkıştırması, donanım mühendislerinin ışık hızı sınırlarına dayandıktan sonra, işletim birimlerinin hızını alıştıkları gibi artıramayacaklarını anlamaları ve çok çekirdekli donanım mimarisine yönelmelerine kadar devam etti. Bu noktaya gelene kadar yazılım mühendisleri donanım mühendislerine takılarak, “biz birden fazla işletim birimi varmış gibi programlarımızı yazarız, aynı işletim birimini paylaşarak çalışırlar ve en azından geliştirdiğimiz uygulamalar kullanıcıya birden fazla işletim birimi varmış gibi numara yaparlar” söylemleri ile yetindiler. Doğal olarak bu tür programlama tarzına alıştılar. Birden fazla işletim birimli sistemler çıktığında semaforlar ve threadler yardımı ile geliştirdikleri uygulamaları birden fazla işletim birimi üzerinde koşturmaya devam ettiler, eski program yazma stillerinden ödün vermeden, bunun gelecekte programcı olarak var olma ya da olmamanın cevabı olacağının farkına bile varamadan. Yazılımcıların çoklu thread (multithreading) uygulamaları geliştirmekte başarılı oldukları söylenemez. Günümüzde bile birçok programcı bir Java uygulamasında iki threadin senkronizyonunda zorluk çekmektedir. Günümüzdeki kullanımda olan modern bir bilgisayarın en az iki ya da dört çekirdeği var. Bu yazıyı 2.8 GHz hızında, iki çekirdekli bir notebook üzerinde yazıyorum. Kullandığım işletim sistemi yükü eşit bir şekilde bu çekirdeklere dağıtmaya çalışıyor. Ne kadar başarılı olduğu tartışılır. Kullandığım bazı programlar birden fazla çekirdeğin üzerinde koşacak şekilde geliştirilmemiş. Bu yüzden beklenen performansı sağlayamıyorlar. Donanım mühendislerinin bir köşeden bizi izleyip “iki çekirdeği bile doğru dürüst programlayamıyorlar, kısa bir zaman sonra binli çekirdeklerle nasıl başa çıkacaklar acaba” dediklerini duyar gibiyim. Bir uygulamanın optimal bir şekilde birden fazla çekirdeği kullanabilmesi için, uygulama parçalarının threadler yardımı ile birden fazla çekirdek üzerinde koşturulmaları gerekir. Bu çoğu zaman veri kaybını önlemek için threadler arası senkronizyonu zorunlu kılar. Java dilinde synchronized kelimesi ile birden fazla threadin aynı kod bloğunu koşturması engellenir. Bu tür bir sekronizasyon anlaşılması zor, komplike uygulamaların oluşmasına sebep verir. Tek bir thread ile istenilen tarzda bir davranış sergileyen bir uygulama, ikinci bir thread devreye girdiğinde çok başka bir davranış biçimi sergileyebilir. Böyle bir uygulamanın bünyesinde barındırdığı hataları debugging yöntemi ile keşfetmek çok zor bir hal alabilir. Kısaca bu tür uygulamaları geliştirmek, koşturmak ve hataları gidermek çok zordur ve çok az sayıda programcı bu durumlarla baş edebilecek yeteneklere sahiptir. Kullandığımız imperatif (Java, C/C++, Python, PHP, Javascipt) diller thread senkronizyonu problemi haricinde başka bir sorunla karşılaşmamıza neden olmaktadırlar. İmperatif dillerde yapılan işlemler hafızada yer alan verilerin değiştirilmesi üzerine kuruludur. Bunun en basit örneği aşağıda yer alan Groovy kodunda görülmektedir.
for(i in 1..10){
println(i);
}
Groovy örneğinde for döngüsü başlatılmadan önce i ismini taşıyan bir değişken oluşturulur. Bilgisayarın hafıyasında i değişkeninin sahip olduğu değeri tutabilmek için 32 bit (bir int) uzunluğunda bir alan kullanılır. i nin başlangıç değeri 1 olduğu için, bu hafıza alanında yer alan değer 1 (00000000 00000000 00000000 00000001) olacaktır. Birinci döngü sona erdikten sonra i ye yeni bir değer atanır. Bu değer 2 rakamıdır. Değişkenin ismi değişmemiş olsa bile sahip olduğu değer değişmiştir. Bu imperatif dillerde sıkça karşılaştığımız bir değer atama işlemidir. Daha öncede belirttiğim gibi imperatif dillerde yapılan işlemler hafızada yer alan değerlerin değiştirilmesi üzerine kuruludur. Uygulama doğrudan hafızaya erişme (sadece kendi hafıza alanına) ve orada yer alan değerleri manipüle etme yetkisine sahiptir. İşlem gören her satır uygulamanın sahip olduğu anlık durumun (state) degişmesi anlamına gelmektedir. Eğer i global bir değişken ve for döngüsü bir hata nedeniyle sonlanmış olsaydı, i kendisine atanan en son değeri korurdu. Örneğin i değişkeninin sahip olduğu değere bağımlı olarak başka bir threadin işlem yapmak için beklediğini farz edelim. i döngü içinde istenilen değere ulaşamadığı için diğer thread belkide hayatının sonuna kadar beklemek zorunda kalabilirdi. Tabi bazı programlama teknikleri ile bu gibi durumların önüne geçmek mümkün. Lakin görüldüğü gibi imperatif dillerde uygulamanın sahip olduğu anlık durumun koşturulan her kod satırı ile değiştirilebilir olması, bu durumun geçerliliğinin (consistency) korunmasını çok zor kılmaktadır. Aynı durum OOP’de kullanılan nesneler için de geçerlidir. Nesneler sınıf değişkenleri aracılığı ile belli bir duruma (object state) sahiptirler. Sahip oldukları metotlar aracılığı ile bu durum değiştirilebilir. Herhangi bir metot bünyesinde bir hatanın oluşması, sınıf değişkenlerine geçersiz değerlerin atanmasına ve böylece nesnenin korumaya çalıştığı durumun geçersiz hale gelmesine sebep verebilir. Şimdi on ya da yirmi sene sonrasını hayal edelim. Binlerce çekirdeği olan bir işletim birimini geliştirdiğimiz imperatif tarzı bir uygulama nasıl optimal kullanabilir? Threadleri senkronize etmeye çalışarak bunu başarmamız mümkün değildir. Bu sebepten dolayı şimdiden binlerce çekirdeği olan bir işletim birimini optimal bir şekilde nasıl programlarız sorusuna cevap aramamız gerekmektedir. Aslında bu soruyu sorarak programcı olarak nasıl bir geleceğe sahip olacağımızın cevabını arıyoruz. Programcı olarak geleceğimizin nasıl şekilleneceği bu sorunun cevabında gizli. Bahsettiğim problemleri aşmanın ve binlerce çekirdeği olan bir sistemi programlamanın yolu, değişkenlere tekrar, tekrar değer atamamaktan geçmektedir. Değişen değer senkronizasyonu mecbur kılmaktadır. Bu mecburiyet tüm çekirdeklerin aynı anda uygulama tarafından kullanılmasının önünde büyük bir engel teşkil etmektedir. Bu mecburiyeti ortadan kaldırmak için kullandığımız veri yapılarını değiştirilemez (immutable) hale getirmemiz gerekmektedir. Bu bir değişkene bir kere bir değer atandıktan sonra, bu değerin bir daha değiştirilemez olması gerektiği anlamına gelmektedir. Bu değişken uygulama son bulana kadar sahip olduğu değeri koruyabildiği taktirde, threadler arası senkronizasyon gerekliliği ortadan kalkar. Değişmeyen bir değer için senkronizasyon gerekli değildir. Java gibi dillerde bir değişkene sadece bir kere değer atanması ve değişkenin bu değeri koruması final kelime ile sağlanabilir. final int i = 10; şeklinde bir tanımlama, daha sonra i=11; atamasının yapılmasına izin vermeyecektir. i her zaman 10 değerine sahip olacaktır. Bunun yanı sıra konstrüktör parametreleri aracılığı ile oluşturulan ve sınıf değişkenlerine set metotları aracılığı ile sonradan atama yapılmasına izin vermeyen nesneler değiştirilemez (immutable object) türdedir. Bu nesnelerin threadler arası paylaşımı hiçbir sorun teşkil etmez. Bu gibi yapılar thread güvenlidir (thread safe), çünkü içerikleri değişikliğe uğramaz/uğrayamaz. Binlerce çekirdeği olan bir sistemi optimal şartlarda programlamanın yolu sonradan değiştirilemeyen (immutable) veri yapılarından geçmektedir. Peki bunun fonksiyonel programlama ile ne ilgisi var? Açıklayayım. Fonksiyonel dillerde fonksiyonların sahip oldukları bazı özellikler şöyledir:
(+ 1 1) Yukarıda yer alan Clojure örneğinde 1+1 işlemi yapılmaktadır. + burada kullanılan fonksiyonun kendisi 1 ve 1 bu fonksiyona gönderilen parametrelerdir. Parantezler kullanılarak fonksiyonun nerede başlayıp, kullanılan parametrelerin nerede bittiği ifade edilir. 5 + 9 / 4 * 2 işlemini gerçekleştirmek için Clojure dilinde şu şekilde bir fonksiyon yazabiliriz. (/ (+ 5 9 ) (* 4 2)) Bu Clojure örneğinde 3 değişik fonksiyon kullanılmaktadır. Birinci fonksiyon / fonksiyonudur. Bu fonksiyona iki parametre verilmektedir. Birinci parametre 5+9 un neticesi olan 14 değeri, ikinci parametre 4*2’nin neticesi olan 8 değeridir. Yani yukarıda yer alan fonksiyonu şu şekilde de yazabilirdik: (/ 14 8) Görüldüğü gibi bir fonksiyon geri verdiği değer ile yer değiştirebilmektredir. Bu sadece ve sadece fonksiyonun işlem yaparken yan etki oluşturmaması ve aynı parametreler ile aynı neticeyi geri vermesi durumunda mümkündür. Eğer fonksiyon işlem yaparken durum (state) değişikliğine sebep vermiş olsaydı, yapılan durum değişikliğine göre (* 4 2) işleminde 8 değeri yerine başka bir değer geri alınabilirdi ki bu da bölme işleminin yanlış değerler kullanılarak yapılmasına sebep olurdu. Paralel programlamanın önündeki en büyük engel kullanılan programlama dilinin hafıyaya doğrudan erişerek mevcut değişkenlerin sahip olduğu değerleri manipüle edebilmesidir. Uygulamanın her parçası hafızada yer alan değerleri herhangi bir zamanda değiştirebilir. Bu hem parallel programlamayı hem de uygulamanın anlık hangi durumda olduğunu öngörmeyi zorlaştıran bir durumdur. Değiştirilemez veri yapılarının (immutable data structures) kullanılması uygulamanın sahip olduğu anlık durumu (state) öngörülebilir hale getirmekte ve paralel programlama için gerekli altyapıyı sunmaktadır. Fonksiyonel dillerin değiştirilemez veri yapılarını temel olarak almaları, bu dillerin parallel programlamada daha avantajlı bir konumda olmalarını sağlamaktadır. Gelecekte imperatif dillerin yanısıra fonksiyonel dilleri de sıklıkla kullanacağız. Donanımın geleceği parallelikte, yazılımcı olarak bizim geleceğimiz de bu paralelliğe nasıl hükmedeceğimizde. Bundan sonra geliştireceğimiz uygulamalar donanımın bize sağladığı her türlü kaynağı en optimal şekilde kullanacak yapıda olmalı. Bunun için yeni dönemde bizi bekleyen yeniliklere hazırlanmalıyız. EOF (End Of Fun) |
|
Nasıl Arkadaş Kazanır ve Diğer İnsanları Etkilersiniz Posted: 16 Dec 2012 12:19 AM PST Dale Carnegie tarafından 1937 yılından yazılan How To Win Friends and Influence People (nasıl arkadaş kazanır ve diğer insanları etkilersiniz) başlıklı kitap her yazılımcının mutlaka okuması gereken ve güncelliğini günümüzde de koruyan çok değerli bir kaynak kitap.
Kitapta günlük hayatta takip edilmesi gereken bazı kurallar yer alıyor. Bunlar: Kimseyi eleştirmeyin, yargılamayın ve şikayet etmeyin Bir başkasını eleştirmek faydasızdır, çünkü eleştirilenin kendisini ve haklılığını savunmasını doğurur. Eleştirmek tehlikelidir, çünkü eleştirilenin onurunu incitir, kendisini kötü hissetmesini sağlar. Bir şeyleri eleştirerek, durumun daha iyileşmesini sağlamak mümkün değildir. Taktiri hak edenlere bunu gösterin İnsanların en önemli motivasyon kaynaklarından birisi taktir görmek ve kendini önemli hissetmektir. Büyük eserlerin, yapıtların ya da şahsiyetlerin oluşmasında en büyük rolü bu ihtiyaç oynamıştır/oynamaktadır. İnsanlar bu ihtiyaçlarını giderebilenlerin elinde hamur gibidirler. Hak ettiği için övülen ve taktir edilen şahıs bunu hiçbir zaman unutmaz. Başkalarının içinde istek uyandırın Bir bireyin istekleri diğer bireyleri ilgilendirmez. Her birey kendi istekleri ekseninde hayatına şekil verir. Bu sebepten dolayı insanları etkilemenin en kolay yolu onların istekleri hakkında konuşmaktır. Her bireysel hareketin temelinde bir istek yatar. İnsanları herekete geçirmek için içlerinde bu isteği uyandırmak yeterlidir. Başkalarıyla ilgilenin Baskalarıyla ilgilenenlere insanlar arası ilişkilerde her türlü kapı açıktır. İlgi çekmek için uğraş veren bir bireyin iki senede kazanamadığı arkadaşlıkları, başkalarıyla ilgilenen birisi iki ay içinde elde edebilir. Dost ve arkadaşlığın temelinde bu samimi ilgi yatar. Gülümseyin Gülümseme iyi niyetin göstergesidir. Gülümseme onu görenlerin gününü güzelleştirir. Gülümseme “size kanım kaynadı, beni mutlu ediyorsunuz, sizi gördüğüme sevindim” demektir. Herkes için kendi isminin en hoş ve önemli kelime olduğunu unutmayın Insanları kazanmanın en kolay yollarından birisi, isimlerini hatırlamak ve onur duymalarını sağlamaktır. İsimler insanları özelleştirir. İsimlerle birlikte gönderilen mesajlar daha başka anlam kazanirlar. Bunlar gibi birçok tavsiye Dale Carnegie tarafından 1937 yılından yazılan How To Win Friends and Influence People (nasıl arkadaş kazanır ve diğer insanları etkilersiniz) başlıklı kitapta yer almakta. Okunulması tavsiye olunur.
|
|
Posted: 11 Dec 2012 01:12 PM PST Geçen cumartesiyi pazara bağlayan gece, saat 00:00 civarı KurumsalJava.com, BTSoru.com, BizimAlem.com ve diğer web projelerimin üzerinde yer aldığı sunucuların erişilemez oldukları haberini aldım. Akabinde gecenin bir yarısı hizmet aldığım ISP’nin yolunu tuttum. Tam, tamına 4 senedir bir düğmesine bile basmadığım, bu kadar zaman sorunsuz çalışmış olan firewall sistemlerinin neden çalışmadıklarını incelemeye başladım. Bugüne kadar birçok DOS saldırısına karşı koymuş ve çalışmaya devam etmiş bu sistemlerin bir DOS saldırısı sonucu tıkanmış olabileceklerine ihtimal vermedim. Aslında firewall sistemlerinin işletim sistemlerini en yeni versiyona çekmem gerekiyordu. Fırsat bu fırsat dedim ve gerekli kurulum ve ayarları yaptım. Pazar sabahı saat altıya doğru tekrar evimin yolunu tuttum.
Yaptığım müdahale hiçbir şey değiştirmedi. Sunucular pazar günü öğleden sonra tekrar erişilemez oldular. Bu işte başka bir şey olmalı diye tekrar ISP’nin yolunu tuttum ve firewall sistemlerini canlı yayında yakından takip etmeye başladım. Kısa bir zaman sonra bu sistemleri tıkayan sebebin bir DOS saldırısı olduğunun farkına vardım. 80/100 m/bit boyutunda olan bir DOS saldırısı ile router ve firewall sistemlerini erişilemez hale getirmişlerdi. Firewall ve router sistemleri üzerinde DOS saldırılarının kaynağı olan network segmentlerinin (188.0.0.0/8) sunuculara erişmesini engelledim. Aldığım bu son önlem karşı tarafı daha da kızdırmış olacak ki dünden beri 3 g/bit kapasitesinde DDOS saldırıları başlatılmış. Biraz önce sunucularımın barındığı ISP’nin administratörlerinden birisi beni arayarak, durumdan haberdar etti. ISP’nin benim yer aldığım network segmenti tamamen offline olmuş durumda. Çok uzun zamandan beri, özellikle BizimAlem.com web platformu ile Türk hackerlerin saldırılarına mağruz kalıyorum. Bir şekilde hepsine sistemi daha güçlü hale getirerek cevap verdim. Bu gibi şeylerle muhattap olmak benden çok sinir aldı götürdü, ama bilgi olarak ta birçok şey kattı. Ama artık saldırıların boyutu o kadar değişti ki, karşı taraftaki şahıslar koca bir ISP’yi dize getirebiliyorlar. Bu sebepten dolayı bu yazının başlığında da yer aldığı gibi bu şahısları ayakta alkışlıyorum. İnsanlığa, vatana, millete o kadar çok büyük emek, fayda ve katkıları var ki, kendilere mutlaka bir onur madalyası verilmeli. Benim internet girişimciliğinde son on yılda edindiğim tecrübeler şöyle:
Neden böyle bir yazı yazdım? Saldırıları yapanlar haberdar olsunlar ve saldırıları durdursunlar diye mi? Hayır, böyle bir amacım yok. Kimin, hangi sebepten dolayı böyle bir şeyi yaptığı beni pek ilgilendirmiyor. Ben daha ziyada edindiğim tecrüleri, konuyla ilgilenen okurlarımla paylaşmak istedim. Eğer internet girişimci iseniz ya da internet girişimcisi olmak istiyorsanız, sizi bekleyen bazı sürprizler var. Hazırlıklı olmanızda fayda var. EOF (End Of Fun) |
|
Posted: 03 Dec 2012 12:45 AM PST KurumsalJava.com RSS Adresi degişmiştir. Lütfen http://www.kurumsaljava.com/feed/ olarak değiştiriniz. |
|
Posted: 18 Nov 2012 02:01 AM PST Internetin bu kadar büyümesi ve özellikle Google gibi arama motorlarının günlük iş hayatımızın bir parçası haline gelmesi biz programcılar için ne kadar hayırlı oldu, bilemiyorum. Pek te hayırlı olmadığı kanısındayım. Açıklamaya çalışayım.
Çalıştığım projede bir çalışma arkadaşımın masasında algoritmalarla ilgili kalınca bir kitap gördüm. Havadan, sudan konuşurken kendisi böyle kitapların artık gereksiz olduğunu, çünkü internette istediğin algoritmayı bulup, kullanabildigini söyledi. Doğru, haklıydı. Bugünlerde Pratik Spring Core 3 isminde yeni kitabım üzerinde çalışmalarımı sürdürüyorum. Spring ile XML bazlı deklaratif transaksiyon yönetimi konfigürasyonu nasıl yapılır diye internette araştırma yaparken, birkaç klasik blog sayfasına rastladım. Bu klasik blog yazılarını tanırsınız. Basit bir örnek üzerinde belli bir konfigürasyonun nasıl yapıldığını gösterirler. Bu örnekleri copy/paste yaparak programcının kendi uygulamasına eklemesi zor değildir. Zamanında az yapmadık. Copy/paste işlemi esnasında insan keşfettiği örneğin altındaki konseptlerin ne olduğunu sorgulamıyor. Programcı için önemli olan bir şeyleri çalışır hale getirebilmek. Ve bunu başardıktan sonra, “ya, bunu çalışır hale getirdim ama, bu işin temelindeki konseptler nelerdir” şeklinde bir sonuca varmıyor ya da varamıyor. İşte internet üzerinden bilgiye bu kadar hızlı, sorgusuz ve sualsiz erişmenin getirdiği hayırsızlıkta bu noktadan itibaren başlıyor. Programcı bir bilgiyi edindi ve kullandı. Ama o bilginin neyi temsil ettiğini, yani temelinde neler olduğunu bilmiyor. Spring ile XML bazlı deklaratif transaksiyon yönetimi konfigürasyonu örneğinde 3 satırlık copy/paste edilen XML konfigürasyonunun altında AOP (Aspect Oriented Programming) ve Proxy tasarım şablonu gibi konseptler var. İki satırlık konfigürasyonu kopyaladım, ama temelinde yatan bu bilgileri kopyalayamadım. Bunlar internette kalmaya devam etti. Benim kopyaladığım cansız, iki satırlık kod parçası. Kod örneğiyle, kodun temelindeki konseptlerin zahmetsiz bir şekilde beyne nasıl kopyalandığını Matrix filmede gördük. Neo anında Karate-Kid olmuştu. Bizim böyle bir imkanımız yok. Böyle bir şeyin gelecekte mümkün olacagını da düşünmüyorum. Geriye kalan, kaynağını bulup, copy/paste yaptığımız bilgilerin temelinde yatan konseptleri ögrenmek ve uygulamak. O zaman hayırsız olarak tabir ettiğim işlemi hayırlı bir işleme dönüştürebiliriz. Copy/paste zihinlere o kadar yerleşmiş ki, bilgiye sahip olmanın değil de, bilgiye nasıl ulaşabileceğini bilmenin daha önemli olduğunu savunanlar var. Bu artık copy/paste filozofisinin mükemmelleştirilerek, getirildiği en son noktadır, yani bu akımın en tepesindeki noktadır. Bu filozofiye sahip olanların, iş görüşmelerinde bu tür ifadelerde bulunmamalarını tavsiye ediyorum, çünkü iş verenin en sevmediği ve iş görüşmesini anında sonlandırabileceği durumların başında, iş görüşmesi yaptığı şahsın “ufak bir internet araştırması ile bu sorunu çözebilirim” tarzı söylemlerde bulunmasıdır. Böyle bir şey söyleyen şahıs aslında “ben bu bilgiye sahip değilim, başkaları sahip, ama bir bakayım, bakalım, bir yerlerden bulabilir miyim” demektedir. Böyle bir şey söyleyen bir programcıyı siz olsanız işe alır mıydınız? İyi bir programcı olabilmek için iyi bir temele sahip olmak gerekir. Bu temel edinilmiş ve zaman içinde edinilen bilgiden oluşur. Programcı tüm yeteneklerini bu temel üzerine inşa eder. Zaman zaman internetten bir şeyleri copy/paste etmek suç ya da günah değildir. Bu hepimizin yaptığı bir şeydir. Lakin programcı copy/paste yaptıktan sonra, copy/paste yaptığı kod parçasının temelinde yatan bilgiyi sahip olduğu temele eklemekle yükümlüdür. Aksi taktirde bu bakkaldan bir sakız alıp, parasını ödememek gibi bir şey olur. Copy/paste yapılan şeyin bedeli ödenmelidir. Copy/paste edilen kodun üzerinde fiyat etiketi olmadığı için beleş sananlar vardır. Hayır, beleş değildir. Bedeli ödenmeden kullanılan bilgi, temeli sağlam olmayan bir binanın en ufak bir rüzgar esintisinde sallanması misali programcıya zarar verir. Copy/paste yapalm, ama bedelini ödeyerek… EOF (End Of Fun) |
|
Test Edebilme Uğruna Her Şey Mübahtır! Posted: 13 Nov 2012 09:44 AM PST Geçenlerde yine tartışması yapılıyor: private olan metotları nasıl test ederiz? Benim cevabım: “edemeyiz!” Karşıdan gelen cevap: “dediğim gibi, her şeyi test etmek mümkün değil demek ki!“. Benim cevabım: “her şeyi test etmek mümkün, private’i protected yaparsın, olur, biter.” Karşı tarafın cevabı: “kardeşim ortada OOP diye bir şey var, kafana göre nasıl öyle private bir metodu proteted yaparsın?“. Yaparım canım kardeşim. OOP’yi filan takmam! Protected‘de yaparım, public‘de. Bir sınıfı test edebilmek için her türlü yöntemi kullanırım, gözünün yaşına bakmam. Bu uğurda her şey mübahtır.
Bu bazıları için çok radikal bir yaklaşım tarzı gibi görünebilir. Ama uygulama çalışırken bir sebepten dolayı patladığı zaman kimse gelip size, bu sınıfı neden OOP’ye uygun tasarlamadınız diye sormaz. Bu sınıf neden patladı, sizin testleriniz yok mu diye sorar! O zaman da “bu metot private idi, o yüzden test yazamadım” mi diyeceksiniz? Pek ikna edici değil, değil mi? Bir uygulama geliştirken bir kod biriminin test edilebilirliğinin önündeki engel OOP prensipleri olmamalıdır. Eğer öyle ise, OOP prensipleri yanlış uygulanmış demektir. OOP kod geliştirmek için kullanılan bir araçtır. private yerine göre kullanılabilecek bir erişim mekanizmasıdır. Lakin private olan bir metot benim için yoktur, yani test edilmesi mümkün değildir. Benim için test edilemeyen kod birimi olamayacağı için, private ile tanımlanmış metotları yok sayarım. Test edilemeyen bir metodun başına her türlü iş gelebilir. Benim bu tür metotları görünce ilk yaptığım şey, metodu hemen protected yapmak ve o metodu izole bir şekilde test eden bir birim testi yazmak olur. Protected yetmedi ise public yaparım. Nihai amaç metodu bir şekilde test edebilmektir. Ne test edilir, ne test edilmez, nasıl test edilir tartışmalarının sonu gelmez ne yazık ki. Geçenlerde yine kocaman, ne yedüğü belli olmayan bir sınıfa yeni bir özellik eklemem istendi. Eğer gerekli kodu o sınıfa direk ekleseydim, yeni eklediğim kod birimini test etmem çok zor olacaktı, bunu biliyordum. Sınıfın tümünü test edecek bir birim testi oluşturmak için vaktim yoktu. Bende bunun yerine yeni bir static iç sınıf oluşturup, kodu bu sınıfın bir metodu olarak geliştirdim ve bir birim testi ile test ettim. Testin başarılı olduğunu gördükten sonra ne yedüğü belirsiz diye tabir ettiğim sınıfın gerekli metoduna oluşturduğum yeni sınıfın bir nesnesini yerleştirerek, static sınıfın metodunu orada koşturdum. Böylece ne yedüğü belirsiz sınıf dolaylı bir şekilde yeni oluşturduğum kod birimine sahip oldu. Doğal olarak gelen ilk soru şu oldu: “neden static bir iç sınıf oluşturdun ki? Orada bu işi görecek bir metot vardı, o metoda kendi kodunu ekleyebilirdin“. Eklerdim, eklemesine, lakin bu sınıfa eklediğim kodu nasıl test edebilirdim? Edemezdim. Sınıf için daha önce hiç birim testi yazılmamıştı. Sınıfa eklediğim kod birimini test edebilmek için tüm uygulamayı ayağa kaldırıp, benim eklediğim kod birimi çalışıyor mu diye otomatize edilemeyecek türden bir test yapmam gerekirdi. Yok efendim. bu tür yöntemler sınıf enflasyonuna sebep oluyormuş. Olursa, olsun, sorun nedir? Önemli olan benim yeni kod birimini izole bir şekilde test etmemdi. OOP, tasarım şablonları ya da tasarım prensipleri sadece test edilebilirliğin emrinde çalışabilecek erlerdir, tersi değil! Bu saydığım araç, gereçlerin hepsinin efendisi test edilebilirlik özelliğidir. Bunların arkasına saklanıp, bu kod birimi test edilemez diyen yazılımcı kendisine ve müşterisine ihanet içinde olur. EOF (End Of Fun) |
|
Posted: 09 Nov 2012 01:05 PM PST Yazılım yaparken en büyük zaman kaybının nedeni, kodu değiştirip, derleyip, çalışır hale getirdikten sonra değişikliğin sonucunu test etmektir. Kodu değiştir/derle/çalıştır/dene süreci otuz saniyeden beş dakikaya kadar sürebilir. Günde bunu on sefer yaptığınızda bir saatlik bir zamanı boşa harcamış olursunuz. Bu sebepten dolayıdır ki EJB2 ve benzeri teknolojilerin yerine Spring gibi daha hafif yazılım yapmayı sağlayan çatılar (framework) oluşmuştur.
Çoğu programcı yılmadan kodu değiştir/derle/çalıştır/dene döngüsünde programlamaya devam ediyor. Büyük bir inatla, akıl almaz derecede komplike olan algoritmaları deneme, yanılma usulü ile geliştirmeye ya da değiştirmeye çalışanlar var. Neymiş efendim, bir sonraki döngüde çalışacakmış… Murphy’nin kuralları gereği yapılan değişikliğin beklenen neticeyi getirmeme ihtimali çok yüksek. Ve böylece programcı hiç farkına bile varmadan dönme dolap içinde dönüp, duran bir fare misali hayatını devam ettirir…. Aslında böyle durumlarda beyinde tehlike çanları çalmaya başlayıp, programcının içinde bulunduğu kısır döngüyü aşması için gerekli rutinlerin çalışması gerekir. Mesela bu rutinlerden bir tanesi “hemen kodu test eden bir birim testi yaz” olabilir. Bir birim testi bahsi geçen kod birimini izole edilmiş halde çok hızlı bir şekilde test etmek için kullanılabilir. Yapılan değişikliğin neticesini görmek bir saniye bile sürmeyecektir. Gerekli değişiklikler yapıldıktan sonra ayrıca bonus olarak bir birim testine sahip olunur. Daha sonra yapılması gereken değişiklikler birim testi sayesinde çocuk oyuncağı haline gelir. Zaman ayrılıp, mutlaka böyle bir yatırım yapılmalıdır, aksi taktirde deneme, yanılma tarzı programlamanın bedeli çok daha ağır olacaktır. Sözüm tabi verimliliğim düştü derdinde olmayanlara değil. “Hemen test yaz” vari bir rutinin devreye girebilmesi için iki şeye ihtiyaç duyulmaktadır:
Bu rutini çalıştırabilmek için bu rutine sahip olmak gerekiyor. Böyle bir rutine nasıl sahip olunabileceğine Kod Kata ve Pratik Yapmanın Önemi başlıklı yazımda değinmeye çalıştım. Bu işin sırrı devamlı pratik yapmaktan geçiyor. Pratik sahibi olan bir programcının aklına ilk gelen şey “ya ben neden bir birim testi yazmıyorum da, burada taklalar atıyorum” olur. Basit gibi görünen bu beyin performansını sağlayabilmek için beyni bu yönde eğitmek gerekiyor. Bunun yolu da kod katalarından geçiyor. KodKata.com‘a bir göz atmanızı tavsiye ederim. Deneme, yanılma tarzı programlama hem insanı yorar, hem usandırır, hem de bir zaman sonra bıkkınlık verir. Ayrıca bu tarz programlamanın yazılım mühendisliği ile yakından, uzaktan ilgisi yoktur. Mühendis olmak demek yol, yordam bilmek demektir, metotlu, yöntemli çalışmak demektir. Çaylak gibi bilgisayar başına geçip, deneme, yanılma tarzı programlama yapan yazılımcıya gülerler. Profesyonel programcı hemen çekmecesinden ihtiyaç duyduğu aracı, yani birim testi yazma yetisi, çıkarır ve sorunu en kısa sürede daha önce etkinliliği milyarlarca defa kanıtlanmış bir yöntemi kullanarak çözer. Yol, yordam bilmek budur. Profesyonel yazılımcıya yakışan budur. EOF (End Of Fun) |
|
Posted: 30 Sep 2012 09:30 AM PDT Eşli programlama yapmak için illa iki programcının aynı mekanda olması gerekmiyor. Eclipse kullanıyorsanız Saros pluginini kurarak, uzak bir bilgisayardaki bir şahıs ile eşli programlama yapabilirsiniz.
Uzaktan eşli programlama için neler gerekli? Eclipse >> Help >> Install new Software menüsünden http://dpp.sourceforge.net/update Saros update sayfasını kullanarak gerekli plugini kurabilirsiniz. Kurulumun nasıl yapıldığı Saros kurulum sayfasında yer almaktadır. Kurulum işlemi tamamlandıktan ve Eclipse kapatılıp, yeniden açıldıktan sonra yukarıda yer alan login paneli görünecektir. İki programcının beraber aynı proje üzerinde çalışabilmeleri için aynı sunucuya bağlantı kurmuş olmaları gerekmektedir. Saros sunucu olarak bir Jabber sunucusu kullanıyor. Yeni bir hesap oluşturduktan sonra sunucuya bağlanabilirsiniz. Eşli programlama yapmak istediğiniz şahsın Saros hesabını edinmeniz ve aşağıda yer aldığı gibi Saros paneli üzerinden Buddy listenize eklemeniz gerekiyor. Buddy ekleme işlemi tamamlandıktan sonra eğer Buddyniz online ise, herhangi bir Eclipse projesini karşı taraf ile paylaşıp, eşli programlamaya başlayabilirsiniz. Proje üzerinde yaptığınız her değişiklik anında karşı tarafa yansıyacaktır. Ben bu altyapıyı genelde uzakta bir bilgisayar kullanan bir programcı ile beraber kata yapmak için kullanıyorum. Kata seansında iletişimi kolaylaştırmak için Skype kullanılabilir. Benimle kata yapmak isterseniz koordinatlarım şöyle: Saros hesabım: ozca...@saros-con.imp.fu-berlin.de Bana bir e-posta gönderin ve hangi katayı benimle birlikte eşli programlama yöntemi ile yapmak istediğinizi bildirin. Birlikte bir zaman dilimi bulmaya ve katayı yapmaya çalışalım. EOF (End Of Fun) |
|
Posted: 17 Sep 2012 12:17 PM PDT Editörü açtınız, public class yazarak yeni bir sınıf oluşturdunuz. Bu sınıfa yeni bir metot eklediniz. Başka bir şey yapan yeni bir metot daha eklediniz. Sınıf yavaş yavaş şismeye başladı. Birkaç refactoring yaptınız. Buradan yeni bir sınıf dünyaya gözlerini açtı. Bir sınıf, bir sınıf daha derken sınıf sayısı onlara ulaştı. Her sınıfın metot küfesi iyice ağırlaşmaya başladı. Göz açıp, kapayana kadar birkaç bin satır kod oluştu. Program istediğiniz şekilde çalışıyor sanırım. Yaptığınız işten memnunsunuz, ama içinizden bir ses bir şeylerin doğru gitmediğini söylüyor. Eksik olan ne? Testler!
Yazılımcı neden test yazmaz? Sıralayalım:
Bir yazılımcının görevi nedir? Sıralayalım:
Yazılımcı test yazmadığı zaman ne olur? Sıralayalım:
Veresiye satan tüccar ile peşin satan tüccarın hikayesini bilirsiniz. Veresiye satanın sonu malumdur. İşi başından sıkı tutmadığı için batma aşamasına gelir. Test yazmayan programcının da durumu aynıdır. Projenin geleceğini veresiye dağıtır. Siz veresiye mi satıyorsunuz, peşin mi? EOF (End Of Fun) |
|
Uzman ve Usta Yazılımcı Arasındaki Fark Posted: 17 Sep 2012 05:56 AM PDT Fanatik futbol severleri bilirsiniz. Takımları için yapmayacakları yoktur. Bu fanatiklik başka insanlara zarar vermeye kadar varabilir. Yazılımda da durum farklı değildir. Tek fark bu fanatikliğin insana zarar verecek seviyede olmamasıdır.
Çok duymuşsunuzdur Java dili şöyle, Java dili böyle, diğer dillerden üstündür… diye. Neden bazı yazılımcıların böyle fanatizm olarak görülebilecek bir ilgi ve alaka ile bazı bilişim öğelerine bağlandıklarını biliyor musunuz? Bu yazımda bu soruya cevap vermeye çalışacağım. Bilişim ya da yazılımda fanatik olanlar savundukları konu hakkında uzman ya da uzman olduklarını düşünen şahıslardır. Yıllar süren çalışmalar sonunda örneğin bir programlama diline uzman seviyesinde hakimdirler. Sahip oldukları fanatizm ile savundukları konu hakkında yıllar süren çalışmalar ardından yoğun bilgi, tecrübe ve beceri sahibi olmuşlardır. Kısacası bu konuda uzmanlaşmışlardır. Fanatik bir biçimde kendilerini ifade etmeye çalışmalarının altında gizli olan iki şey vardır: Birincisi sahip oldukları bilgi, tecrübe ve beceriyi ortaya koymak istemeleri, ikincisi farkında olmadan yeniliklere açık olmadıklarını ifade etmeleri. Bir konuda uzmanlık ne yazık ki çok uzun sürebilecek bir bağımlılığı beraberinde getirebilir. Bu insanda zaman içinde sahiplenme hissi doğurur. Çok emek harcayarak bir yerlere geldiğini düşünen şahıs için savunduğu konu kutsallaşır. Bu durum savunma ve koruma güdülerini tetikler. Buradan da tanıdığımız ve hoşumuza gitmeyen verimsiz tartışmalar doğar. Belli bir programlama dilinin fanatikçe savunulduğu tartışmaların kimseye bir şey katmayacağını çok iyi biliriz. Ama bu fanatizm yine de bir son bulmaz. Devam eder gider, çünkü yazılımcı uzman olmaya devam eder, çünkü usta olmanın ne olduğunu kavrayamamıştır. Tipik bir uzmanın bir çalışma ömrü boyunca aynı programlama dilini kullandığını görmek mümkündür. Uzmanlık sıfatı ne kadar olumlu görünsede, bahsettigim sebeplerden dolayı sakınılması gereken bir durumdur. Bir uzman yazılımcı at gözlüğü takmışcasına hayatın içinden geçer, gider, kişisel gelişim için gerekli nimetleri edinemeden. Kendisini bir konuya adadığı için, bu konunun modası geçtiğinde ortada kalıverir. Hakim olduğu konu haricinde yeni bir şeyler öğrenme yetilerini geliştirmeği için iş hayatının son bulması teklikesi ile karşı karşıya kalabilir. Buraya kadar yazdıklarımdan bir konuda uzman olmaya sıcak bakmadığımı düşünebilirsiniz. Bir değil, birden fazla konuda uzmanlığı tercih ederim. Bunun yazılımdaki karşılığı uzman değil, usta yazılımcı olmaktır. Usta bir yazılımcının öz geçmişine göz attığınızda, onlarca programlama dilini kullandığını görürsünüz. Hepsinde uzman olup, olmadığı tartışılır. Lakin bu onun uzmana nazaran değişikliklerle daha iyi yaşayabildiği anlamına gelmektedir. Zaman ve mekan neyi gerektiriyorsa, o konuyu seçerek, konu üzerinde çalşır ve zamanla uzmanlaşır. Geniş bir perspektife sahip olmasının sebebi buradan kaynaklanmaktadır. Çok değişik dil, teknoloji ve platformlarla çalışmak daima onun ufkunu genişletir. Bu onun günlük işlerine olumlu olarak yansır. Bu ona pragmatik olmayı öğretir. Bu ona teknoloji fanatiği olmamayı öğretir. Uzman ve usta yazılımcı arasındaki fark budur! EOF (End Of Fun) |
|
Posted: 20 Aug 2012 08:12 AM PDT Her izcinin uyduğu bir kural vardır: “Kamp yaptığın yeri bulduğundan daha iyi bir şekilde bırak!” Yazılımcı olarak bu kuralı uygulamak istediğimiz taktirde, mevcut yazılım standartlarına uymayan kod birimlerini tereddüt etmeden yeniden yapılandırarak, uygulamanın bakılabilirliğini ve geliştirilebilirliğini artırmamız gerekir.
Usta bir yazılımcının geride bıraktığı kodun kalitesi, işe başladığında bulduğu kodun kalitesinden her zaman daha iyidir. EOF (End Of Fun) |
|
Posted: 20 Aug 2012 07:56 AM PDT Yazılım yapmayı zorlaştıran her zaman kod birimleri arasındaki bağimlılıklar ve bu bağımlılıkların yönetimi olmuştur. Bu bağımlılıkları tamamen yok etmek yazılım sistemini anlamsız kılarken, kontrolden çıkmalarına göz yummak yazılım sisteminin ölüm fermanı olabilir. Yazılım mühendisi bunu bilir ve gerekli gördüğü yerlerde DIP, ISP ve SRP gibi tasarım prensiplerini kullanarak kodu dokur.
Yazılımcının kod yazarken devamlı uygulaması gerektiği bir tasarım prensibi varsa, bu da SRP (Single Responsibility Principle) tek sorumluluk prensibidir. Bu prensibe göre her kod biriminin sadece ve sadece bir sorumluluk alanı, yani yaptığı tek bir iş olmalıdır. Bu kod birimi bir paket (package), bir sınıf, metot ya da bir değişken olabilir. SRP uygulanmadığı taktirde yazılım sistemi kontrol edilemez, kırılgan bir bağımlılıklar yumağı haline gelebilir. Genelde bir bakışta bir kod biriminin SRP uyumlu olup, olmadığını anlamak zor değildir. Eğer bir sınıf iki bin satırdan oluşuyorsa, bu sınıfın birden fazla işle meşgul olduğu söylenebilir, aksi taktirde bu kadar büyümesi mümkün olmazdı. Büyük yazılım sistemlerini sınıf sınıf gezip, kim SRP uyumlu, kim değil diye kontrol etmek mümkün değil. Yazılımcı olarak daha ziyade anlık resmi görmemizi sağlayacak bir araç oluşturmamız lazım. Bu aracı geliştirmeden önce, sınıfların SRP uyumluluklarını ölçmek için bir yönteme ihtiyacımız var. Bu yöntem örneğin bir sınıfın versiyon kontrol sistemi bünyesinde ne kadar değişikliğe uğradığını ölçebilir. Eğer bir sınıf sıkça değişikliğe uğruyorsa, bu bu sınıfın birden fazla sorumluluk sahibi olduğu anlamına gelebilir. Gerçekten de binlerce satırdan oluşan kod birimlerinin versiyon kontrol sistemindeki geçmişleri incelendiğinde, çok sık değişikliğe ugradıklarını görmek mümkündür. Değişik sorumluk sahibi bir sınıf, her bir sorumluluk için değişikliğe maruz kalabileceğinden, bu gibi sınıfların sicilleri kabarıktır. O zaman bu sınıfları, kaç satır ihtiva ettiklerini ve kaç sefer değişikliğe uğradıklarını tespit edebilecek bir uygulama geliştirelim. Böyle bir uygulamanın kodu aşağıda yer almaktadır.
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public final class SvnHistory {
private static final String PROJECT_ROOT = "C:/xxx";
private static Map<String, ClassStats> STATS_MAP = new HashMap<String, ClassStats>();
private static StringBuilder STATS = new StringBuilder();
private static final String SVN_EXE = "C:/Program Files/SlikSvn/bin/svn.exe";
private static final String SVN_LOG_COMMAND = "log";
public static void main(final String[] args) {
makeHistory(new File(PROJECT_ROOT));
makeHeader();
makeLines();
printStats();
}
private static void makeHeader() {
STATS.append("Filename;revisionCount;loc").append("\n");
}
private static void makeLines() {
for (final String key : STATS_MAP.keySet()) {
makeLine(key, STATS_MAP.get(key));
}
}
private static void printStats() {
System.out.println(STATS.toString());
}
private static void makeLine(final String key, final ClassStats s) {
STATS.append(key).append(";").append(s.revCount).append(";")
.append(s.loc).append("\n");
}
private static void makeHistory(final File root) {
for (final File file : root.listFiles()) {
if (isJavaFile(file)) {
history(file);
}
if (file.isDirectory()) {
makeHistory(file);
}
}
}
private static boolean isJavaFile(final File file) {
return file.getName().endsWith(".java");
}
private static void history(final File file) {
try {
final int revCounter = getRevisionCount(file);
STATS_MAP.put(file.getName(), ClassStats.make(file.getName(), revCounter,
getLinesOfCode(file)));
} catch (final Exception err) {
err.printStackTrace();
}
}
private static int getRevisionCount(final File file) throws IOException,
InterruptedException {
String line = null;
int revCounter = 0;
final Process p = Runtime.getRuntime().exec(
SVN_EXE + " " + SVN_LOG_COMMAND + " " + file.getAbsolutePath());
final BufferedReader bri = new BufferedReader(new InputStreamReader(
p.getInputStream()));
final BufferedReader bre = new BufferedReader(new InputStreamReader(
p.getErrorStream()));
while ((line = bri.readLine()) != null) {
if (line.startsWith("r"))
revCounter++;
}
bri.close();
bre.close();
p.waitFor();
return revCounter;
}
private static int getLinesOfCode(final File file) {
int loc = 0;
try {
final FileInputStream fstream = new FileInputStream(file);
final DataInputStream in = new DataInputStream(fstream);
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
while (br.readLine() != null) {
loc++;
}
in.close();
} catch (final Exception e) {
System.err.println("Error: " + e.getMessage());
}
return loc;
}
private static class ClassStats {
private String fileName;
private int revCount;
private int loc;
private ClassStats() {
}
public static ClassStats make(final String name, final int revCounter, final int loc) {
final ClassStats classStats = new ClassStats();
classStats.fileName = name;
classStats.revCount = revCounter;
classStats.loc = loc;
return classStats;
}
}
}
Bu uygulama kodun Subversion tabanlı bir versiyon kontrol sisteminde tutulduğunu varsaymaktadır. PROJECT_ROOT değişkeni versiyon kontrol sisteminden edinilmiş projenin (working copy) ana dizinine işaret ediyor. Uygulama ana dizinde bulunan tüm dizinleri taradıktan sonra bulduğu her Java dosyası için versiyon kontrol sicilini kontrol ediyor ve dosya üzerinde yapılan değişiklik adedini tespit ediyor. Akabinde her Java dosyanın satır adedi tespit edildikten sonra, aşağıdaki şekilde bir ekran çıktısı oluşturuluyor: Filename;revisionCount;loc AAA.java;13;55 BBB.java;21;325 CCCjava;3;292 DDD.java;2;367 EEE.java;6;384 FFF.java;1;156 GGG.java;1;77 HHH.java;11;47 III.java;10;81 JJJ.java;3;132 KKK.java;1;111 LLL.java;9;29 MMM.java;32;433 NNN.java;45;1301 OOO.java;3;88 PPP.java;11;47 QQQ.java;60;212 Ekran çıktısı bir csv (comma seperated value) dosyası. Bu dosyayı örneğin Excel ile import edip, aşağıdaki şekilde bir diagram oluşturmak mümkün. Diagram üzerindeki her nokta bir Java dosyasına işaret etmektedir. X ekseninde her dosyanın maruz kaldığı değişiklik adedi (revision count), Y ekseninde dosyanın sahip olduğu satır adedi yer almaktadır. Bu diagrama bakıldığında tek sorumluluk prensibine ters düşen kod birimleri hangileridir? SRP uyumlu olmayan sınıflar için sağ üst alana bakmak yeterli. Bu sınıfları lokalize ettikten sonra tek bir iş yapacak hale getirmek biz yazılımcıların asli görevlerinden birisi. İzcilerden kendimize örnek alalım :) EOF(End Of Fun) |
|
Posted: 20 Oct 2011 01:19 PM PDT Bu refactoring oturumu bünyesinde mevcut sınıfı aşağıda yer alan refactoring metotlarını kullanarak yeniden yapılandırdım:
|
|
Geçici Değişkenlerin Sorguya Dönüştürülmesi (Replace Temp with Query) Posted: 19 Oct 2011 10:06 AM PDT Bir metot bünyesinde bir işlemin sonucu geçici bir değişkende saklanabilir. Aşağıda yer alan kod örneğinde basePrice geçici bir değişkendir ve ihtiva ettiği değer fiyat * adet şeklinde hesaplanmaktadır. Replace Temp with Query refactoring metodunu kullanılarak geçici değişkenin değerini elde etmek için kullanılan yapı yeni bir metot bünyesinde taşınır. Geçici değişkenin kullanıldığı diğer alanlar metot ismiyle değiştirilir. Bu şekilde yapılan işlemin başka metotlar bünyesinde kullanılması sağlanır.
public double calculatePrice(final double price, final int itemCount)
{
double basePrice = price * itemCount;
double tax = ( basePrice / 100 ) * 18.00;
double totalPrice = basePrice + tax;
return totalPrice;
}
Refactoring işleminin ardından oluşan yeni yapı aşağıdaki şekildedir.
package com.kurumsaljava.refactoring.replaceTempwithQuery;
public class ReplaceTempwithQueryRefactored {
private double price;
private int itemCount;
public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}
public double calculatePrice() {
double tax = (getBasePrice() / 100) * 18.00;
return getBasePrice() + tax;
}
private double getBasePrice() {
return price * itemCount;
}
}
getBasePrice isminde, başka bir metot bünyesinde tekrar kullanılabilir yapıda yeni bir metot oluşturmus olduk. getBasePrice sorgu (query) yaptığımız bir metotdur. Belli bir değeri elde etmek için bu metodu sorgulamaktayız. basePrice isimli geçici değişkeni ortadan kaldırmak için getBasePrice metodunu kullandık yani geçici değişkeni bir sorguya dönüştürmüş olduk. Bu sebepten dolayı Replace Temp with Query‘yi Geçici Değişkenlerin Sorguya Dönüştürülmesi olarak tercüme ettim. calculatePrice metodunu tekrar gözden geçirdiğimiz zaman, katma değer verginin hesaplandığı görmekteyiz. Eğer daha önce basePrice geçici değişkenini bir sorguya dönüştürmemiş olsaydık, katma değer vergisinin hesaplandığı bölümü refactor etmemiz kolay olmayacaktı. Ama bu şekilde katma değer vergisinin hesaplandığı kod kısmını aşağıdaki şekilde refactor edebiliriz.
package com.kurumsaljava.refactoring.replaceTempwithQuery;
public class ReplaceTempwithQueryRefactored {
private static final double TAX_RATE = 18.00;
private double price;
private int itemCount;
public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}
public double calculatePrice() {
double tax = calculateTax();
return getBasePrice() + tax;
}
private double calculateTax() {
return (getBasePrice() / 100) * TAX_RATE;
}
private double getBasePrice() {
return price * itemCount;
}
}
|
|
Parametrele Değer Atamasının Kaldırılması (Remove Assignments to Parameters) Posted: 16 Oct 2011 12:52 PM PDT Metot imzasında yer alan parametrelere metot bünyesinde değer atanmamalıdır. Bu gibi atamalar istenmeyen sonuçlar doğurabilir. Remove Assignments to Parameters refactoring metodu kullanılarak bu tür parametre değer atamaları geçici degişkenlere yapılmalıdır.
private void execute(int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
Yukarda yer alan metot Remove Assignments to Parameters uygulandığı taktirde aşağıdaki yapıya sahip olacaktır. Metot bünyesinde result isminde yeni geçici bir değişken tanımlanmış ve böylece parametrenin taşıdığı değer metot gövdesinde korunmuştur.
private void execute(int value) {
int result = 0;
if (value == 10)
result = 1;
doSometthink(result);
}
final kelimesi kullanılarak metot bünyesinde parametrelere değer atanması otomatik olarak engellenebilir.
private void execute(final int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
Yukarda yer alan value=1 şeklindeki atama, value parametresi final olduğu için aşağıda yer alan hataya sebep olacaktır: The final local variable value cannot be assigned. It must be blank and not using a compound assignment
|
|
Yeni Metot Oluşturma (Extract Method) Posted: 16 Oct 2011 08:59 AM PDT Uzun metotları daha okunabilir ve kompak hale getirmek için Extract Method refactoring metodunu kullanabiliriz.
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
calculatePrice metodu bünyesinde ülkeye bağlı olarak katma deger vergisi oranı (taxRate) tespit edilmektedir. Bu işlemi aşağıdaki şekilde başka bir metot bünyesine alarak, calculatePrice metodunu küçültebiliriz.
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
taxRate = getTaxRateBasedOnCountry(locale);
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
Bu refactoring metodunu kullanırken seçtiğimiz metot isimlerinin, işlemin nasıl yapıldığını değil, hangi işlemin yapıldığını yansıtmasına dikkat etmemiz gerekmektedir. Bu kodun okunmasını kolaylaştıracaktır. Uzunda olsa yapılan işlemleri tanımlayan metot isimleri kullanmaktan kaçınmamamız gerekmektedir. Yeni oluşturulan metodun küçük tutulmasında fayda vardır. Bu, yeni metodun başka bir metot bünyesinde tekrar kullanım şansını artıracaktır. Eğer uzun bir metodun bu şekilde parçalarına bölünmesi okunurluluğu ve kodun anlaşılma seviyesini artırıyorsa Extract Method refactoring metodu kullanılmalıdır. Bu refactoring işleminin ardından bile calculatePrice metodunu daha da kompak hale getirme potansiyeli vardır. calculatePrice metodunu aşağıdaki şekilde tekrar refactor etmeye ne dersiniz?
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}
private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double result;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
Şimdi calculatePrice metodunun eski ve yeni halini bir kıyaslayalım. Hangi metot size ilk bakışta ne yaptığını daha kolay anlatıyor? Eski hali:
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
Yeni hali:
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}
calculatePrice metodunda gördüğümüz gibi oluşturdugumuz metotlar 4-5, en fazla 6-7 satırı geçmemelidir. Bu kurala uyduğumuz taktirde aşağıda yer aldığı gibi, okunması, bakımı ve geliştirilmesi çok kolay sınıflar oluşur.
package com.kurumsaljava.refactoring.extractmethod;
public class TaxCalculator {
private static final String LOCALE_DE = "de_DE";
private static final String LOCALE_TR = "tr_TR";
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;
private static final Double NULL_VALUE_DOUBLE_OBJECT = new Double(0);
public Double calculate(String locale, double price) {
return calculateBasedOnLocale(locale, price);
}
private Double calculateBasedOnLocale(String locale, double price) {
if (isCountryGermany(locale)) {
return calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
return calculatePrice(price, locale);
} else
return NULL_VALUE_DOUBLE_OBJECT;
}
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}
private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double tax = Double.valueOf( (price / 100) * taxRate);
return Double.valueOf(tax.doubleValue() + price);
}
private double getTaxRateBasedOnCountry(String locale) {
if (isCountryGermany(locale)) {
return TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
return TAX_RAT_TURKISH_REPUBLIC;
} else
return NULL_VALUE_DOUBLE_OBJECT.doubleValue();
}
private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}
private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}
}
Refactoring için çıkış noktamız aşağıdaki sınıftı. Umarım Extract Method refactoring metodunun ne kadar kıymetli bir araç olduğunu gösterebilmişimdir :)
package com.kurumsaljava.refactoring.extractmethod;
public class TaxCalculatorOld {
public Double calculate(String locale, double price) {
Double result = new Double(0);
double taxRate = 0;
if (locale != null && locale.equals("de_DE")) {
taxRate = 19.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals("tr_TR")) {
taxRate = 18.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
|
|
Koşulları Parçalarına Ayırma (Reverse Conditional) Posted: 15 Oct 2011 10:34 AM PDT TaxCalculator sınıfı, uygulamanın kullanıldığı ülkeye bağlı olarak katma değer vergisini ihtiva eden fiyatı hesaplamak için kullanılmaktadır.
package com.kurumsaljava.refactoring.reverseconditional;
<span id="more-1415"></span>
public class TaxCalculator{
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;
public Double calculate(String locale, double price){
Double result = new Double(0.0);
if(locale != null && locale.equals("de_DE")){
Double tax = Double.valueOf( (price / 100 ) * TAX_RAT_GERMANY);
result = Double.valueOf(tax.doubleValue() + price);
}
else if(locale != null && locale.equals("tr_TR")){
Double tax = Double.valueOf( (price / 100) * TAX_RAT_TURKISH_REPUBLIC);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
calculate gibi metotlarda karşılaştığımız en büyük sorunlardan birisi if/else blokları içinde mantıksal operatörler kullanılarak bir takım koşulların tanımlanması ve bu koşullara bağımlı olarak bazı işlemlerin yapılmasıdır. Bu tür bir programlama tarzı ne yazık ki uzun ve anlaşılması zor metotların oluşmasına sebep olmaktadır. calculate metoduna baktığımızda if/else bünyesinde ne olup bittiğini belki anlamaktayız, lakın neden bunun yapıldığını anlamak her zaman mümkün olmamaktadır. Bahsettiğim sorunları ortadan kaldırmak için Reverse Conditional refactoring metodunu kullanabiliriz.
package com.kurumsaljava.refactoring.reverseconditional;
public class TaxCalculator {
private static final String LOCALE_DE = "de_DE";
private static final String LOCALE_TR = "tr_TR";
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;
public Double calculate(String locale, double price) {
Double result = new Double(0.0);
if (isCountryGermany(locale)) {
result = calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
result = calculatePrice(price, locale);
}
return result;
}
private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}
}
Yeni implmentasyonu gözden geçirdiğimizde calculate metodunda olup bitenlerin ne anlama geldiğini daha iyi algılayabilmekteyiz. Reverse Conditional refactoring metodunu kullanarak if/else koşullarını parçalara böldük ve yeni metotlar oluşturduk. Kullandığımız metot isimleri yapılan işlemi algılamamızı daha kolaylaştırmakta ve bu şekilde kodun okunulurluk derecesi artmaktadır.
|
|
Posted: 02 Sep 2011 07:34 AM PDT AgileMentor.com ismini verdiğim ve çevik süreçlerle ilgili danışmanlık ve koçluk hizmetleri sunduğum yeni bir websayfası hazırladım. Beğeninize sunuyorum. |
|
Posted: 09 May 2011 02:47 AM PDT Bu aralar bir Corba projesinde çalışıyorum. Corba teknolojisinde interface tanımlama dili olarak IDL kullanılıyor. Eğer bir Corba servisine erişimi sağlamak için size bir IDL verildi ise, bu IDL´i kullanarak client sınıflarını oluşturabilirsiniz. Bu işlem için aşağidaki sınıfı oluşturdum.
Bu implementasyon Sun JDK bünyesinde bulunan com.sun.tools.corba.se.idl.toJavaPortable.Compile ve IBM JDK bünyesinde bulunan com.ibm.idl.toJavaPortable.Compile sınıflarını kullanarak client sınıflarını oluşturuyor. Bu iki sınıf JDK lib dizini altındaki tools.jar içinde yer alıyor. Sınıfın bulunabilmesi için -Djava.home= parametresinin tanımlanmasi gerekiyor. JDK bin dizininde yer alan idlj komutu ile aynı işlemi gerçekleştirebilirsiniz.
/**
*
* @author F520947 Oezcan Acar
*
*/
public interface IdlToJavaGeneratorService
{
void generate( String workingDir, String idlFile, String outputDirectroy );
}
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.plexus.util.StringOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.csg.cs.testing.common.CommonConstants;
import com.csg.cs.testing.generator.GeneratorException;
import com.csg.cs.testing.project.ProjectDirectoryUtils;
import com.csg.cs.testing.service.logger.ILogger;
/**
*
* @author F520947 Oezcan Acar
*
*/
@Component
final class DefaultIdlToJavaGeneratorServiceImpl implements IdlToJavaGeneratorService
{
@Autowired
private ILogger logger;
@Autowired
private IIdlParserService parser;
@Override
public void generate( final String inputDirectory, final String idlFile, final String outputDirectory)
{
File idl = new File( ProjectDirectoryUtils.getWorkingDirRoot() + "/" + idlFile );
List< String > includes = parser.getIdlIncludes( idl );
List<String> args = new ArrayList< String >();
args.add( "-emitAll" );
args.add( "-fclient" );
args.add( "-pkgPrefix" );
String interfaceName = parser.getRealInterface( idl );
args.add( interfaceName );
args.add( CommonConstants.SERVICE_PACKAGE );
for (String include : includes)
{
args.add( "-pkgPrefix" );
args.add( include );
args.add( CommonConstants.SERVICE_PACKAGE );
}
List< String > baseInterfaces = parser.getBaseInterfaces( idl );
for (String base : baseInterfaces)
{
args.add( "-pkgPrefix" );
args.add( base );
args.add( CommonConstants.SERVICE_PACKAGE );
}
args.add( "-pkgPrefix" );
args.add( "CS_Inquiry_0_0" );
args.add( CommonConstants.SERVICE_PACKAGE );
args.add( "-pkgPrefix" );
args.add( "CS_Inquiry_0_0Operations" );
args.add( CommonConstants.SERVICE_PACKAGE );
args.add( "-i" );
args.add( inputDirectory );
args.add( "-td" );
args.add( outputDirectory );
args.add( idlFile );
Class<?> compilerClass = getCompilerClass();
invokeCompiler( compilerClass, args );
}
private Class<?> getCompilerClass()
{
ClassLoader cl = this.getClass().getClassLoader();
Class<?> idljCompiler;
try
{
idljCompiler = Class.forName(getIDLCompilerClass());
}
catch (ClassNotFoundException e)
{
try
{
File javaHome = new File(System.getProperty("java.home"));
logger.log("java.home=" + javaHome);
logger.log("tools.har=" + javaHome+"/lib/tools.jar");
File toolsJar = new File(javaHome, "/lib/tools.jar");
URL toolsJarUrl = toolsJar.toURL();
URLClassLoader urlLoader = new URLClassLoader(new URL[]
{ toolsJarUrl }, cl);
// Unfortunately the idlj compiler reads messages using the
// system class path.
// Therefore this really nasty hack is required.
System.setProperty("java.class.path",System.getProperty("java.class.path")+ System.getProperty("path.separator")+ toolsJar.getAbsolutePath());
if (System.getProperty("java.vm.name").indexOf("HotSpot") != -1)
{
urlLoader.loadClass("com.sun.tools.corba.se.idl.som.cff.FileLocator");
}
idljCompiler = urlLoader.loadClass(getIDLCompilerClass());
}
catch (Exception notUsed)
{
throw new GeneratorException(notUsed);
}
}
return idljCompiler;
}
private String getIDLCompilerClass()
{
String vendor = System.getProperty( "java.vm.vendor" );
if ( vendor.indexOf( "IBM" ) != -1 )
{
return "com.ibm.idl.toJavaPortable.Compile";
}
return "com.sun.tools.corba.se.idl.toJavaPortable.Compile";
}
private void invokeCompiler( final Class<?> compilerClass, final List<String> args)
{
logger.log("Current dir : " + System.getProperty("user.dir"));
Method compilerMainMethod;
String arguments[];
args.add(0, "-verbose");
arguments = (String[]) args.toArray(new String[args.size()]);
String command = compilerClass.getName();
for (int i = 0; i < arguments.length; i++)
{
command += " " + arguments[i];
}
logger.log(command);
try
{
compilerMainMethod = compilerClass.getMethod("main", new Class[] { String[].class });
}
catch (NoSuchMethodException e1)
{
throw new GeneratorException("Error: Compiler had no main method");
}
int exitCode = 0;
// Backup std channels
PrintStream stdErr = System.err;
PrintStream stdOut = System.out;
// Local channels
StringOutputStream err = new StringOutputStream();
StringOutputStream out = new StringOutputStream();
System.setErr(new PrintStream(err));
System.setOut(new PrintStream(out));
try
{
Object retVal = (Object) compilerMainMethod.invoke(compilerClass,new Object[]{ arguments });
if (retVal != null &amp;amp;amp;&amp;amp;amp; retVal instanceof Integer)
exitCode = ((Integer) retVal).intValue();
}
catch (InvocationTargetException e)
{
throw new GeneratorException(e);
}
catch (Throwable e)
{
e.printStackTrace();
throw new GeneratorException("IDL compilation failed");
}
finally
{
if (!"".equals(out.toString())) logger.log(out.toString());
if (!"".equals(err.toString())) logger.log(err.toString());
// Restore std channels
System.setErr(stdErr);
System.setOut(stdOut);
}
}
}
|
|
Posted: 06 May 2011 09:50 AM PDT Bugün Amazon.com’dan sipariş ettiğim Kindle DX geldi. Tek klime ile harika. Daha önce uzunca bir zaman Irex Iliad kullandım. Ama Kindle DX onu arattırmayacak sanırım :)
Teknik Özellikleri Boyutları Ekran Hafıza CPU Bateri Desteklediği Formatlar Kindle DX’in en ilginç özelliklerinden birisi, bünyesinde bir EDGE/UMTS modem barındırması. Amazon’dan alınan kitaplar anında mobile network üzerinden Kindle DX’in hafızasına ışınlanıyor. Benim asıl ilgimi çeken, kullanılan E-Ink teknolojisi ve Kindle DX’in PDF formatındaki dosyaları okuyabiliyor olması. Doğal olarak ilk aldığım tepki neden IPad almadığım oldu. IPad bir LCD ekranına sahip ve baterisi 10 saat kadar dayanabiliyor. LCD ekranda PDF dosyaları okumanın gözler için ne kadar yorucu olduğunu düşünebilirsiniz. Buna karşın Kindle DX E-Ink teknolojisiyle çalışan bir ekrana sahip. Böyle bir ekranda bir PDF dosyasını okumak, bir kitabı okumaktan farksız ve bir o kadar da zevkli. |
|
Posted: 28 Apr 2011 03:20 AM PDT Elipse altında Subversion pluginini kullanıyorsanız, bir Subversion repositorisine bağlanmak için kullandığınız şifreyi nasıl değiştirebileceğinizi merak etmiş olabilirsiniz. Eclipse altında bu şifreyi değiştirmek mümkün değil, çünkü plugin şifreyi %APPDATA%\Subversion\auth (Linux altında ~/.subversion/auth) altında saklıyor. Bu dizini sildiğiniz taktirde, tekrar şifreyi girme panelini görebilirsiniz.
|
|
Posted: 24 Apr 2011 11:10 PM PDT Dün kızıma lego parçalarından oluşan bir set aldım. Bu sabah beraber lego parçalarından kaleler yaparken birşeyin farkına vardım. Yazılım mühendisleri olarak çok soyut şeylerle ugraşıyoruz. Artık soyutluk seviyesi öyle bir hal almış ki, geçenlerde kendimi CPU içide yer alan registerlerin Assembler kullanılarak programlanmasından bahseden bir programcı hakkında “bu kadar low level işlerle uğraşılır mı ya” gibisinden düşünürken yakaladım. Her defasında soyutluk çıtasını bir kademe daha yukarıya çekmeye alışmış ben, somut olan ve ele alınıp, bir mikroskop altında görülebilecek olan CPU registerlerine ne kadar yabancılaşmışım! Bu verebileceğim örneklerden sadece bir tanesi.
İşler soyutlaşdıkça tüm resmi algılamak ve sistemleri modellemek belki daha da kolaylaşıyor, lakin temelde olup bitenleri anlamadığımız sürece, dünyanın en büyük finans krizine sebep olan bankacılardan bir farkımız kalmıyor. Onlarda ne yazik ki temelde çürük kredilerden oluşan yeni finans ürünleri oluşturarak, bilgisiz insanlara pazarladılar. Bu ürünleri kullanarak yeni finans ürünleri soyutladılar. Bu işlem, kimsenin içinde ne olduğunu anlamadığı finans ürünleri oluşana kadar devam etti. Sonuç malum! Bunların lego ile ne alakası var diye düşünebilirsiniz. Yeni yetişen yazılımcıların OOP (Object Oriented Programming), modüler yazılım sistemleri, komponent tabanlı yazılım, kodun tekrar kullanımı (code reuse) gibi kavramları teorik ve pratik olarak kavramaları çok güç olabilir, çünkü bunlar ilk etapta elle tutulur, gözle görülür olmayan soyut kavramlar. Bu kavramları elle tutulur, gözle görülür hale getirebilseydik nasıl olurdu? Bunun için lego parçalarından faydalanabiliriz. Siz daha önce bir yazılım sisteminin maketini elinizde tuttunuz mu? Bizler yazılım mimarilerini yazılım mimarlarının kağıtlar üzerinde çizdikleri kutucuklardan tanıyoruz. Bu iki boyutlu resimler örneğin kodun tekrar kullanımını bize ne kadar ifade edebiliyor? Nasıl mimarlar tasarladıkları evlerin küçük maketlerini yapıyorlarsa, biz yazılım mühendisleri de legoları kullanarak oluşturmak istediğimiz yazılım sistemlerinin maketlerini oluşturabiliriz. Örneğin bir lego parçası tekrar kullanılabilir bir modülü simgeliyor olabilir. Yazılımcı bu lego parçasını eline alıp, sistemin değişik bölümlerinde tekrar kullanabilir. Bunu yaptığı esnada kodun tekrar kullanımının ne anlama geldiğini daha iyi anlayabilir, kodun tekrar kullamının getirdiği avantajları daha iyi görebilir. İnsan elinde tuttuğu üç boyutlu bir nesneyi daha kolay algılama eğilimi gösteriyor. Ben de dün kızımla beraber oynarken bunun farkına vardım. Lego parçalarını bir an için tekrar kullanılabilir yazılım modülleri olarak hayal ettim. Aynı renkte ve boyuttaki lego parçalarını değişik kalelerin (yazılım sistemi olarak düşünün) yapımında kullandım. Lego parçaları birer modül ve modülleri bir araya getirerek, değişik sistemler oluşturmak mümkün. Her defasında temel bir lego modülünü icat etmek zorunda kalmadan tekrar tekrar yeni bir sistemin inşasında kullanabiliyorum. İşte oluşturduğumuz yazılım sistemleri de bu prensibe göre inşa edilmeli. Yazılımda işin sırrı tekrar kullanılabilir modüllerde yatıyor. Kanımca genç bir yazılımcıya bahsettiğim kavramları somutlaştırıp, eline alabileceği üç boyutlu nesneler olarak sunduğumuz taktirde, bu kavramların algılanması daha da somutlaşıyor. Belki yazılım maketleri oluşturmak yazılım mimarilerinin esnekliğini artırabilir. Ne dersiniz? Bu konu hakkında biraz kafa yormamız lazım!
|
|
Posted: 24 Apr 2011 12:43 AM PDT 4.4.2011 tarihinden itibaren İsviçre’nin Zürich kentinde bulunan Credit Suisse bankası için freelance danışman olarak çalışmaya başladım. Banka bünyesindeki uygulamaların çoğunu IBM Mainframe sistemleri üzerinde çalışan PL/1 uygulamaları oluşturuyor. Bu uygulamalar Iona firmasının Corba ORB (Object Request Broker) ürünü kullanılarak diğer sistemler tarafından kullanılır hale getirilmiş. Webservis teknolojilerinin gelişmesi ile Corba teknolojileri yanında Webservis teknolojileri de kullanılmaya başlanmış. Benim ilk faaliyet alanım, Mainframe üzerinde çalışan Corba ve Webservis uygulamaları için yapılacak performans testlerinin standart bir hale getirilmesi olacak. Bu amaçla testleri otomatik olarak oluşturacak bir generatör programı üzerinde çalışıyorum. Bu generatör, HP Loadrunner programı bünyesinde çalışacak olan Corba ve Webservis servis testlerini otomatik olarak oluşturarak, performans testlerini koşturacak olan test mühendislerinin testlere odaklanmalarını sağlamış olacak. Credit Suisse bünyesindeki çalışmalarım hakkında gelişmeleri burada sizlerle paylaşmaya devam edeceğim.
|
|
Posted: 17 Apr 2011 02:42 AM PDT Yaklaşık 10 aylık bir çalışmanın sonunda 1 şubat 2011 tarihinde İşbankası Corebanking projesindeki görevimi tamamladım. Corebanking projesi, İsbankası’nın 2 sene önce başlatmış olduğu, Cobol ile geliştirilen Mainframe sistemlerinden açık sistemlere (Java, J2EE) geçişi öngören bir proje. Projenin nihayi amacı uzun vadede bankanın alt yapısını tamamen açık sistemlere taşımak ve Mainframe sistemlerini devre dışı bırakmak.
Danışman, teknik ekip lideri ve programcı olarak çalışmış olduğum bu 10 aylık zaman diliminde, Corebanking projesindeki faaliyet alanlarım şu şekilde oldu:
İlk çalışma alanlarımdan birisi build ve deployment işlemlerini otomatize etmek oldu. Bunun için gerekli Ant skriptleri oluşturdum ve build işlemlerinin merkezi bir yerden tetiklenmesini sağladım. Bir proje bünyesinde kodun sürekli entegre edilmesi (Continuous Integration) önemli ve gerekli bir işlemdir. Bu şekilde entegrasyon problemlerini zamanında lokalize etmek mümkündür. Bu amaçla Corebanking projesi için Cruise Control kullanarak bir sürekli entegrasyon serveri oluşturdum. Bu server programcıların yaptığı her commit sonunda otomatik olarak tüm kodu Clear Case’den alarak derliyor ve mevcut testleri kosturuyor. Altta yer alan resimlerde de görüldüğü gibi bir build monitörü kullanarak, projelerin entegrasyon işlemlerinin ne durumda olduğunu takip etmemiz mümkün. Herhangi bir kırılma olması durumunda gerekli programcılar email aracılığı ile uyarılıyor. Bunun yanısıra entegrasyon monitörü herkesin gözü önünde olduğundan, entegrasyon sürecinin hangi safhada olduğunu görmek her zaman mümkün. Diğer bir faaliyet alanım performans testleri esnasında performans ölçümünde kullanılabilecek metriklerin oluşturulması idi. Bu amaçla AspectJ (Java’da Aspect Oriented Programming yapmak için kullanılan bir framework) kullanarak perfomans aspektleri oluşturdum. Bu aspektleri kullanılan teknolojiye göre (Webservice, Rest, JDBC) metot bazında işlem tamamlama süresini ölçecek şekilde programladım. Tüm proje bu aspektler kullanılarak derlendikten sonra, kod bazında bir değişiklik yapmak zorunda kalmadan, işlem tamamlama sürelerini ölçmek mümkün oldu.
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect("perthis(execution(public * com.kurumsaljava.core.restapi.*Resource.*(..)))")
public class RestPerformanceMonitor extends AbstractPerformanceMonitor
{
@Before("execution(public * com.kurumsaljava.restapi.*Resource.*(..))")
public void before()
{
startMonitor();
}
@After("execution(public * com.kurumsaljava.restapi.*Resource.*(..))")
public void after()
{
stopMonitor("[performance:rest]");
}
}
Çevik süreçleri kullanarak iterasyon bazlı proje yönetimi yapabilmek için kullanıcı hikayelerinin (user story) oluşturulması gerekiyor. Corebanking bünyesindeki bir projenin yazılım sürecini hikaye kartları (story card) kullanarak yapılandırdık. Hikaye kartları üzerinde kullanıcı hikayeleri yer aldı. Aşağıdaki resimlerde görüldüğü gibi çalışma alanımızın bir duvarını hikaye kartlarını barındıracak biçimde şekillendirdik. Son dört aylık çalışmamın temelini, Corebanking projesinin daha modüler ve geliştirilebilir bir alt yapıya geçirilmesi işlemi oluşturdu. Corebanking uygulamaları, oluşturduğum ve Corebanking Next Generation (CBNG) ismini verdiğim bu yeni alt yapı ile şimdi İşbankasi’nın production sistemlerinde deploy ediliyor. Neden CBNG’ye gerek duyulduğunu sizlere kısaca aktarmak istiyorum. Başlangıçta tek bir uygulama olarak yola çıkan Corebanking projesi, zaman içinde büyüyerek, bünyesinde üç ve daha fazla uygulama barındırmaya başlamış. Prensipte birbirlerinden bağımsız olan bu projeler ne yazık ki aynı kod köklerine sahip olduklarından, birbirlerinden bağımsız olarak deploy edilmeleri mümkün olmuyordu. Yaşadığımız en büyük sorun, hazır olmayan ve test edilmemiş kodun, Corebanking bünyesindeki herhangi bir uygulamanın test ya da production sistemlerine çıkmayı istemesi durumunda, bu projeyle beraber gitmek zorunda olmasıydı, çünkü tüm Corebanking uygulamaları bir EAR dosyası olarak deploy ediliyordu. Bir başka sorun ise, proje yöneticilerinin sürekli kendi test ya da production çıkışlarını diğer projelerle koordine etmek zorunda olmaları idi. Bu şartlar altında yeni Corebanking uygulamalarının geliştirilmesi, yani binanın üzerinde yeni katlar çıkılması imkansız bir hal alıyordu. Buradan yola çıkarak mimari ekip / teknik lider toplantılari bünyesinde Corebanking Next Generation alt yapısına geçiş fikri oluştu ve kısa bir zaman sonra bu yeni alt yapıyı oluşturmak için çalışmalara başladım. Prensip olarak yeni alt yapının “Programming in the large” modeline göre tasarlanması gerekiyordu. Bu modele göre proje bünyesindeki birçok alt proje, ufak ekipler tarafından, birbirlerini etkilemeden, versiyonlanmış modüler ve komponentler kullanılarak geliştirilebilir hale geliyor. Yeni alt yapı büyük bir gökdelen inşa etmek yerine, bir site oluşturarak, bu site içerisinde 2-3 katlı villalar kurulmasına izin veriyor. Her bir villayı bir proje olarak düşünürsek, ekipler birbirlerinden bağımsız olarak kendi villalarını oluşturabiliyorlar. Kulağa hoş gelen bu model, ne yazik ki 2 senelik bir kod tabanına sahip bir projeden yola çıkıldığında uygulaması çok zor bir hal alabilir. Karşılaştığım en büyük problemlerden birisi, her proje tarafından kullanılan ortak sınıfların lokalize edilmesi ve her proje tarafından kullanılabilir bir modül haline getirilmesi oldu. Bunun yanısıra sınıflar arasında sirküler bağımlılıkların (circular dependency) olması, ortak kullanılabilir modüllerin ortaya çıkmasını zorlayan bir durumdu. Yer yer DRY (Do not repear yourself) prensibine uyulmadığı için aynı özellikleri taşıyan sınıfların birden fazla lokasyonda bulunması ve konsolide edilmesi karşılaştığım diğer bir sorundu. İşe mevcut tüm projeleri Maven projesi olacak şekilde yeniden yapılandırarak başladım. Daha sonra Corebanking bünyesindeki en ufak çaplı projeyi seçerek, bu projeyi yeni modele uygun şekilde yeniden yapılandırdım. Bu esnada diğer projelerinde kullanabileceği ortak modüller şekillenmeye başladı. Bu şekilde adım adım ilerleyerek ilk Corebanking uygulamasını CBNG konform hale getirdim. Bu işlemin ardından projenin tüm testlerini (1000 üzerinde acceptance testleri) koşturarak, yeni yapıyı test ettik. Testler yüzde yüz çalışır hale gelinceye kadar yeni alt yapıyı rekonfigüre ettim. Bu işlemler tamamlandıktan sonra yeni alt yapıyı programcı ekibe teslim ettim. Bu şekilde yeni alt yapı sahada yayılmaya başladı. Bu noktadan itibaren iki Corebanking uygulaması, eski ve yeni dünya olarak paralel yaşamaya başladı. Tüm projeyi durdurup, 1-2 ay yeni alt yapıyı oluşturmak için ayıramayacağımız için bu şekilde bir seçim yapmak zorunda kaldık. Kısa bir zaman sonra ilk CBNG uygulaması bir EAR dosyası olarak production sistemlerinde deploy edildi. Akabinde diğer bir Corebanking uygulamasını aynı şemayi takip ederek eski alt yapıdan yeni alt yapıya uyarladım ve sahada kullanılır hale gelmesini sağladım. Bu sürecin sonunda Corebanking tamemen transform edilmiş ve CBNG olarak hayatını sürdürmeye devam ediyor olacak. Bu gerçekleşene kadar eski ve yeni alt yapı iki değişik EAR olarak yanyana yaşamaya devam edecek. Lokal repository olarak Nexus serverini kurdum. Bu server bünyesinde ortak kullanılan tüm modüller versiyonlanmış JAR dosyalar olarak yer aldı. Nexus ve Maven bağımlılıkların otomatik yönetimini ve kod birimlerinin ortak ve yeniden kullanımını sağlayan alt yapı komponentleri. Bu şekilde herhangi bir CBNG projesi ihtiyaç duydugu bir modülü Nexus‘dan temin edebilir. Aynı şekilde bu proje diğer projelere sunmak istediği modülleri versiyonlanmış bir Jar dosyası olarak Nexus bünyesine katabilir. Benim için şüphesiz en heyecan verici faaliyet alanı CBNG oldu. Geliştirme plarformu olarak kullandığımız RAD 7 (Eclipse 3.2) mümkün olan tüm engelleri ortaya koyarak, yaptığım işin hergün heyecan verici kalmasını sağladı :) En büyük sorunlardan birisi RAD‘ın eski bir Eclipse versiyonu olduğu için M2Eclipse pluginini aktüel versiyonunda kullanamıyor olmamdı. Eski ve birçok bugı olan bir M2Eclipse plugin versiyonu ile çalışmanın ne kadar ve heyecan verici olabileceğini düsünebilirsiniz :) Oluşan birçok problem için birçok workaround bulmak zorunda kaldım. Bu kadar sorunla karşılaşacağımı önceden biliyor olsaydım, acaba CBNG‘yi hayata geçirmeye kalkarmıydım? Beni tanıyanlar bu sorunu cevabını biliyor :) Corebanking Next Generation (CBNG) hakkında yazabileceğim sayfalar dolusu anılarım var. Lakin banka bünyesinde olup bitenleri çok detaylı bir şekilde anlatmak doğru olmaz. Danışman ya da banka çalısanı olarak dikkat etmemiz gereken en önemli konulardan birisi, gizlilik prensibine uymaktır. Corebanking projesi bünyesinde birçok yetenekli programcıyla çalışma fırsatı buldum. Bilgi alışverişine açık bir ortamda çalışmak gerçekten çok zevk vericiydi. Bu programcı arkadaşlara bu vesile ile buradan kucak dolusu selam ve sevgilerimi gönderiyorum. EOF ( End of Fun) Özcan Acar |
|
Posted: 26 Oct 2010 01:13 AM PDT Danışman olarak çalıştığım bir projenin modüllerini Maven2 kullanacak şekilde yeniden yapılandırdım. Birçok JAR ve WAR dosyası bir araya gelerek büyük bir EAR dosyası oluşturdu. JAR ve WAR’ları oluştururken bir sorun yaşamadım, lakin EAR dosyasını alırken durum farkli idi. EAR projesi için maven clean install komutunu kullandığımda aşağıdaki hata oluşuyordu:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:515)
at java.lang.StringBuffer.append(StringBuffer.java:306)
at java.io.StringWriter.write(StringWriter.java:77)
at hidden.org.codehaus.plexus.util.IOUtil.copy(IOUtil.java:214)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:416)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:405)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1595)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1571)
at org.apache.maven.project.DefaultMavenProjectBuilder.findModelFromRepository(DefaultMavenProjectBuilder.java:562)
at org.apache.maven.project.DefaultMavenProjectBuilder.buildFromRepository(DefaultMavenProjectBuilder.java:251)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedProject(MavenMetadataSource.java:163)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedArtifact(MavenMetadataSource.java:94)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:387)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.collect(DefaultArtifactCollector.java:74)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:316)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:304)
at org.apache.maven.plugin.DefaultPluginManager.resolveTransitiveDependencies(DefaultPluginManager.java:1499)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:442)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 seconds
[INFO] Finished at: Tue Oct 26 11:40:45 EEST 2010
[INFO] Final Memory: 9M/9M
[INFO] ------------------------------------------------------------------------
C:\CCVIEWS\itcs_oacar_UCM_YUKSELIS\CBVOB01\CorebankingAppBuilder>
EAR dosyası birçok JAR ve WAR’dan oluştuğu için doğal olarak büyük bir hacme sahip. Default Maven2 ayarları bu boyuttaki bir EAR dosyasını oluşturmak için yeterli değil. Bu sorunu aşmak için Maven’in daha fazla hafıza alanını kullanabilmesi gerekiyor. Bunu sağlamak için Windows işletim sistem ile çalışıyorsanız, console altında set MAVEN_OPTS=-Xmx512m ya da Linux/Unix altında export MAVEN_OPTS=-Xmx512m komutunu girmeniz yeterli. Bir sonraki mvn komutu tanımladığınız şekilde yeni hafıza alanı ile çalşacaktır. |
|
Posted: 25 Oct 2010 11:21 PM PDT Bug ingilizce dilinde böcek, bilgisayar dillerinde bir program hatası anlamına geliyor. İlk program bug’ı 1947 yılında Grace Murray Hopper’in Harvard Üniversitesi’nde kullandığı Mark II Aiken isimli röle (relay) (resim 1) bazlı hesaplayıcıda (primitif bir bilgisayar) bulundu.
9 eylül 1947 tarihinde hesaplayıcının programlandığı şekilde çalışmadığı, sorun çıkardığı görüldü Yapılan araştırma üzerine F panelindeki 70 numaralı rölenin bacakları arasında bir güvenin (moth) sıkışıp kaldığı görüldü. Program hatasının sebebi bulunmustu; bir güve yani bir böcek (ingl. bug). Bilgisayar programlama tarihine ilk program hatası olarak geçen bu böcek operatör tarafından log defterine resim 2’deki şekilde eklendi.
Bu işlemin ardından operatörler hata giderildi anlamına gelen debugged (böcek temizlendi) kelimesini kullanmaya başladılar. Bizim bugünlerde sıkça kullandığımız debug kelimesinin kökeni bu güveden geliyor :) Bug kelimesi 1947’den öncede sistem hataları için kullanılan bir terimdi. Örneğin Amerika’daki ilk telefon şebekelerinde sesin karıncalı gelmesi durumunda hatta hata var anlamına gelen „bugs in a telephone cable“ ifadesi kullanılırmış. |
|
Posted: 23 Oct 2010 09:25 AM PDT Çoğu zaman programcı adaylarının piyasada en çok talep gören programramlama dilini seçip, bu dili öğrendikleri malum. Bu doğal bir seçim; talep olan yerde arzın bedeli olur. Bu bedel programcının iyi bir maaş ile hayatını sürdürmesi anlamına gelir.
Bilindiği üzere son zamanların en popüler ve talep edilen dili Java. İnternetteki birçok istatiktik Java’nın bir numara olduğunu tastikliyor. Programcı adaylarının da Java’yı seçmeleri doğal. Java’yi bilenler ve kullananlar iyi ve akıllı programcılardır degil mi? Peki Python ya da piyasası pek fazla olmayan bir başka dili bilen bir programcının sadece Java’ya hakim bir programcıdan daha iyi ve akıllı bir programcı olduğunu söylesem nasıl tepki verirdiniz? İyi programcı, program yazmayı seven programcıdır. İyi programcı yenilikleri deneyip, ufkunu genişleten programcıdır. İyi programcı programcılık dünyasının sadece Java’dan oluşmadığını bilendir. Peki iyi programcıyı nasıl anlarsınız? Bir sonraki iş görüşmesinde işe alınacak programcı adayına Java haricinde hangi dil ya da dilleri bildiğini sorun. Alacağınız cevap programcının ne kadar iyi olduğunu gösteren indikatördür. Eğer aday sadece Java’yı bildiğini söylerse iyi bir programcı olma ihtimali doğru olabilir. Bunun ispatı edindiği tecrübeler ve bilgi birikimidir. Eğer aday Java’nın yanında Python ya da Groovy dillerine de hakim olduğunu söylerse, bilin ki karşınızda akıllı (smart) bir aday duruyor. Neden? Bunun nedeni çok basit. Piyasası olmayan bir programlama dilini ögrenmiş bir programcı, program yazmayı gerçekten seviyor olmalı ki talebin dışında kalan bir programlama dilini zaman ayırarak öğrenmiş. Bu onun yenilikçi, ögrenmeyi ve program yazmayı seven birisi oldugunu gösterir. Karşınızdaki bu kişi büyük bir ihtimalle sadece Java bilen bir adaydan çok daha akıllı ve programcılık konusunda ileri seviyede. Yanlış anlaşılmasın; Java’ya hakim olanlarda mutlaka iyi programcılardır. İyileri de var, iyi olmayanları da. Ama piyasının ihtiyacı olmayan bir dili öğrenen bir programcı çok daha fazla potansiyele sahip. Bu bir gercek! Ben böyle bir programcıyı tercih ederdim. Sadece bir programlama dilinde (bu genelde piyasası olan bir dil olacaktır) takılıp kalmış olanların verdiği cevap hep aynı olacaktır: “Çalıştığım ortamlarda yeni bir programlama dili öğrenme fırsatı bulamadım.” Gerçekten programcılığa gönül vermiş birisi o fırsatı hemen yaratır! Bu konuda Paul Graham’ın yazısını okumanızı tavsiye ederim. |
|
Common Reuse Principle (CRP) – Ortak Yeniden Kullanım Prensibi Posted: 24 Jul 2010 12:41 PM PDT |
|
Posted: 24 Jul 2010 12:20 PM PDT Mevcut bir sınıf hiyerarşisini ya da sınıfın yapısını değiştirmeden, oluşturulan nesnelere yeni özelliklerin eklenme işlemini gerçekleştirmek için Decorator tasarım şablonu kullanılır.
Alt sınıfların oluşturulması yöntemiyle, sınıflara yeni özelliklerin eklenmesi, daha sonra sisteme eklenecek alt sınıflar için değiştirilmesi zor kalıpların oluşmasını beraberinde getirir. Bu durumda, üst sınıflarda tanımlanmış olan bazı özellikler statik ve alt sınıflar için değistirilemez ya da kullanımı engellenemez bir hal alabilir. Kullanıcı sınıflar içinde bu sorun teşkil edebilir, çünkü kendi istekleri doğrultusunda bir nesnenin ne zaman ve nasıl oluşturulması gerektiğini yönlendiremeyebilirler. Nesnelere, sahip oldukları sınıfların yapılarının değistirilmeden yeni özelliklerin eklenmesini sağlayan Decorator tasarım şablonu ile, istenilen özelliklerin ekleneceği nesne başka bir nesne içine gömülür. Yeni özellik eklenen nesneyi içine alan nesneye dekoratör ismi verilir. Dekoratör nesnesi ile yeni özellik eklenen nesne aynı üst sınfa dahil olduklarından, birbirleriyle değiştirilebilir haldedirler. Bu özellikten dolayı kullanıcı sınıf, dekoratör sınıf ile dekoratör nesne bünyesinde bulunan diğer nesne arasında ayrım yapmaz. Nesneler arası ilişkiye aşagıda ye ralan Uml diagramında görüyoruz. Bu yazıyı PDF olarak edinebilirsiniz.
|
|
K.Maraş Sütçü İmam Üni. – Bilge Programcı – Nasıl Uzman Yazılımcı Olunur ? Posted: 02 Jul 2010 10:47 PM PDT 25 mart 2010 tarihinde K.Maraş Sütçü İmam Üniversitesi’ne bir sunum yapmak için davet edildim. Seminer videosunu, resimleri ve sunumu bu yazıda bulabilirsiniz. Özcan Acar
Sunum dosyasını PDF olarak aşağıdaki linkten edinebilirsiniz.
|
|
Posted: 26 May 2010 07:42 AM PDT Bir Java sınıfının hangi Java compiler versiyonu ile derlendiğini görmek için javap komutu aşağıdaki şekilde kullanılır.
C:\>javap.exe -verbose tumbler.Story
Compiled from "Story.java"
public interface tumbler.Story extends java.lang.annotation.Annotation
SourceFile: "Story.java"
RuntimeVisibleAnnotations: length = 0x1B
00 02 00 09 00 01 00 04 65 00 0A 00 0B 00 0C 00
01 00 04 5B 00 01 65 00 0D 00 0E
minor version: 0
major version: 50
Constant pool:
const #1 = class #15; // tumbler/Story
const #2 = class #16; // java/lang/Object
const #3 = class #17; // java/lang/annotation/Annotation
const #4 = Asciz value;
const #5 = Asciz ()Ljava/lang/String;;
const #6 = Asciz SourceFile;
const #7 = Asciz Story.java;
const #8 = Asciz RuntimeVisibleAnnotations;
const #9 = Asciz Ljava/lang/annotation/Retention;;
const #10 = Asciz Ljava/lang/annotation/RetentionPolicy;;
const #11 = Asciz RUNTIME;
const #12 = Asciz Ljava/lang/annotation/Target;;
const #13 = Asciz Ljava/lang/annotation/ElementType;;
const #14 = Asciz TYPE;
const #15 = Asciz tumbler/Story;
const #16 = Asciz java/lang/Object;
const #17 = Asciz java/lang/annotation/Annotation;
{
public abstract java.lang.String value();
}
thumbler.Story sınıfı için major number 50, minor number 0 görünmektektedir. Aşağıdaki listeden bu sınıfın hangi Java compiler versiyonu ile derlendiğini anlamak mümkündür. * Java 1.2 uses major version 46
|
|
Bilge Programcı – Nasıl Uzman Yazılımcı Olunur? Seminerinden İzlenimler Posted: 22 May 2010 04:00 AM PDT 24 mart 2010 tarihinde Elazığ Fırat Üniversitesi’ne bir sunum yapmak için davet edildim. Seminer videosunu, resimleri ve sunumu bu yazıda bulabilirsiniz. Özcan Acar
Sunum dosyasını PDF olarak aşağıdaki linkten edinebilirsiniz.
|
|
CETURK Kıbrıs Java ve Kariyer Günü Posted: 31 Mar 2010 02:11 AM PDT CETURK tarafından 3 nisan 2010 tarihinde Kıbrıs Doğu Akdeniz Üniversitesi’nde düzenlenen Java ve Kariyer Gününe “Bilge Programcı. Nasıl uzman yazılımcı olunur?” başlıklı sunumumla katılıyorum.
![]() Detaylar için bakınız >> |
|
Kurumsal Java Akademisi Seminerleri Başlıyor! Posted: 17 Mar 2010 11:39 AM PDT |
|
Posted: 12 Mar 2010 03:21 AM PST 15 mart’dan 30 mart 2010’a kadar, üniversitelerde ücretsiz KurumsalJava.com seminerleri düzenlemek üzere İstanbul’da bulunacağım. İlgilenler bana acar[AT]unitedinter.net adresinden ulaşabilirler.
___________________________ Özcan Acar Sun Java Champion |
|
Posted: 06 Mar 2010 10:26 AM PST Java ile yazılımı tam anlamıyla kavramak isteyenler mutlaka bu kitabı okumalı! ![]() |
|
Java JVM’i (Java Virtual Machine) Anlamak Posted: 06 Mar 2010 09:59 AM PST Java JVM’i tam anlamıyla kavramak isteyenler mutlaka bu kitabı okumalı! ![]() |
|
Posted: 06 Mar 2010 09:52 AM PST Java dilini tam anlamıyla kavramak isteyenler mutlaka bu kitabı okumalı! ![]() |
|
Java Projelerinde Teknik Liderlik Pozisyonu Posted: 06 Mar 2010 02:15 AM PST Türkiye’de çalışma hayatımı sürdürmek üzere, projelerinde Java kullanan firmalarda teknik takım liderliği, kurumsal mimari ve çevik yazılım süreçlerinin uygulanması sorumluluğuna sahip iş pozisyonları arıyorum. Teklif ve CV için acar(AT)unitedinter.net adresinden bana ulaşabilirsiniz.
Özcan Acar Sun Java Champion |
|
Posted: 23 Feb 2010 03:39 AM PST Java_tr grubunda aşağıdaki yer alan soru soruldu. Bu soru ve benim bu konudaki düşüncelerim aşağıda yer almaktadır. Java’da bulmaca sevenlere; Aşağıda ki kod parçası için çıktı ne olur,
public class FinalizerTest {
<span id="more-1098"></span>
public FinalizerTest() {
System.out.println("constructed");
throw new RuntimeException();
}
protected void finalize() throws Throwable {
System.out.println("finalized");
}
public static void main(String[] args) {
try {
new FinalizerTest();
} catch (Exception e) {
System.out.println("exception");
}
System.gc();
}
}
Benim verdiğim cevap şu şekildedir:
Yukarıda yer alan programın konstruktöründe try catch ile hata yakalanarak, daha önce bahsettiğim terminasyon metodu (örneğin myExit()) koşturulmalıdır.
|
|
Java’da Final Anahtar Kelimesi Posted: 17 Feb 2010 03:21 PM PST Java’da final anahtar kelimesi aşağıda yer alan yapılar için kullanılabilir:
Final anahtar kelimesi Java programcılığında en çok göz ardı edilenlerdendir. En son siz ne zaman final anahtar kelimesini kullandınız? Final anahtar kelimesinin kullanımında yapılan en büyük hatalardan birisi final static olarak tanımlanmış olan bir sınıf değişkenine değistirilebilir bir değerin atanmasıdır. Örneğin aşağıdaki tanımlama yanlıştır, çünkü static değişken final olmasına rağmen, bu değişkene atanan değer, kullanıcı tarafından değiştirilebilir.
public class App
{
public static final String[] LIST = {"1","2","3"};
public static void main(String[] args)
{
String[] list = App.LIST;
list[0] = "2";
System.out.println(App.LIST[0]);
}
}
Yukarda yer alan main() metodunda final olan LIST degiskeninin ihtiva ettiği array değiştirilmektedir. Bu sorunu ortadan kaldırmak için LIST değişkeni private yapılabilir ve bu değişkeni baz alarak içeriğinin değiştirilemeyeceği bir array değerini geri döndüren metot oluşturulabilir. Bunun bir örneği aşağıda yer almaktadır:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class App
{
private static final String[] LIST = {"1","2","3"};
public static final List<String> getList()
{
return Collections.unmodifiableList(Arrays.asList(App.LIST));
}
public static void main(String[] args)
{
List<String> list = App.getList();
list.remove(0);
System.out.println(list);
}
}
Bu program koşturulduğunda alınacak hata şu şekildedır:
Bu şekilde final olarak tanımladığımız LIST değişkenin ihtiva ettiği değerleri üzerinde oynama yapmak mümkün değildir. getList() metodu bünyesinde Collections.unmodifiableList() ile içeriğinin değiştirilmesi mümkün olmayan bir liste oluşturulmaktadır.
|
|
Posted: 06 Feb 2010 02:22 AM PST Java 1.5 ile kullanıma sunulan ve covariant return type ismini taşıyan bir yapı mevcuttur. Java 1.5 öncesi bir alt sınıf, bir üst sınıfın sahip olduğu metodu yeniden implemente etmek (method overriding) istediginde, bu metodun geri döndürdüğü veri tipinin, üst sınıftaki metot ile aynı olması gerekmekteydi. Bunun bir örneği aşağıda yer almaktadır.
package test.controller;
import java.util.Map;
import test.criteria.Criteria;
public abstract class AbstractController implements Controller
{
public void executeFilter()
{
Map<String, String> map = createCriteria().getCriteriaMap();
}
public abstract Criteria createCriteria();
}
Soyut (abstract) olan AbstractController sınıfı bünyesinde, alt sınıflarda implemente edilmek üzere createCriteria() isminde bir metot tanımlıyoruz. AccountTableController sınıfı bu metodu implemente etmektedir.
package test.controller;
import java.util.HashMap;
import java.util.Map;
import test.criteria.MyCriteria;
import test.model.Konto;
public class AccountTableController extends AbstractController
{
private Account model = new Account();
public Criteria createCriteria()
{
Map<String, String> map = new HashMap<String, String>();
map.put("kontonr", model.getKontonr());
map.put("blz", model.getBlz());
return new Criteria(map);
}
}
Görüldüğü gibi AccountTableController.createCriteria() metodu Criteria tipinde bir degeri geri döndürmektedir. Bu metodun geri döndürdüğü değerin Java 1.5 öncesi Criteria tipinde olması gerekmektedir, çünkü üst sınıfta tanımlanmış olan bu metodun, üst sınıf bünyesinde geri döndürdüğü değer Criteria tipindedir. Java 1.5 ile bu durum değişmiştir. Üst sınıfta yer alan bir metot alt sınıfta tekrar reimplement edilirse (method overriding), reimplemente edilen metodun geri döndürdüğü değerin tipi, üst sınıftaki metodun geri döndürdüğü sınıfın bir alt sınıfı olabilir. Bunun bir örneği aşağıda yer almaktadır.
package test.controller;
import java.util.HashMap;
import java.util.Map;
import test.criteria.MyCriteria;
import test.model.Konto;
public class AccountTableController extends AbstractController
{
private Account model = new Account();
@Override
public MyCriteria createCriteria()
{
Map<String, String> map = new HashMap<String, String>();
map.put("kontonr", model.getKontonr());
map.put("blz", model.getBlz());
return new MyCriteria(map);
}
}
MyCriteria, Criteria sınıfının bir alt sınıfıdır ve AccountTableController sınıfı implemente ettiği createCriteria() metodunun MyCriteria tipinde bir değeri geri döndürmesi sağlamıştır. Bu şekilde cast işlemi yapılmadan alt sınıfların daha zengin tipte verileri geri döndürebilmeleri amaçlanmaktadır.
|
|
Java Enum ile Singleton Tasarım Şablonu Posted: 02 Feb 2010 12:32 AM PST Daha önceki bir yazımda Singleton tasarım şablonunun ne olduğunu ve Java’da nasıl kullanıldığını yakından incelemiştik. Bu yazımda Java 1.5 den itibaren kullanıma sunulan Enum sınıfı ile singleton tasarım şablonunun nasıl kullanılabileceğine değinmek istiyorum.
Öncelikle küçük bir örnek üzerinde singleton bir nesnesinin nasıl oluşturulacağına bir göz atalım:
package com.kurumsaljava.com.designpatterns.singleton;
public class MySingleton
{
private static final MySingleton instance = new MySingleton();
private MySingleton()
{
}
public void myMethod()
{
}
}
MySingleton sınıfının sahip olduğu konstruktör private olduğu için bu sınıftan bir nesne oluşturmamız mümkün değildir. Bu sınıf bünyesinde ve MySingleton tipinde olan instance ismindeki sınıf değişkeni singleton nesnedir. Bu sınıf ilk kullanıldığında private olan konstruktör bir defaya mahsus olarak işlem göreceği için instance degişkeni bir singleton nesne haline gelecektir. Buradaki sorun, reflection yöntemleri kullanılarak private olan konstruktörün birden fazla singleton nesne oluşturmasını sağlayabilmektir. Bunun yanısıra eger MySingleton sınıfı Serializable interface sınıfını implemente ediyorsa, readResolve() metodunun da implemente edilmesi gerekmektedir, aksi taktirde deserializasyon esnasında yeni bir nesne oluşturulur ki, bu da singleton tasarım şablonunun mantığına ters düşer. Java 1.5 den itibaren kullanıma sunulan Enum sınıfı ile singleton nesneler oluşturmak mümkündür. Bunun bir örneği aşağıda yer almaktadır.
package com.kurumsaljava.com.designpatterns.singleton;
public enum MySingletonEnum
{
INSTANCE;
public void myMethod()
{
}
}
MySingletionEnum.INSTANCE sadece ve sadece bir kere mevcut olan bir singleton değişkendir. Bunun yanısıra reflection metotları kullanılarak yeni bir singleton olmayan nesne oluşturulamaz. Ayrıca serializasyon işlemlerinde readResolve() kullanılmak zorunda kalmadan enum nesnesinin singletonluğu garanti edilmektedir. Tek elementli olan bir enum sınıfı singleton tasarım şablonunu implemente etmek i.in kullanılabilecek en iyi yöntemdir. |
|
Oracle’ın Sun ve Java için stratejik planları hakkında ip uçları Posted: 01 Feb 2010 11:20 PM PST JUG (Java User Group) e-posta listesinde olduğum için bana ulaşan bir e-postayı sizlerle paylaşmak istedim. E-posta Oracle’ın Sun ve Java için stratejik planları hakkında ip uçları verebilir…
———————————– “Oracle plans to increase investment in Java, Sparc, Solaris, and I will provide more info if I can get my hands on it. Justin Kestelyn On Feb 1, 2010, at 4:02 PM, Joshua Partogi wrote: > Hi all, |
|
Posted: 15 Jan 2010 07:48 AM PST DBUnit JUnit ile entegre edilerek entegrasyon ve regresyon testlerinin JUnit testleri olarak implemente edilmesi mümkündür. DBUnit JUnit entegrasyonu aşağıdaki şekilde gerçekleştirilebilir. Bilgibankası olarak örnekte HSQLDB kullanılmıştır (bakınız HSQLDB JUnit entegrasyonu). Verilerin dbunit-dataset.xml dosyasında tanımlanmıs olması gerekmektedir. setUp() metodu her test öncesi JUnit frameworkü tarafından koşturulacağı için istenilen veriler test öncesi bilgibankasına eklenmiş ve regresyon testleri için taban oluşturulmuş olacaktır.
// JDBC Driver
public static final String DRIVERCLASS = "org.hsqldb.jdbcDriver";
// standalone mode
public static final String CONNECTIONURL = "jdbc:hsqldb:hsql://localhost:9006/mydb";
public static final String USERNAME = "sa";
public static final String PASSWORD = "";
public void setUp() throws Exception
{
PreparedStatement pstmt = null;
try
{
IDatabaseConnection connection = getConnection();
IDataSet dataSet = getDataSet();
try
{
// REF INTEGRITY MUST BE DISABLED IN ORDER TO IMPORT THE DATA
// THIS SOLUTION IS ONLY VALID FOR HSQLDB 1.8
pstmt = connection.getConnection().prepareStatement("set referential_integrity FALSE");
pstmt.executeUpdate();
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
pstmt = connection.getConnection().prepareStatement("set referential_integrity TRUE");
pstmt.executeUpdate();
}
finally
{
connection.close();
}
}
catch (Exception e)
{
throw e;
}
}
protected IDatabaseConnection getConnection() throws Exception
{
Class.forName(DRIVERCLASS);
Connection jdbcConnection = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
IDatabaseConnection con = new DatabaseConnection(jdbcConnection);
DatabaseConfig config = con.getConfig();
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
return con;
}
protected final IDataSet getDataSet() throws Exception
{
final URL url = DatabaseTestCase.class.getResource( "/dbunit-dataset.xml");
final File file = new File(url.getPath());
return new FlatXmlDataSet(file);
}
|
|
Posted: 15 Jan 2010 07:36 AM PST Oluşturduğum entegrasyon JUnit testlerinde, testler öncesi verilerin bilgibankasına aktarılması gerekiyor. Aşağıda yer aldığı şekilde JUnit sınıfı bünyesinde HSQLDB serveri standalone modda çalıştırılabilir ve DBUnit ile veriler bilgibankasına eklenebilir.
// JDBC Driver
public static final String DRIVERCLASS = "org.hsqldb.jdbcDriver";
// standalone mode
public static final String CONNECTIONURL = "jdbc:hsqldb:hsql://localhost:9006/mydb";
public static final String USERNAME = "sa";
public static final String PASSWORD = "";
private void startHsqlDb()
{
if(!isHsqlDbStarted())
{
Server hsqlServer = new Server();
hsqlServer.setAddress("localhost");
hsqlServer.setPort(9006);
hsqlServer.setDatabasePath(0, "db/hsqldb-data/");
hsqlServer.setDatabaseName(0, "mydb");
hsqlServer.start();
}
}
private boolean isHsqlDbStarted()
{
boolean result = false;
try
{
Class.forName(DRIVERCLASS);
Connection con = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
con.close();
result = true;
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
private static void stopHsqlDb()
{
try
{
Class.forName(DRIVERCLASS);
Connection con = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
Statement statement = con.createStatement();
statement.execute("SHUTDOWN");
statement.close();
con.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
|
|
SCEA 5 (Sun Certified Enterprise Architect) Kiti Posted: 09 Jan 2010 09:09 AM PST Daha önceki yazımda bildirdiğim gibi kısa bir zaman önce SCEA 5 sertifikasını aldım. Bugün Sun tarafından gönderilen sertifika kiti bana ulaştı. Kitin içeriği aşağıda yer alan resimlerde görülmekte.
|
|
SCEA 5 (Sun Certified Enterprise Architect) Posted: 29 Dec 2009 02:54 PM PST Bugün itibariyle SCEA 5 (Sun Certified Enterprise Architect) sertifikasını almış bulunuyorum. Bir seneye yayılan sertifikasyon sürecinde üç değişik sınav yaparak, bu sertifikayı edindim. Gerçekten kolay değildi, bir de JEE 5 alanında uzman olduğumu düşünürdüm ;-)
![]() SCEA 5 sertifikası Sun tarafından sunulan Java sertifikasyon programının en en son kademesinde bulunan ve yazılım mimarisi ile uğraşan yazılımcıların edinebilecekleri bir sertifika. SCEA 5 sertifikasyon süreci üç sınavdan oluşuyor. İlk sınav JEE 5 teknolojisini kapsayan iki ya da daha fazla seçenekli sorulardan oluşuyor. İkinci sınav bir JEE 5 projesini kapsıyor. Sun, sertifika adayına üzerinde çalışmak üzere bir proje tayin ediyor. Sertifika adayı Sun tarafından belirlenen kriterler doğrultusunda JEE 5 teknolojisi ile örnek bir çözüm hazırlayarak, çözümünü Sun’a iletiyor. Son sınavda sertifika adayı geliştirdiği proje hakkında sorular cevaplayarak, çözümün kendisine ait olduğunu ispatlamak zorunda. Sun Java sertifikasyon programi hakkında detayları bu link üzerinden edinebilrisiniz Kaynak Kitaplar
Sertifikasyon İÇERİĞİSCEA 5 üç bölümden (sınav) oluşuyor. Bunlar CX-310-052SCEA sertikasyonunun ilk basamağını CX-310-052 sınavı oluşturuyor. Bu sınavın kapsadığı alan şu şekilde: Section 1: Application Design Concepts and PrinciplesSection 2: Common ArchitecturesSection 3: Integration and MessagingSection 4: Business Tier TechnologiesSection 5: Web Tier TechnologiesSection 6: Applicability of Java EE TechnologySection 7: PatternsSection 8: SecuritySinav için hazırlıklarınızı tamamladıktan sonra Sun’a 200 dolar ödeyerek, bu sınava girebiliyorsunuz. Sınav yeri Prometric üzerinden belirlenebiliyor. CX-310-301ABirinci sınavı (CX-310-052) başarıyla tamamladıktan sonra sırada SCEA 5’in ikinci basamağı olan CX-310-301A yer almaktadır. Bu bir sınavdan ziyade JEE 5.0 teknolojisi ile geliştirilmiş bir projedir. Sertifika adayının JEE 5.0 teknoloji bütününü kullanarak verilen kriterler doğrultusunda projeyi tamamlaması ve Sun firmasına iletmesi gerekmektedir. Bana Sun tarafından Dreamcar projesi tayin edildi. Gizlilik prensipleri doğrultusunda burada ne yazik ki tüm detayları açıklamam mümkün değil. Lakin birkaç cümle ile gidişatın nasıl olduğunu sizlere aktarmak isterim. CX-310-301A çercevesinde Sun, sertifika adayının mevcut bir proje üzerinde mimari oluşturma ve açıklama yeteneklerini sınamaktadır. Sertifika adayının, proje gereksinimlerinin UML diagramları oluşturarak nasıl çözümlenebileceklerini açıklamaklası gerekmektedir. Sertifika adayı 6-10 arası değişen proje gereksinimini (requirement) Sequence Diagram, Class Diagram, Components Diagram ve Deployment Diagram kullanarak, oluşturduğu çözümü UML diagramları aracılığıyla görselleştirir. Sertifika adayının oluşturduğu çözümlerde hangi teknolojiyi ve tasarım şablonlarını kullandığını detaylandırması gerekmektedir. Dreamcar projesi için kullandığım tasarım şablonları şu şekilde idi: Presentation tier: MVC – Model View Controller MVC is used in most of the popular web frameworks (JSF, Struts…). In my implementation I used the domain objects as models, Java Server Pages as views and servlets as controller. View is only responsible for displaying data to the user. Navigation, validation and business logic access is done in controller classes. Presentation tier: View Helper Business domain objects are also used as view helpers when rendering JSPs. Presentation tier: Business Delegate In order to hide the complexity of accessing remote business logic (EJB) from clients I used a BusinessDelagate class. Presentation tier: Service Locator The BusinessDelegate class uses internally a ServiceLocator class to locate the remote business logic classes via JNDI calls. It caches remote interface instances to speed up the access to remote objects. Business tier: Session Facade The stateless session beans are used as session facade classes. The session facade centralizes and controls the access to business logic. Business tier: Business Object (BO) I used business object classes to encapsulate the business logic. The session beans uses the business object to delegate the business logic calls the business object instances. With business objects the business logic is encapsulated and reusable. Database tier: Data Access Object (DAO) I used DAO to isolate the database access for business tier. The business tier should not know any specific details about data storage. One can use other DAO implementations to change the data storage kind. Database tier: Domain Store I used the domain store design pattern to seperate persistance from my object model. JPA Entity manager is used to persist the busines domain objects. Sertifika adayından kod yazması beklenmemektedir. Çözümlerin UML diagramları aracılıği ile oluşturulmuş olması yeterlidir. CX-310-062Sertifikanın üçüncü basamağı CX-310-062‘dır. Bu sınavı yapabilmek için ikinci basamakta sertifika adayına verilen projenin tamamlanmış ve Sun’a iletilmiş olması gerekmektedir. Bu basamakta sertifika adayı, ikinci basamakta oluşturduğu çözüm hakkında kendisine sorulan soruları cevaplamak zorundadır. Bu şekilde sertifika adayının ikinci basamakta oluşturduğu çözümün kendisine ait olup, olmadıği kontrol edilmektedir. SonuçYazılım mimarisi ve Java ile uğraşan yazılımcıların gözdesi olan SCEA 5 sertifikası kolay elde edilebilecek bir sertifika değil. Bu sebepten dolayı da kıymetli ve itibarlı bir serfikadır. Sertifika adayının J2EE ve JEE 5 teknolojilerini yakından tanıması ve çalıştığı projelerde bu teknolojileri kullanmış olması, SCEA 5 sertifikasını edinmesini kolaylaştırmaktadır. SCEA 5 sertifikası diğer Java sertifikalarından bağımsız olarak alınabilmektedir. Bu sertifika için herhangi bir ön koşul bulunmamaktadır.
|
|
Türkiye’den İlk Java Champion Özcan Acar Posted: 22 Dec 2009 03:20 PM PST Bugün Java Champion olarak seçildim. Benim için çok mutlu bir gün, çünkü bir Java yazılımcısının edinebileceği en yüksek ünvanlardan birisi Java Champion ünvanıdır. James Gosling, Bill Burke, David Flanagan ve Josh Bloch gibi tanınmış sahışların Java Champion ünvanına sahip olduklarını ve dünya çapında 100 civarında Java Champion ünvanına sahip yazılımcının olduğunu düşünürsek bu ünvanın ne kadar itibarlı ve kıymetli olduğunu görebiliriz.
Bir Java Champion nedir? Bu konu hakkında detaylı bilgiyi Java Champion websayfasından edinebilirsiniz. Java Champion Sun tarafından hayata geçirilmiş ve desteklenen bir proje. Java Championlar Sun tarafından seçilmemektedir. Java Champion adayları, mevcut Java Championların kendi aralarında yaptıkları oylama sonucunda bu ünvanı kazanırlar. Java Championlar Sun firmasından bağımsız olarak çalışan, Java platformunun gelişmesine katkıda bulunan yazar, eğitmen, senior mimar ve yazılımcılardır. ![]() Resim 1 Java Champion listesinde ismimi görebilirsiniz. Özcan Acar |
|
Posted: 21 Dec 2009 02:38 PM PST Sürücü üzerinde (disk) ya da hafıza (memory) da yer alan ve verinin depolandıktan sonra alındığı ön belleğe write through cache ismi verilmektedir. Bu şekilde veri deposuna eklenmiş olan veri ön belleğe de eklenmiş olur. Write through cache sayesinde verilerin, veri deposuna eklenme performansı artırılmazken verilerin tekrar edinilme işlemi hızlandırılmış olur. |
|
Posted: 09 Dec 2009 02:09 AM PST 21.7.2009 tarihinden itibaren Schwäbisch Hall (Almanya) şehrinde bulunan Kreditwerk firması için JEE danışman ve yazılım mimari (freelancer) olarak işe başladım. 40 iş günü olarak anlaşdığım proje’de bugün itibariyle 5,5 ayı doldurmuş bulunuyorum :) Bu son iki yılın projelerinde az rastlanan bir durum değil. Projeye 40 ya da 60 iş günü olarak alınırsınız ve performansınıza ve proje bütçesine göre bu süre uzayıp gidebilir. 2010 senesi için de 160 iş günü devam etmem isteniyor.
![]() Kreditwerk, Schwäbisch Hall isimli Alman bankasının BT işlerini yürüten bir yazılım ve altyapı firması. Bu firmanın büyük bir hissesi Schwäbisch Hall bankasına ait. Schwäbisch Hall bankası için kendi işlerini sahip olduğu BT firmalarına yaptırıyor diyebiliriz. Benim çalıştığım projenin ismi Wohnrente 2009. Alman hümümeti emeklilik sonrasında çalışanların ev alabilmesi için her sene 600 EUR’a kadar yardımda bulunuyor. Eğer 1 den fazla cocuğunuz varsa bu rakam binleri geçebiliyor, çünkü çocuk başına devlet 300 Eur yardımda bulunuyor. Alman hükümetinin amacı her çalışanın emeklilikten sonra bir ev sahibi olabilmesi ve emeklilik maaşının az oluşundan etkilenmemelerini sağlamak. Proje JEE tabanli ve EBJ3 teknolojisini kullanıyoruz. Bankanın IBM Host tabanlı sistemlerinde bulunan verilerin devletin Wohnrente projesinden sorumlu kurumuyla XML bazında değiş-tokuş edilmesi gerekiyor. Bu işlemler için JAXB teknolojisinin en uygun olacağını düşündüm ve sistemi bu şekilde implemente ettim. EJB3 komponentleri ve Host sistemleri arasındaki bağlantıyı Corba üzerinden gerçekleştiriyoruz. JAXB ile unmarshalling (XML den Java nesneleri oluşturmak) performansını artırmak için Stax teknolojisini kullanmayı tercih ettim. Stax, SAX gibi event bazlı bir teknoloji ve XML dosyalarının adım adım işlenmelerini mümkün kılmakta. XML dosyalarının içinde yüzlerce ya da binlerce veri olabilir. Eğer default olan DOM teknolojisini kullanırsanız, tüm XML dosyasının işlem öncesi hafızaya yüklenmesi gerekmektedir. Bu kısa bir zamanda OutOfMemory hatası doğurabilir. Hızlı transformasyon ve az hafıza kullanımını sağlamak için unmarshalling işlemini aşağıdaki şekilde gerçekleştiriyorum.
// Liste mit den MZahlungsanstoss Objekten
List<Zahlungsanstoss> list = new ArrayList<Zahlungsanstoss>();
XMLInputFactory xmlif = XMLInputFactory.newInstance();
FileReader fr = new FileReader(file);
XMLEventReader xmler = xmlif.createXMLEventReader(fr);
EventFilter filter = new EventFilter() {
public boolean accept(XMLEvent event) {
return event.isStartElement();
}
};
XMLEventReader xmlfer = xmlif.createFilteredReader(xmler, filter);
StartElement e = (StartElement) xmlfer.nextEvent();
JAXBContext ctx = JAXBContext.newInstance(JAXB_PACKAGE);
Unmarshaller um = ctx.createUnmarshaller();
while (xmlfer.peek() != null)
{
Object o = um.unmarshal(xmler);
if (o instanceof Zahlungsanstoss)
{
Zahlungsanstoss m = (Zahlungsanstoss)o;
list.add(m);
}
}
fr.close();
EJB3 komponentlerini tamamen test güdümlü (TDD – Test Driven Development) geliştiriyorum. EJB komponentleri POJO sınıfları olduklarından test güdümlü geliştirilmeleri oldukça kolay. Bu şekilde %85’in üzerinde test kapsama alanı oluşturabildim. Bu yazılım metriğini elde etmek için EclEmma aracını kullanıyorum. JAXB teknoloji kullanılarak nasıl Java mapping sınıflarının oluşturulduğunu daha önceki bir blog kaydımda sizinle paylaşmıştım. Java sınıflarını oluşturabilmek için (generated) XML DTD dosyalarından faydalandım. Geçen hafta üzerinde çalıştığım implementasyon bünyesinde bir XML Schema kullanmam gerekti. Bir XML Schema’dan Java nesneleri oluşturmak için aşağıdaki Ant hedefini (target) kullanıyorum:
<target name="ZusyAA01" description="Compile all Java source files">
<echo message="Compiling the schema..." />
<mkdir dir="gen-src" />
<xjc schema="${basedir}/etc/jaxb/zusyaa01/ZusyAA01.xsd" package="de.***.mapping" destdir="${basedir}/gen-src">
<produces dir="gen-src" includes="**/*.java" />
</xjc>
</target>
İlerleyen proje günlerinde üzerinde çalıştığım konular ve karşılaştığım sorunlar hakkında sizinle paylaşıma devam edeceğim.
|
|
Intercepting Filter Tasarım Şablonu Posted: 09 Dec 2009 01:23 AM PST Front Controller tasarım şablonunda, kullanıcıdan gelen isteklerin (request) merkezi bir yerde toplanarak, işlem yapıldığını daha önce görmüştük. Intercepting Filter tasarım şablonu ile, kullanıcının isteği (request) işleme alınmadan önce filtreler kullanılarak süzgeçten geçirilir. Örneğin bir filtre ile kullanıcının işlem öncesi login yaptığını konrol edebiliriz. Filtremiz, session (HttpServletSesion) içinde login bilgilerini bulamadığı taktirde, kullanıcıyı login sayfasına yönlendirebilir.
Aynı şekilde işlem tamamlandıktan sonra kullanıcıya gönderilecek cevap (response) filtreler yardımı ile modifike edilebilir. ![]() Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Reuse-Release Equivalence Principle (REP) – Tekrar Kullanım ve Sürüm Eşitliği Posted: 09 Dec 2009 01:03 AM PST Program modülleri paketler (packages) kullanılarak organize edilir. Paketler arasında sınıfların birbirlerini kullanmalarıyla bağımlılıklar oluşur. Bunun bir örneği resim 1 de yer almaktadır. Eğer paket B bünyesindeki bir sınıf paket A bünyesinde bulunan bir sınıf tarafından kullanılıyorsa, bu paket A’nin paket B’ye bağımlılığı olduğu anlamına gelir. Bu tür bağımlılıkların oluşması doğaldır. Amaç bu bağımlılıkları ortadan kaldırmak değil, kontrol edilebilir hale getirmek olmalıdır. Bu amaçla paket bazında uygulanabilecek tasarım prensipleri oluşturulmuştur. Bunlardan birisi Reuse-Release Equivalence (tekrar kullanım ve sürüm eşitliği) prensibidir.
![]() Bir proje bünyesinde değişik modüllerden oluşan bir yazılım sistemini implemente etmek için çalıştığımızı düşünelim. Her modülden ayrı bir ekip sorumlu olsun. Üzerinde çalıştığımız modülün implementasyonunu yapabilmek için büyük bir ihtimalle diğer modülleri kullanmamız gerekecektir. Örneğin bilgibankası üzerinde işlem yapmamızı sağlayacak bir modülü başka bir ekip implemente etmiş olabilir. O ekip bilgibankası üzerindeki işlemler için gerekli tüm sınıfları dao isimli bir paket içine yerleştirmiş olabilir. Bizim bu paket içindeki sınıfları tekrar kullanabilmemiz (reuse) için belirli şartların yerine gelmesi gerekmektedir. Bunlar:
Tekrar kullanımı kolaylaştırmak için paket sürümlerinin oluşturulması şarttır. REP’e göre tekrar kullanılabilirlik (reuse) sürüm (release) ile direk orantılıdır. Sürüm ne ihtiva ediyorsa, o tekrar kullanılabilir. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
JPA Anotasyonları ve Dinamik Tablo İsmi Posted: 25 Nov 2009 12:54 AM PST Projelerde komponent tabanlı çalışmaya özen gösteriyorum. Komponent olarak geliştirdiğim bir modülü, konfigürasyon değişikliği yaparak başka bir projede kullanabilmeliyim. Komponentler kodun tekrar kullanımını (reuse) ve programcının daha az kod yazmasını mümkün kılar.
Komponent bünyelerinde Hibernate kullanarak, komponent verilerini bilgibankasında sakliyorum. Şu an geliştirdiğim JugTR.org projesinde JPA anotasyonlarını kullandım. Aşağıda blog kayıtlarını temsil eden Blog Entity sınıfı yer alıyor. package bt.common.manager.blog.domain; import java.io.Serializable; import java.sql.Timestamp; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="blog") public class Blog implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; @Column(name = "subject", nullable = false) private String subject; @Column(name = "body", nullable = false) private String body; @Column(name = "userid", nullable = false) private Long user; @Column(name = "created", nullable = false) private Timestamp created; public Blog(long blogId) { this.id = blogId; } public Blog() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public Long getUser() { return user; } public void setUser(Long user) { this.user = user; } public Timestamp getCreated() { return created; } public void setCreated(Timestamp created) { this.created = created; } } @Table anotasyonunu kullanarak bilgibankası tablosunu belirliyorum. Örnekte görüldüğü gibi Blog nesnesinin ihtiva ettiği veriler blog isimli tabloda saklanmakta. Blog komponentini (Blog.java ve diger komponent sınıfları) daha önce başka projelerde de kullandım. Komponenti tekrar kullanmak (reuse) benim için kod üzerinde değişiklik yapmamak anlamına gelmektedir. Eğer blog verilerini jugtr_blog isimli tabloda tutmak istersem, Blog.java sınıfında yer alan @Table anotasyonunu değiştirmem gerekiyor. Bu durumda komponentin kodunu değiştirmiş olacağım. Bu da bir nevi tekrar kullanım, lakin bu durumda komponentin birden fazla versiyonu oluşmakta ve bunların bakımı zamanla güçleşmektedir. Benim hayalimde kod üzerinde değişiklik yapmadan Blog komponenentini istediğim projede kullanabilmek ve bilgibankası tablo ismini projeye göre değiştirebilmek var. Bu ne yazik ki JPA anotasyonlarını kullanarak mümkün değil. Eğer @Table anotasyonu ile tablo ismini belirlediyseniz, o zaman tablo ismini değiştirmek için sınıfı değiştirmeniz gerekiyor. Bu çoktan beri üzerinde çalıştığım bir sorun. Sorunu Hibernate bünyesinde bir interceptor sınıf oluşturarak çözebildim. Interceptor sınıfı aşağıdaki yapıya sahip:
package bt.jugtr.application.common.hibernate;
import org.hibernate.EmptyInterceptor;
public class JugTRHibernateInterceptor extends EmptyInterceptor
{
private static final long serialVersionUID = 1L;
@Override
public String onPrepareStatement(String sql)
{
String prepedStatement = super.onPrepareStatement(sql);
prepedStatement = prepedStatement.replaceAll("public.", "jugtr.");
return prepedStatement;
}
}
Bu interceptor sınıfını aktive etmek için SessionFactory konfigürasyonunu (Spring applicationContext.xml bünyesinde) aşağıdaki şekilde değiştiriyorum:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="entityInterceptor">
<bean class="bt.jugtr.application.common.hibernate.JugTRHibernateInterceptor" />
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect"> ${hibernate.dialect} </prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.connection.release_mode">after_transaction</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="annotatedClasses">
<list>
<value>bt.common.manager.user.domain.BTUser</value>
<value>bt.common.manager.user.domain.Authorities</value>
<value>bt.common.manager.blog.domain.Blog</value>
<value>bt.common.manager.blog.domain.BlogComment</value>
</list>
</property>
</bean>
Hibernate, oluşturduğu SQL komutunu bilgibankasına göndermeden önce JugTRHibernateInterceptor sınıfının onPrepareStatement() metodunu koşturur. Bu metot bünyesinde public. ile başlayan tablo isimlerini jugtr. olarak değiştiriyorum. public. ve jugtr. bilgibankasında kullandığım şemalar (schema). Hibernate tarafından oluşturulan SQL komutlarında public. isminin kullanımını @Table anotasyonuna schema değişkenini ekleyerek sağlayabilirim:
@Entity
@Table(name="blog", schema="public")
public class Blog implements Serializable
{
}
Bu değişikliğin ardından Hibernate tarafından oluşturulan bir SQL aşağıdaki şekilde olacaktır: /* from Blog order by id desc */ select top ? blog0_.id as id2_, blog0_.body as body2_, blog0_.created as created2_, blog0_.subject as subject2_, blog0_.userid as userid2_ from public.blog blog0_ order by blog0_.id desc Görüldüğü gibi Hibernate blog tablosunu public.blog seklinde adreslemeye başladı. Tekrar JugTRHibernateInterceptor sınıfına geri dönelim. onPrepareStatement() metodu bünyesinde SQL komutunda yer alan public. kelimesinin jugtr. olarak değiştirilmesini sağlıyorum. Bu şekilde dinamik olarak başka bir şemayı ve bu şemada bulunan blog tablosunu adreslemiş oluyorum. JugTRHibernateInterceptor devreye girdikten sonra SQL komutu aşağıdaki şekilde değişikliğe uğruyor. Bu şekilde başka bir şema içinde bulunan blog isimli tablo kullanımı mümkün oluyor. Görüldüğü gibi Blog.java kodunu değiştirmek zorunda kalmadan başka bir tabloyu kullanabildik. /* from Blog order by id desc */ select top ? blog0_.id as id2_, blog0_.body as body2_, blog0_.created as created2_, blog0_.subject as subject2_, blog0_.userid as userid2_ from jugtr.blog blog0_ order by blog0_.id desc
|
|
Apache ile Tomcat Arasında Reverse Proxy Oluşturma Posted: 22 Nov 2009 05:13 AM PST JugTR.org projesi Tomcat içinde deploy edilen bir Java 6 web aplikasyonu (Servlet 2.5 spec). Bu aplikasyona http://www.jugtr.org adresi üzerinden ulaşabilmek için Tomcat’in 80 numaralı port üzerinde çalışması gerekmektedir. Kullandığım server üzerinde 80 numaralı portta Apache çalışmakta. Bu durumda Tomcat’i 80 numaralı port üzerinde çalıştırmam mümkün değil. 80 haricinde herhangi bir port seçerek, JugTR.org aplikasyonunu deploy edebilirim, örneğin port 8181. Bu durumda aplikasyonun erişim adresi http://www.jugtr.org:8181 olacaktır.
Apache ile Tomcat arasında Reverse Proxy oluşturarak JugTR.org aplikasyonuna 80 numaralı port üzerinden ulaşabiliriz. Reverse Proxy kullanıldığında http://www.jugtr.org adresine gelen kullanıcı istekleri Apache tarafından http://127.0.0.1:8181 adresine yönlendirilir ve bu şekilde 8181 portu üzerinde faaliyet gösteren Tomcat’e 80 numaralı port üzerinden erişim sağlanmış olur. Apache, kullanıcı ile Tomcat içinde deploy edilmiş ve 8181 numaralı portta çalışan JugTR.org aplikasyonu arasında aracılık etmiş olur. Apache ile Tomcat arasında Reverse Proxy oluşturmak için öncelikle mod_proxy modülünü Apache httpd.conf dosyasina eklememiz gerekiyor. Ben Apache 2 versiyonunu kullanıyorum. Aşağıda httpd.conf dosyasında yer alması gereken konfigürasyon bulunmakta.
LoadModule proxy_module modules/mod_proxy.so [Proxy *] Order deny,allow Allow from all [/Proxy] NameVirtualHost 192.168.1.76 [VirtualHost 192.168.1.76] ServerName www.jugtr.org ServerAlias www.jugtr.org jugtr.org ServerAdmin in...@jugtr.org DocumentRoot /database/usr/local/apache/htdocs/ ProxyPass / http://127.0.0.1:8181/ [Location /] ProxyPassReverse / [/Location] [/VirtualHost] Yukarda görüldügü gibi VirtualHost direktifini kullanarak, jugtr.org icin yeni bir virtual domain oluşturuyorum. Bu sayede aynı Apache kurulumu üzerinde birden fazla domain ismini kullanmam mümkün. VirtualHost direktifini yakından incelediğimizde ProxyPass direktifi ile http://www.jugtr.org/ adresine gelen tüm kullanıcı isteklerinin (request) http://127.0.0.1:8181 adresine yönlendirildiğini görmekteyiz. ProxyPass direktifini kullanarak Apache ve Tomcat arasında Reverse Proxy oluşturmus oluyoruz. Tomcat kullanıcı isteklerine konfigüre edildigi IP ve port adresi ile cevap verir. Yukardaki örnekte kullanıcı http://127.0.0.1:8181 adresine yönlendirilecektir. Bu durumda Apache ile Tomcat arasındaki proxy çalışmamaktadır. Tomcat’in gönderdigi cevapların reverse proxy adresinde olmasını sağlamak için Tomcat’in server.xml konfigürasyon dosyası üzerinde değişiklik yapmamız gerekmektedir. Yapılması gereken değişiklik Connector elementi bünyesindedir. Connector port="1224" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" debug="0" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="UTF-8" proxyName="www.jugtr.org" proxyPort="80" Connector elementinde kullandığımız proxyName ve proxyPort atributlarıyla Tomcat’in gönderdiği cevapların http://www.jugtr.org şeklinde olmasını sagladık.
EOF ( End Of Fun )
Özcan Acar
|
|
Posted: 21 Nov 2009 05:25 AM PST Geçen hafta Belçika’da düzenlenen Devoxx konferansına katıldım. Java ile ilgilenenlerin mutlaka katılması gereken bir konferans. Bir hafta boyunca değişik konularda, konularında uzman şahısların sunum yaptıkları bu konferansta James Gosling, Robert C. Martin, Chris Richardson, Scott Ambler gibi ustaları dinleme ve onlarla sohbet etme fırsatı bulabiliyorsunuz.
Aşağıda yer alan fotografları Devoxx’da çektim.
Resim 1 de Agile Mythbuster konulu sunumu yapan Scott Ambler yer almakta. Çevik süreçlerde oluşan bazı efsaneleri tematize eden bu sunumda Scott Ambler bazı efsanelerin nasıl oluştuğunu ve nasıl çürütüldüğünü istatistikler üzerinde dinleyicilere aktardı. En çok hoşuma giden efsane ise, çevik süreçlerde sertifikasyonun çok önemli olduğu idi. Scott Ambler’e göre 3 sene doktora yapmış bir şahıs ile 2 günlük bir seminer ardından sertifika almış bir şahısı aynı kefeye koymak çok mantık dışı :) Ben de kendisiyle aynı fikri paylaşıyorum. Sertifika peşinde koşmaktansa, konuyu derinlemesine araştırıp, anlamak ve uygulamak çok daha faydalı olacaktır.
Resim 4 de Architecting Robust Applications for Amazon EC2 konulu sunumu yapan Chris Richardson yer almakta. Chris Richardson POJO in Action konulu kitabın yazarı. Bu kitap yazılımcı kariyerimde beni en çok etkileyen kitaplardan birisidir. Chris Richardson ustayı Devoxx da dinleme firsatı bulmak benim için büyük bir şerefdi.
Resim 5 de Java’nın babası James Gosling ve ben yer alıyorum.
Resim 6 Resim 6 da programcılar için hazırlanan anketlerden birisi yer almakta. Resim 6 da yer alan ankette, programcılara kullandıkları yapılandırma (build tool) aracı sorulmuş. Çoğunluğu maven ve ant yapılandırma aracı oluşturmakta. Benim son zamanlardaki favorim maven. Resim 7 Resim 7 de değişik ülkelerde faaliyet gösteren JUG (Java User Group) liderlerinin katıldığı toplantıdan bir resim yer almakta. Bu toplantıda JUG liderleri Java hakkındaki sorularını Java’nın babası James Gosling’e yönelttiler. Bu toplantıda bir çok JUG lideri ile tanışma firsatı buldum. Türkiye’de de bir JUG kurmanın zamanı geldi. Kısa bir zaman sonra JugTR.org adresinde Türkiye’nin ilk resmi JUG’u faaliyete geçecek. Bu konudakı çalışmalarım devam etmekte.
|
|
Posted: 17 Nov 2009 06:15 AM PST YAGNI = You Ain´t Gonna Need It = İhtiyacın Olmayan Birşeyi Oluşturma! Extreme Programming prensiplerinden birisi olan YAGNI, JUnit test karşılığı olmayan ve ihtiyaç duyulmayan program kodunun programcılar tarafından oluşturulmamaları gerektiğini ifade eder. Test güdümlü çalışıldığı taktirde YAGNI prensibi uygulanmış olur. Testlerin olmadığı yerde YAGNI vardır :)
|
|
Posted: 17 Nov 2009 05:58 AM PST KISS = Keep It Simple, Stupid (KISS) = Mümkün Olan En Basit Çözümü Seç! KISS prensibine göre bir programcı, mevcut bir sorunu çözerken mümkün olan en basit çözümü seçmelidir. En basit çözüm genelde en optimal çözümdür. Genelde programcılar bir sorunun en basit çözümünü basit ve yetersiz gördüklerinden daha karmaşık çözümler üretirler, ama bilmezler ki KISS prensibine bu sekilde ters düşmüş olurlar :)
|
|
Posted: 17 Nov 2009 05:20 AM PST DRY = Don’t Repeat Yourself = Kendini Tekrarlama! DRY prensibine göre programcının kodlama esnasında kod tekrarlarından (code duplication) sakınması gerekmektedir. Kodun kendini tekrarlaması (örneğin copy-paste metodu kullanılarak) yazılım sisteminin genelde bakımını ve geliştirilmesini zorlaştırır. Bunun önüne geçmek için azimle DRY prensibinin uygulanması gerekmektedir.
|
|
Posted: 17 Nov 2009 01:43 AM PST Herhangi bir araç kullanmadan UML sequence diagramı çizmek istiyorsanız, Websequencediagrams.com sitesini bir deneyin :)
|
|
Interface Segregation Principle (ISP) – Arayüz Ayırma Prensibi Posted: 17 Nov 2009 01:08 AM PST Birbiriyle ilişkili olmayan birçok metodu ihtiva eden büyük bir interface sınıf yerine, birbiriyle ilişkili (cohesive) metotların yer aldığı birden fazla interface sınıfı daha makbuldür.
![]() Resim 1 de yer alan Connector interface sınıfı bünyesinde JMS (Java Message Service) için gerekli iki metot bulunmaktadır: commit ve rollback. Bu metodlarin RMIConnector implementasyonunda implemente edilmeleri anlamsız olur. Büyük bir ihtimalle RMIConnector sınıfında bu metotların implementasyonu kod 1 de yer aldığı şekilde olacaktır.
package shop;
public class RMIConnector implements Connector
{
public void commit()
{
throw new RuntimeException("not implemented");
}
public void rollback()
{
throw new RuntimeException("not implemented");
}
}
Kod 1 de yer alan yapı LSP ile uyumlu değildir. Connector sınıfını kullananlar RuntimeException oluşabileceğini göz önünde bulundurmak zorunda bırakılmaktadırlar. ISP uygulandığı taktirde resim 1 de yer alan Connector interface sınıfını yok ederek, yerine sorumluluk alanları belli iki yeni interface sınıf oluşturabiliriz. Resim 2 de bu çözüm yer almaktadır. ![]() Programcı olarak bir interface sınıfa birden fazla sorumluluk yüklememek için, interface sınıfın sistemdeki görevini iyi anlamamız gerekmektedir. Bu sadece bir sorumluluk alanı olan bir interface sınıf oluşturmamızı kolaylaştıracaktır. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Test Güdümlü Yazılımın Tasarım Üzerindeki Etkileri Posted: 17 Nov 2009 12:37 AM PST Yazılımcı olarak çalıştığım projelerde geleneksel ve çevik yazılım süreçleri hakkında tecrübe edinme firsatı buldum. En son kitabım bir çevik süreç olan Extreme Programming hakkındadır. Edindiğim tecrübeler doğrultusunda çevik süreçlerin, klasik yazılım süreçlerine nazaran bakımı ve geliştirilmesi daha kolay yazılım sistemlerinin oluşturulmasında daha avantajlı olduğunu söyleyebilirim.
Bu yazımda sizelere test güdümlü yazılım sürecinin, yazılım tasarımı üzerindeki etkilerini bir örnek üzerinde aktarmak istiyorum. TDD ile birlikte oluşan tasarım, kendiliğinden oluşan birşey değildir. Testler şekil aldıkça, oluşturmak istediğimiz tasarımın modeli de gözümüzde canlanmaya başlar. Oluşturduğumuz testler, programın gelecekteki kullanıcılarını (client) simule ettiği için, programın nasıl kullanılacağını testler bünyesinde gözlemlemek kolaylaşmaktadır. Bu süreç, sınıfların ve metotların kullanıcı gözüyle (client) tasarlanmasını sağlar. Bu sayede basit ve kullanışlı API (Application Programming Interface)’ler oluşur. Test güdümlü yazılım tasarımı devamlı zorlar ve yetersiz kaldığı yerlerde refactoring yöntemleriyle yenilenmesini sağlar. Bu süreç sayesinde kendisini devamlı yenileyen ve yeni gereksinimlere cevap veren bir tasarım oluşur. Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Posted: 17 Nov 2009 12:31 AM PST Daha önceki bölümlerde Abstract Factory tasarım şablonu ile değişik nesne ailelerinden nasıl nesneler üretildiğini incelemiştik. Builder tasarım şablonu da Abstract Factory tasarım şablonunda oldugu gibi istenilen bir tipte nesne oluşturmak için kullanılır. İki tasarım şablonu arasındaki fark, Builder tasarım şablonunun kompleks yapıdaki bir nesneyi değişik parçaları bir araya getirerek oluşturmasında yatmaktadır. Birden fazla adım içeren nesne üretim sürecinde, değişik parçalar birleştirilir ve istenilen tipte nesne oluşturulur.
Diğer bölümlerde olduğu gibi bir örnek üzerinde bu tasarım şablonunu yakından inceliyelim. ![]() Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Posted: 30 Oct 2009 03:15 AM PDT Roterdam’da yapılan ikinci uluslararası SOA (Service Oriented Architecture) sempozyumunda SOA alanında faaliyet gösteren öncü yazılımcılar tarafından SOA manifestosu açıklandı.
SOA manifestosunun oluşturulma amacı, bu alanda son yıllarda edinilen tecrübelerin ışığında SOA’nın kullanım amaçlarını göstermek ve SOA’nın temelini oluşturan prensipleri tanımlamak. SOA manifestosu çevik süreçler için tanımlanmış olan manifesto örnek alınarak hazırlanmış.
|
|
Service Locator Tasarım Şablonu Posted: 30 Oct 2009 03:01 AM PDT Business Delegate örneğinde, Service Locator Tasarım şablonunun nasıl uygulandığını görmüştük. Service Locator, işletme (business) katmanında bulunan komponentlerin lokalizasyonu için kullanılır
![]() Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Business Delegate Tasarım Şablonu Posted: 30 Oct 2009 02:55 AM PDT Modern yazılım sistemleri birden fazla katmandan oluşur. Bu katmanlar her zaman aynı server üzerinde mevcut olmayabilir. Bu durumda bir katmandan diger katmana ulaşmak için remote call olarak isimlendirilen RMI operasyonları yapılır. Örneğin EJB teknolojisi ile hazırlanan komponentler birden fazla server üzerinde hizmet sunabilir. Bu komponentlere bağlanıp, işlem yapabilmek için RMI kullanılır. EJB sistemlerinde bazı işlemler bilgisayar ağı üzerinden erişim gerektirebileceği için, zamanla sistem performansı negatif etkilenebilir. Bunun yanısıra gösterim katmanında bulunan sınıflar direk EJB komponentler ile interaksiyona girdikleri taktirde, gösterim katmanı ile EJB’lerden oluşan İşletme (business) katmanı arasında sıkı bir bağ oluşur. EJB komponentler üzerinde yapılan değişiklikler gösterim katmanını etkiler. Bu bağı azaltmak ve RMI performansını artırmak için Business Delegate tasarım şablonu kullanılır. ![]() Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Dependency Inversion Principle (DIP) – Bağımlılıkların Tersine Çevrilmesi Prensibi Posted: 29 Oct 2009 02:23 AM PDT Bu prensibe göre somut sınıflara olan bağımlılıklar soyut sınıflar ve interface sınıflar kullanılarak ortadan kaldırılmalıdır, çünkü somut sınıflar sık sık değişikliğe uğrarlar ve bu sınıflara bağımlı olan sınıflarında yapısal değişikliğe uğramalarına sebep olurlar. ![]() Resim 1 de görülen yapı DIP prensibine ters düşmektedir, çünkü RemoteControl sınıfı somut bir sınıf olan TV sınıfına bağımlıdır. TV bünyesinde meydana gelen her değişiklik doğrudan RemoteControl sınıfını etkileyecektir. Ayrıca RemoteControl sınıfını TV sınıfı olmadan başka bir yerde kullanılması mümkün değildir. ![]() Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Liskov Substitution Principle (LSP) – Liskov’un Yerine Geçme Prensibi Posted: 29 Oct 2009 02:05 AM PDT Barbara Liskov tarafından geliştirilen bu prensip kısaca şöyle açıklanabilir: Alt sınıflardan oluşturulan nesneler üst sınıfların nesneleriyle yer değiştirdiklerinde aynı davranışı göstermek zorundadırlar.
LSP’ye göre herhangi bir sınıf kullanıcısı, bu sınıfın alt sınıfları kullanmak için özel bir efor sarf etmek zorunda kalmamalıdır. Onun bakış açısından üst sınıf ve alt sınıf arasında farklılık yoktur. Üst sınıf nesnelerinin kullanıldığı metotlar içinde alt sınıftan olan nesneler aynı davranışı sergilemek zorundadır, çünkü oluşturulan metotlar üst sınıf davranışları örnek alınarak programlanmıştır. Alt sınıflarda meydana gelen davranış değişiklikleri, bu metotların hatalı çalışmasına sebep verebilir. Özellikte bu metotlarda instanceof gibi nesnelerin tipleri arasında kıyaslama yapılmak zorunda kalındığı zaman, LSP prensibi çiğnenmiş olur ki, bu alt sınıfların varlığından haberdar olunduğu anlamına gelir. Kullanıcı sınıflar ideal durumda alt sınıfların varlığından haberdar bile olmamalıdır. ![]() Resim 1 de yer alan Client sınıfındaki print() metodunun nasıl LSP prensibine ters düştüğünü yakından inceleyelim. Bu metot A sınıfından olan nesneler üzerinde işlem yapmaktadır. A sınıfı B ve C sınıfları tarafından genişletilmiştir, yani A sınıfından olan bir parametre nesnesi aynı zamanda B ve C sınıfında da olabilir.
package shop;
public class Client
{
public void print(A a)
{
if(a instanceof B)
{
((B)a).printB();
}
else if(a instanceof C)
{
((C)a).printC();
}
}
public static void main(String[] args)
{
Client client = new Client();
B b = new B();
C c = new C();
client.print(b);
client.print(c);
}
}
Kod 1 Kod 1 de yer alan print() metodu LSP ile uyumlu değildir. Bu metodun A sınıfına bağımlılığı vardır ve bu sınıfın nesneleri üzerinde işlem yapacak şekilde implemente edilmiş olması gerekir. Lakin print() metodu instanceof komutuyla parametre olarak verilen nesnenin hangi tipte olduğunu tespit etmeye çalışmakta ve tipe bağımlı olarak nesne üzerinde işlemi gerçekleştirmektedir. Bu durum hem OCP’ye (Open Closed Principle) hemde LSP’ye ters düşmektedir. OCP uyumlu değildir, çünkü print() metodu A’nin her yeni alt sınıfı için değişikliğe uğramak zorundadır. LSP’ye uyumlu değildir, çünkü B ya da C sınıfından olan nesneler A sınıfından olan bir nesnenin yerini alamadığı için print() metodu instanceof ile nesnenin tipini tespit etmek zorunda bırakılmaktadır. Buradan genel bir sonuç çıkartabiliriz: LSP’ye ters düşen bir implementasyon aynı zamanda OCP’ye de ters düşer. ![]() LSP’nin nasıl uygulanabileceğini diğer bir örnekte görelim. Resim 2 de bir firma çalışanları Personel sınıfını genişleten MaasliPersonel ve ParttimePersonel sınıfları ile temsil edilmektedirler. Personel sınıfı soyuttur (abstract). Çalışanların maaşlarının ödenebilmesi için Personel sınıfında bulunan soyut odeme() metodunun alt sınıflarca implemente edilmesi gerekmektedir. Firmaya yeni stajyerler alındığı için, modelin resim 3 deki gibi adapte edildiğini farz edelim. ![]() Staj yapan elemanlar için maaş ödenmez, bu yüzden odeme() metodu StajyerPersonel sınıfında boş implemente edilmesi gerekir. İmplementasyon şu şekilde olabilir:
public int odeme()
{
return 0;
}
Kod 2 Sıfır değerine geri vererek, odeme() metodunun geçerli ve kullanılabilir bir metot olduğunu ifade etmiş oluyoruz. Bu yanlıştır! Bu metodun StajyerPersonel sınıfında implemente edilmemesi gerekir, çünkü staj yapanlara maaş ödenmez. Bu sorunu kod 3 de yer aldığı gibi bir Exception oluşturarak çözebiliriz. Eğer bir stajyer için odeme() metodu kullanılacak olursa, oluşan PersonelException, bir stajyer için maaş ödenemeyeceğini metot kullanıcısına bildirir. Exception nesneleri olağan olmayan durumları ifade etmek için kullanılır.
public int odeme()throws PersonelException
{
throw new PersonelException("Stajyer maasli calismaz!");
}
Kod 3 Bu implementasyon ilk örnekte olduğu gibi LSP ile uyumlu değildir. Neden uyumlu olmadığını inceleyelim. Kod 4 de çalışanlara ödenen toplam maaş miktarı hesaplanmaktadır.
List<Personel> personel = getPersonelList();
int total = 0;
for(int i=0; i < personel.size(); i++)
{
total+=personel.get(i).odeme();
}
Kod 4 StajyerPersonal sınıfının sisteme eklenmesiyle kod 4 de yer alan kodun değiştirilmesi gerekmektedir. Ya try/catch bloğu kullanılarak oluşabilecek bir PersonelException’in işleme tabi tutulması gerekir ya da metot signatüründe throws kullanılarak PersonelException’in bir üst katmana iletilmesi gerekir. StajyerPersonel ismini taşıyan yeni bir sınıf oluştuğu için, mevcut Personel sınıfı kullanıcıları (client) yapısal değişikliğe uğramıştır.
try
{
List<Personel> personel = getPersonelList();
int total = 0;
for(int i=0; i < personel.size(); i++)
{
total+=personel.get(i).odeme();
}
}
catch (Exception e)
{
// handle exception
}
Kod 5
List<Personel> personel = getPersonelList();
int total = 0;
for(int i=0; i < personel.size(); i++)
{
if(! (personel.get(i) instanceof StajyerPersonal))
{
total+=personel.get(i).odeme();
}
}
Kod 6 Try/catch blokları komplike yapılardır ve kodun okunulabilirliğini azaltırlar. Try/catch bloğundan kurtulmak için kod 6 de yer alan implementasyon düşünülebilir. Burada instanceof ile sırada hangi tip bir nesnenin olduğunu tespit edebilir ve StajyerPersonel tipi nesneleri işlem dışı bırakabiliriz. Daha öncede gördüğümüz gibi bu implementasyon LSP ile uyumlu değildir, çünkü üst sınıf ile çalışan bir metot instanceof ile alt sınıfları tanımak zorunda bırakılmaktadır. Bunun tek sebebi StajyerPersonel sınıfından olan bir nesnenin, Personel sınıfından bir nesne ile yer değiştiremez durumda olmasıdır. Personel sınıfını kullanan diğer sınıflar alt sınıf olan StajyerPersonel sınıfı sisteme eklendikten sonra bu değişiklikten etkilenmiştir. LSP’ye göre bu olmaması gereken bir durumdur. Alt sınıfların nesneleri, üst sınıflardan olan nesnelerin kullanıldığı metotlar içinde üst sınıf nesneleriyle aynı davranışı göstermek zorundadırlar, aksi taktirde kullanıcı sınıflar bu durumdan etkilenirler. Sorun staj yapan bir elemanın Personel sınıf hiyerarşisinde yer alan bir sınıf aracılığıyla modellenmiş olmasında yatmaktadır. Staj yapan bir eleman maaş almadığı için personele ait değildir, bu yüzden StajyerPersonel sınıfının Personel sınıfını genişletmesi doğru değildir. Sorunu çözmek ve LSP konform hale gelmek için StajyerPersonel sınıfının Personel sınıf hiyerarşisinden ayrılması gerekmektedir. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Open Closed Principle (OCP) – Açık Kapalı Tasarım Prensibi Posted: 16 Oct 2009 01:38 AM PDT Yazılım disiplininde değişmeyen birşey varsa o da değişikliğin kendisidir. Birçok program müşteri gereksinimleri doğrultusunda ilk sürümden sonra değişikliğe uğrar. Bu doğal bir süreçtir ve müşteri programı kullandıkça ya yeni gereksinimlerini ya da mevcut fonksiyonlar üzerinde adaptasyonları gerekçe göstererek programın değiştirilmesini talep edecektir.
Tanınmış yazılım ustalarından Ivar Jacobson bu konuda şöyle bir açıklamada bulunmuştur: “All systems change during their life cycles. This must be born in mind when developing systems are excepted to last longer than the first version.” Şu şekilde tercüme edilebilir: “Her program görev süresince değişikliğe uğrar. Bu ilk sürümden ötesi düşünülen programların yazılımında göz önünde bulundurulmalıdır.” Değişiklik kaçınılmaz olduğuna göre, bir programı gelecekte yapılması gereken tüm değişiklikleri göz önünde bulundurarak, şimdiden buna hazır bir şekilde geliştirmek mümkün müdür? Öncelikle şunu belirtelim ki gelecekte meydana gelecek değişikliklerin hepsini kestirmemiz mümkün değildir. Ayrıca çevik süreçlerde ilerde belki kullanılabileceğini düşündüğümüz fonksiyonların implementasyonu kesinlikle tabudur (istenilmeyen bir durum anlamında). Bu durum bizi programcı olarak gelecekteki değişiklikleri göz önünde bulundurarak, bir nevi hazırlık yapmamızı engeller. Çevik süreç sadece müşteri tarafından dile getirilmiş, kullanıcı hikayesi haline dönüştürülmüş ve müşteri tarafından öncelik sırası belirlenmiş gereksinimleri göz önünde bulundurur ve implemente eder. Kısacası çevik süreç geleceği düşünmez ve şimdi kendisinden beklenenleri yerine getirir. Böylece müşteri tarafından kabul görmeyecek bir sistemin oluşması engellenmiş olur. Çevik süreç büyük çapta bir tasarım hazırlığı ile start almaz. Daha ziyade mümkün olan en basit şekilde yazılıma başlanır. Her iterasyon başlangıcında implemente edilmesi gereken kullanıcı hikayeleri seçildikten sonra, mevcut yapının yeni gereksinimlere cevap verip, veremeyeceği incelenir. Büyük bir ihtimalle mevcut yapı yeni kullanıcı hikayelerinin implementasyonu için yeterli olmayacaktır. Bu durumda programcı ekip refactoring yöntemleriyle programın yapısını yeni gereksinimleri kabul edecek şekilde modifike eder. Bu esnada tasarım yapısal değişikliğe uğrar. Bu gerekli bir işlemdir ve yapılmak zorundadır, aksi taktirde bir sonraki iterasyon beraberinde getirdiği değişikliklerle programcı ekibini yazılım esnasında zorlayacaktır. Çevik süreç gelecekte olabilecek değişikleri göz önünde bulundurmadığına göre, programı bu değişikliklerin olumsuz yan etkilerine karşı nasıl koruyabiliriz? Bunun yolu her zaman olduğu gibi yazılım esnasında uygun tasarım prensiplerini uygulamaktan geçmektedir. Eğer çevik süreç bize gelecekte olabilecek değişikliklere karşı destek sağlamıyorsa, bizimde tedbir alarak çevik süreci, çevik prensiplere ters düşmeden takviye etmemiz gerekiyor. Çevik süreci, çevik tasarım prensiplerini kullanarak istediğimiz bir yapıda, bakımı ve geliştirilmesi kolay program yazılımına destek verecek şekilde takviye edebiliriz. Tekrar bölüm başında sorduğum soruya dönelim ve sorunun cevabını bulmaya çalışalım. Şu şekilde bir soru sormuştum: “Değişiklik kaçınılmaz olduğuna göre, bir programı gelecekte yapılması gereken tüm değişiklikleri göz önünde bulundurarak, şimdiden buna hazır bir şekilde geliştirmek mümkün müdür?” Bu mümkün değil demiştik. Lakin esnek bir tasarım oluşturarak önü açık ve gelecek korkusu olmayan bir program yapısı oluşturabiliriz. Bunu gerçekleştirmek için kullanabileceğimiz prensiplerin başında Open Closed – Açık Kapalı prensibi (OCP) gelmektedir. Bertrand Meyer tarafından geliştirilen bu prensip kısaca şöyle açıklanabilir: “Programlar geliştirilmeye açık ama değiştirilmeye kapalı olmalıdır.” Programı geliştirmek, programa yeni bir davranış biçimi eklemek anlamına gelmektedir. OCP ye göre programlar geliştirmeye açık olmalıdır, yani programı oluşturan modüller yeni davranış biçimlerini sergileyecek şekilde genişletilebilmelidirler. Bir modüle yeni bir davranış biçimi kazandırılarak düşünülen değişiklik sağlanır. Bu yeni kod yazılarak gerçekleştirilir ( bu yüzden bu işleme değiştirme değil, genişletme denir), mevcut kodu değiştirerek değil! Eğer kendinizi bir müşteri gereksinimini mevcut kod üzerinde değişiklik yaparken bulursanız, biliniz ki OCP prensibine ters düşüyorsunuz. Kod üzerinde yapılan değişiklik, bir sonraki gereksinimlerinde ayni şekilde implemente edilmesini zorunlu kılacaktır. Bu durum, kodun zaman içinde içinden çıkılmaz ve çok karmaşık bir yapıya dönüşmesini çabuklaştırır. OCP prensibinin nasıl uygulanabileceğini bir önceki bölümde yer alan RemoteControl – TV örneği üzerinde inceleyelim. ![]() Resim 1 de görüldüğü gibi RemoteControl sınıfı TV sınıfını kullanarak işlevini yerine getirmektedir. Eğer RemoteControl sınıfını TV haricinde başka bir aleti kontrol etmek için kullanmak istersek, örneğin CDPlayer Resim 2 deki gibi değişiklik yapmamız gerekebilir. Bu noktada esnek bağ prensibini unutarak, resim 2 de yer alan çözümün bizim için yeterli olduğunu düşünelim. ![]()
package org.cevikjava.design.ocp;
/**
* TV ve CDPlayer sınıflarından
* nesneleri kontrol eder.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on(Object obj)
{
if(obj instanceof TV)
{
((TV)obj).tvOn();
}
else if(obj instanceof CDPlayer)
{
((CDPlayer)obj).cdOn();
}
}
/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off(Object obj)
{
if(obj instanceof TV)
{
((TV)obj).tvOff();
}
else if(obj instanceof CDPlayer)
{
((CDPlayer)obj).cdOff();
}
}
}
Bu şekilde oluşturulan bir tasarım OCP prensibine ters düşmektedir, çünkü her yeni eklenen alet için on() ve off() metotlarında değişiklik yapmamız gerekmektedir. OCP böyle bir modifikasyonu kabul etmez. OCP ye göre mevcut çalışır kod kesinlikle değiştirilmemelidir. Onlarca aletin bulunduğu bir sistemde on() ve off() metotlarının ne kadar kontrol edilemez ve bakımı zor bir yapıya bürüneceği çok net olarak bu örnekte görülmektedir. Bunun yanı sıra RemoteControl sınıfı TV ve CDPlayer gibi sınıflara bağımlı kalacak ve başka bir alanda kullanılması mümkün olmayacaktır. TV ve CDPlayer sınıflar üzerinde yapılan tüm değişiklikler RemoteControl sınıfını doğrudan etkileyecek ve yapısal değişikliğe sebep olacaktır. ![]() Resim 3 de yer alan çözüm OCP ye uygun yapıdadır, çünkü kod üzerinde değişiklik yapmadan programa yeni davranışlar eklemek mümkündür. OCP prensibi, esnek bağ prensibi kullanılarak uygulanabilir. OCP ye uygun RemoteControl sınıfının yapısı şu şekilde olmalıdır:
package org.cevikjava.design.loosecoupling.design;
/**
* RemotControlInterface sınıfını
* implemente eden sınıfları
* kontrol edebilen sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Delegasyon islemi için RemoteControlInterface
* tipinde bir sınıf degiskeni tanimliyoruz.
* Tüm islemler bu nesnenin metodlarina
* delege edilir.
*/
private RemoteControlInterface remote;
/**
* Sinif konstuktörü. Bir nesne oluşturma islemi
* esnasında kullanilacak RemoteControlInterface
* implementasyonu parametre olarak verilir.
*
* @param _remote RemoteControlInterface
*/
public RemoteControl(RemoteControlInterface _remote)
{
this.remote = _remote;
}
/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on()
{
remote.on();
}
/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off()
{
remote.off();
}
}
on() ve off() metotları sadece RemoteControlInterface tipinde olan bir sınıf değişkeni üzerinde işlem yapmaktadır. Bu sayede if/else yapısı kullanmadan RemoteControlInterface sınıfını implemente etmiş herhangi bir alet üzerinde gerekli işlem yapılabilmektedir. Bu örnekte on() ve off() metotları değişikliğe kapalı ve tüm program geliştirmeye açıktır, çünkü RemoteControlInterface interface sınıfını implemente ederek sisteme yeni aletleri eklemek mümkündür. Sisteme eklediğimiz her alet için on() ve off() metotları üzerinde değişiklik yapmak zorunluluğu ortadan kalkmaktadır. Uygulanan OCP ve esnek bağ prensibi ile RemoteControl başka bir alanda her tip aleti kontrol edebilecek şekilde kullanılır hale gelmiştir. Stratejik Kapama (Strategic Closure)Ne yazık ki bir yazılım sistemini OCP prensibini uygulayarak %100 değişikliklere karşı korumamız imkansızdır. OCP ye uygun olan bir metot müşterinin yeni istekleri doğrultusunda OCP’ye uygun olmayan bir hale gelebilir. Programcı metodu ilk implemente ettiği zaman, gelecekte olabilecek değişiklikler hakkında fikir sahibi olmayabilir. Metot OCP uyumlu implemente edilmiş olsa bile, bu metodun her zaman OCP uyumlu kalabileceği anlamına gelmez. Eğer kapama tam sağlanamıyorsa, kapamanın stratejik olarak implemente edilmesi gerekir. Programcı her zaman ne gibi değişikliklerin olabileceğini kestiremeyebilir. Bu durumda konu hakkında araştırma yaparak, oluşabilecek değişiklikleri tespit edebilir. Eğer olabilecek değişikliklerin tespiti mümkün değilse, beklenen değişiklikler meydana gelene kadar beklenir ve implementasyon yeni değişiklikleri de yansıtacak şekilde OCP uyumlu hale getirilir. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Loose Coupling (LC) – Esnek Bağ Tasarım Prensibi Posted: 16 Oct 2009 01:07 AM PDT Bir program bünyesinde, tanımlanan görevlerin yerine getirilebilmesi için birden fazla nesne görev alır. Bu nesneler birbirlerinin sundukları hizmetlerden faydalanarak kendi görevlerini yerine getirirler. Bu durumda nesneler arası bağımlılıklar oluşur. Bir nesne kullandığı diğer bir nesne hakkında ne kadar fazla detay bilgiye sahip ise, o nesneye olan bağımlılığı o oranda artar. Oluşan her bağımlılık bir sınıf için dolaylı olarak yapısal değiştirilme rizikosunu artırır, çünkü bağımlı olduğu sınıf üzerinde yapılan her değişiklik kendi yapısında değişikliğe neden olacaktır. Bu durum programın genel olarak kırılgan bir hale gelmesini kolaylaştıracaktır.
Buradan “Eğer bağımlılık varsa, sorun var, bu yüzden bağımlılıkların ortadan kaldırılması gerekmektedir” sonucunu çıkartabiliriz. Nesneye yönelik tarzda tasarlanmış edilmiş bir program içinde bağımlılıkları ortadan kaldırmak imkansızdır, çünkü nesnelerin olduğu yerde interaksiyon ve bağımlılık olmak zorundadır. Bağımlılıkları ortadan kaldıramıyorsak, o zaman onları kontrol altına almamız işimizi kolaylaştıracaktır. Eğer bana soracak olursanız, yazılım disiplininin özü de burada saklıdır: Yazılımcı olarak kullandığımız tüm metotların temelinde bağımlılıkların kontrolü ve yönetilmesi yatmaktadır. İyi bir tasarım oluşturmak için sarf ettiğimiz efor, bağımlılıklara hükmetmek isteyişimizden kaynaklanmaktadır, yani iyi bir tasarım , kontrol edilebilir bağımlılıkları beraberinde getirdiği için iyidir, kendimizi programcı olarak iyi hissetmemizi sağladığı için değil! Biliyoruz ki kötü bir tasarım nesneler arası yüksek derecede bağımlılığa sebep vereceği için programcı olarak hayatımızı zorlaştıracaktır. Bu yüzden sahip olduğumuz tüm teknik yeteneklerimizle bağımlılığa karşı bir savaş veririz. Onu yenmemiz mümkün olmasa da, bize zarar vermeyecek şekilde kontrol altına almamız mümkündür. Anladığımız kadarıyla bağımlılıkları mantıklı bir çerçevede ortadan kaldırmamız imkansız! Peki bağımlılıkları nasıl kontrol altına alabiliriz? Esnek bağımlılıklar oluşturarak! Esnek bağımlılık oluşturmak demek, nesneler arası bağların oluşmasına izin vermek, ama sınıflar üzerinde yapılan yapısal değişikliklerin bağımlı sınıflar üzerinde yapısal değişikliğe sebep vermesini engellemek demektir. Bunu bir örnek vererek açıklayalım. ![]() RemoteControl (tv uzaktan kumanda aleti) ve TV (televizyon) sınıflarının arasında tek yönlü (uni directional) bir bağ oluşmuştur, çünkü bir RemoteControl nesnesi görevini yerine getirebilmek için bir TV nesnesine ihtiyaç duymaktadır. Nesneler arası bağımlılıkların yönü vardır. Resim 1 de bu bağımlılığın yönünün RemoteControl sınıfından TV sınıfına doğru olduğunu görmekteyiz. Bu tek yönlü bir bağdır ve RemoteControl sınıfı bünyesinde TV tipinde bir sınıf değişkeni barındırarak bağı oluşturur. Sınıflar arası bağlar karşılıklı da (bi directional) olabilir. İki taraflı bağlarda sınıflar, bir sınıf değişkeni aracılığıyla karşılıklı olarak birbirlerine işaret ederler. RemoteControl nesnesi bir TV nesnesi olmadığı sürece işe yaramaz. Bu yüzden bu iki sınıf arasında direk bağlantı oluşturulmuştur. Böyle bir bağımlılık aşağıdaki sorunların oluşmasına sebep vermektedir:
package org.cevikjava.design.loosecoupling;
/**
* Bir televizyonu uzaktan kuman etme
* aletini simule eden sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Kontrol edilen televizyon
*/
private TV tv = new TV();
/**
* Televizyonu acmak
* için kullanilan metot.
*
*/
public void tvOn()
{
tv.on();
}
/**
* Televizyonu kapatmak
* için kullanilan metot.
*/
public void tvOff()
{
tv.off();
}
}
RemoteControl sınıfı bünyesinde TV tipinde bir sınıf değişkeni (tv) barındırdığı için kendisini TV sınıfına bağımlı kılar.
package org.cevikjava.design.loosecoupling;
/**
* Bir televizyonu simule eden sınıf.
*
* @author Oezcan Acar
*
*/
public class TV
{
/**
* Televizyonu acmak için
* kullanilan metot.
*
*/
public void on()
{
System.out.println("TV acildi.");
}
/**
* Televizyonu kapatmak için
* kullanilan metot.
*
*/
public void off()
{
System.out.println("TV kapandi");
}
}
![]() Nesneler arası kuvvetli bağların, programın bakımı, geliştirilmesi ve kodun tekrar kullanımını negatif yönde etkilediğini gördük. Bu sorunu ortadan kaldırabilmek için sınıfların ilişkileri üzerinde yapısal değişikliğe gitmemiz gerekiyor. Esnek bağ oluşturabilmek için Abstract (soyut) ya da Interface sınıflarından faydalanabiliriz. Resim 2 de görüldüğü gibi RemoteControlInterface ismini taşıyan bir Interface sınıf ile RemoteControl ve bu sınıfın kontrol etmek istediği aletler arasında esnek bir bağ oluşturuyoruz. Böyle bir yapının esnekliği nereden gelmektedir, bunu yakından inceleyelim.
package org.cevikjava.design.loosecoupling.design;
/**
* RemoteControlInterface sınıfı
*
* @author Oezcan Acar
*
*/
public interface RemoteControlInterface
{
/**
* Bu sınıfı implement eden
* bir aleti acmak için
* kullanilan metot.
*
*/
void on();
/**
* Bu sınıfı implement eden
* bir aleti kapatmak için
* kullanilan metot.
*
*/
void off();
}
Interface sınıflar bünyesinde sadece metot gövdeleri tanımlanır. Alt sınıflar kullanılarak interface sınıfında tanımlanmış olan metotlar implemente edilir. Bu sayede interface sınıfında tanımlanmış metotlar için değişik sınıflarda değişik tarzda implementasyonlar yapmak mümkündür. Herhangi bir sınıf implements direktifini kullanarak bir interface sınıfını implemente edebilir. Interface sınıfında tanımlanmış olan tüm metotların alt sınıflarca implemente edilmesi gerekmektedir. Resim 2 de görüldüğü gibi RemoteControl sınıfı direk TV sınıfı kullanmak yerine, RemoteControlInterface ismini taşıyan interface sınıfı ile beraber çalışmaktadır. Böyle bir yapılanma ile RemoteControl ve bu sınıfın kullanmak istediği somut sınıflar (örneğin TV) arasına bir set çekmiş olduk. RemoteControl sınıfı bu setin arkasında yer alan sınıfları, bunlar RemoteControlInterface sınıfını implemente eden sınıflardır, tanımamaktadır ve tanımak zorunda değildir. RemoteControl sınıfının tanıması gereken tek sınıf RemoteControlInterface sınıfı ve bu interface sınıfının dış dünyaya sunduğu metotlardır. RemoteControl sınıfı böylece dolaylı olarak, RemoteControlInterface sınıfını implemente eden her sınıfı kullanabilir hale gelmektedir. Böylece RemoteControl sınıfının somut sınıflara olan bağımlılığı bir interface sınıf kullanılarak ortadan kaldırılmıştır.
package org.cevikjava.design.loosecoupling.design;
/**
* RemotControlInterface sınıfını
* implemente eden sınıfları
* kontrol edebilen sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Delegasyon islemi için RemoteControlInterface
* tipinde bir sınıf degiskeni tanimliyoruz.
* Tüm islemler bu nesnenin metodlarina
* delege edilir.
*/
private RemoteControlInterface remote;
/**
* Sinif konstuktörü. Bir nesne oluşturma islemi
* esnasında kullanilacak RemoteControlInterface
* implementasyonu parametre olarak verilir.
*
* @param _remote RemoteControlInterface
*/
public RemoteControl(RemoteControlInterface _remote)
{
this.remote = _remote;
}
/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on()
{
remote.on();
}
/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off()
{
remote.off();
}
}
RemoteControl sınıfını RemoteControlInterface sınıfını kullanacak şekilde değiştiriyoruz. Bu amaçla RemoteControl sınıfında RemoteControlInterface tipinde bir sınıf değişkeni (remote) tanımlıyoruz. Sınıf konstruktörü RemoteControlInterface tipinde bir parametre kabul etmektedir. Böylece RemoteControl sınıfından bir nesne oluştururken, istediğimiz tipte bir RemoteControlInterface implementasyon sınıfı kullanabiliriz. RemoteControl sınıfı on() ve off() isminde iki metot tanımlamaktadır. Bu metotlar RemoteControlInterface sınıfında yer alan on() ve off() metotları ile karıştırılmamalıdır. RemoteControl sınıfı sahip olduğu metotlara ac() ve kapat() isimlerini de verebilirdi. Bu metotlar içinde delegasyon yöntemiyle sınıf değişkeni olan remote nesnesi kullanılmaktadır. Bu sayede kullanılan RemoteControlInterface implementasyon sınıfının (örneğin TV) on() ve off() metotları devreye girecektir.
package org.cevikjava.design.loosecoupling.design;
/**
* Bir televizyonu simule eden sınıf.
* RemoteControlInterface sınıfını
* implemente ederek bir RemoteControlInterface
* haline gelir.
*
* @author Oezcan Acar
*
*/
public class TV implements RemoteControlInterface
{
/**
* Televizyonu acmak için
* kullanilan metot.
*
*/
public void on()
{
System.out.println("TV acildi.");
}
/**
* Televizyonu kapatmak için
* kullanilan metot.
*
*/
public void off()
{
System.out.println("TV kapandi");
}
}
TV sınıfı RemoteControlInterface sınıfını implemente etmektedir. Bu sebepten dolayı RemoteControlInterface sınıfında tanımlanmış olan on() ve off() metotlarına sahiptir. TV sınıfı RemoteControlInterface sınıfını implemente etmediği sürece RemoteControl sınıfı tarafından kullanılamaz.
package org.cevikjava.design.loosecoupling.design;
/**
* Test sınıfı
*
* @author Oezcan Acar
*
*/
public class Test
{
public static void main(String[] args)
{
RemoteControlInterface rci = new TV();
RemoteControl control = new RemoteControl(rci);
control.on();
control.off();
}
}
Test.main() bünyesinde ilk önce kullanmak istediğimiz alet nesnesini, (TV) ve akabinde RemoteControl nesnesini oluşturuyoruz. RemoteControl konstruktör parametresi olarak bir satır önce oluşturduğumuz TV nesnesini almaktadır. RemoteControl bünyesinde yer alan on() ve off() metotları ile televizyonu açıp, kapatabiliriz. Ekran çıktısı şu şekilde olacaktır:
RemoteControl sınıfının konstruktörü RemoteControlInterface sınıfını implemente eden her sınıfı kabul ettiği için istediğimiz herhangi bir aleti RemoteControl sınıfı ile kontrol edebilir hale geliyoruz. Oluşturduğumuz yeni tasarımın bize sağladığı avantajlar şöyledir:
İncelediğimiz örneklerde nesneler arası bağın ortadan kaldırılamayacağını ama esnek bağ oluşturma prensibini uygulayarak kontrol edilebilir bir hale getirilebileceklerini gördük. Esnek bağlar oluşturabilmek için Interface yada soyut sınıflardan yararlanabiliriz. Usta yazılımcılar tasarım prensiplerine ve tasarım şablonlarına (design pattern) hakim olup,onları doğru yerde kullanmasını bilirler. Eğer şimdiye kadar tasarım prensipleri hakkında bir çalışmanız olmadıysa, sizin için yazılım disiplininde bir üst boyutun kapısını aralamış olduk. Tasarım prensiplerini uygulayarak ve tasarım şablonlarını kullanarak konseptüel daha yüksek seviyede çalışabilirsiniz. Bu bölümde yer alan tasarım prensipleri yazılım sürecine olan bakış açınızı tamamen değiştirecek niteliktedir. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Single Responsibility Principle (SRP) – Tek Sorumluk Tasarım Prensibi Posted: 14 Oct 2009 05:48 AM PDT Resim 1 deki gibi bir sınıfa daha önce bir yerlerde rastlamışsınızdır. Bu sınıf kendisini bilgibankasına ekleme, silme, müşteriye email gönderme, login yapma (shop sistemi olabilir) ve sipariş oluşturma işlemlerini yapabilmektedir.
Büyük bir ihtimalle bu sınıfı programlayan programcı kendisi ile gurur duyuyor olmalıdır. Aslında böyle bir sınıfın ve programcının küçümsenecek hiçbir tarafı yok. Bu sınıf büyük emek harcanarak oluşturulmuş olabilir, çünkü ihtiva ettiği metotlar basit değildir. Lakin bu sınıf saatli bir bombadır, çünkü içinde bulunduğu ortamdaki her değişiklik, sınıfın yapısal değişikliğe uğramasına sebep olabilir. Bunun yanı sıra Customer sınıfında implemente edilen kod tekrar kullanılmak istendiğinde, Customer sınıfının bağımlı olduğu sınıf ve API’lerin bu sınıfla beraber kullanılma zorunluluğu vardır, yani Customer sınıfı gittiği yere beraberinde kullandığı diğer sınıflar ve API’leri götürür. Bu kodun tekrar kullanımını sağlamak için istenmeyen bir durumdur. Customer sınıfını test edilebilir ve bakımı kolay bir hale getirmek için, sahip olduğu sorumlulukların başka sınıflara yüklenmesi gerekmektedir. Bunun bir örneğini resim 2 de görmekteyiz.
Resim 2 de yer alan Customer sınıfı sahip olduğu sorumlulukların başka sınıflara yüklenmesiyle hafiflemiş ve değişikliklere karşı daha dayanıklı bir hale gelmiştir. Bunun yanı sıra bu sınıfın test edilebilirliği yükselmiştir. Bu ve diğer sınıfların değişmek için artık sadece bir sebebi vardır. Bunun sebebi sadece bir sorumluluk sahibi olmalarında yatmaktadır. Bir sınıfın sorumluluğunu sadece bir sebepten dolayı değişebilir olması olarak tanımlayabiliriz. Eğer bir sınıfın değişikliğe uğraması için birden fazla sebep varsa, bu sınıf birden fazla sorumluluk taşıyor demektir. Bu durumda sınıfta sadece bir sorumluluk kalacak şekilde, sorumlukların diğer sınıflarla paylaşılması yoluna gidilmelidir. Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.
|
|
Chain of Responsibility Tasarım Şablonu Posted: 09 Oct 2009 04:01 AM PDT Chain of responsibility sorumluluk zinciri anlamına gelmektedir. Sisteme gönderilen bir istediğin (komut) hangi nesne tarafından cevaplanması gerektiğini bilmediğimiz durumlarda ya da isteği yapan nesne ve servis sağlayan nesne arasında sıkı bir bağ oluşmasını engellememiz gerektiğinde Chain of Responsibility tasarım şablonu kullanılır. Bu tasarım şablonunda servis sağlayan ilgili tüm nesneler bir kolye üzerindeki boncuklar gibi birbirleriyle ilişkili hale getirilir. Bir nesne zincirdeki kendinden sonraki nesneyi tanır ve isteği kendi cevaplayamadığı durumda, kendinden sonraki nesneye iletir. Bu işlem, zincirde bulunan doğru servis saglayıcı nesneyi bulana kadar devam eder. Bu tasarım şablonu için ilginç bir örnek kullanmak istiyorum. İçine para atarak, kahve aldığımız bir kahve otomatı düşünelim. Bir kahvenin bedeli 1 TL olabilir. Kahveyi alabilmek için 1 TL değerindeki metal parayı otomata atmamız gerekiyor. Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Front Controller Tasarım Şablonu Posted: 09 Oct 2009 03:44 AM PDT Web aplikasyonlarında Front Controller tasarım şablonu ile sisteme yöneltilen tüm istekler (request) merkezi bir yerde toplanarak işlem görürler. Front Controller ile, yönlendirme ve işlem yapma fonksiyonlarının birden fazla view (bir JSP sayfası olabilir) elementine dağıtılması önlenmiş olur.Tüm view elementleri yönlendirme ve işlem yapma fonksiyonlarını ortak kullanırlar. Böylece Front Controller tasarım şablonunun kullanıldığı bir proje bakımı ve geliştirilmesi daha kolay bir hale gelir. Ayrıca Front Controller ile gösterim ve navigasyon fonksiyonları birbirinden ayrıldığı için, gösterim katmanını etkilemeden navigasyon idaresi değiştirilebilir yada tamamen yenilenebilir. Bu yazıyı PDF olarak edinebilirsiniz.
EOF (End of Fun) |
|
Flyweight (Sinek Siklet) Tasarım Şablonu Posted: 09 Oct 2009 02:37 AM PDT Java dilinde yazılan programlar içinde sınıflar ve bu sınıflardan oluşturulan nesneler kullanır. Bazen aynı sınıftan yüzlerce, belki binlerce nesne oluşturup, kullanıyor olabiliriz. Bu gibi durumlarda çok nesne oluşturulduğu için sistem performansı kötüye gidebilir. Flyweight tasarım şablonu kullanılarak, kullanılan nesne adedini aşağıya çekebiliriz.
Bu satırlar oluşurken, büyük bir ihtimalle kullandığım editör flyweight tasarım şablonunu kullanıyor olabilir. Yazdığım her cümle kelimelerden, her kelime birden fazla harften oluşmaktadır. Kullandığım editörün Java dilinde yazıldığını ve her harf için bir nesne kullandığını farzedersek, bir satırlık doküman için 80 ila 100 arası harf nesnesi oluşturması gerekir. 100 satırlık bir doküman için bu 10.000 civarı harf nesnesinin oluşturulması anlamına gelir. Bu yazıyı PDF olarak edinebilirsiniz. Konuyla İlgili Kitaplar
|
|
Posted: 08 Oct 2009 02:26 AM PDT Oluşturulmaları zaman alıcı ve sistem kaynaklarını zorlayan nesnelere vekalet eden nesnelere proxy nesneleri adı verilir. Bu nesneler vekil oldukları nesnelerin tüm metodlarına sahiptirler ve kullanıcı sınıf ile vekil olunan nesne arasında aracılık yaparlar. Vekil olan nesne, kullanıcı sınıfa, vekil olunan nesne gibi davranır ve kullanıcı sınıftan gelen tüm istekleri vekil olunan nesneye iletir. Böyle bir yapının kullanılmasının sebebi, gerek olmadığı sürece vekil olunan nesnenin oluşturulmasını engellemektir ya da vekil olunan nesneyi gizlemektir. Böylece vekil olunan nesneye dışardan erişimlerde kontrol altına alınmış olur. Yazılan programın yapısına göre, değişik tipte proxy nesneler kullanılabilir. Bunlar virtual (sanal), remote (uzak bir nesne) ve dynamic (dinamik nesne) proxy nesneler olabilir. Değişik proxy tiplerini yakından inceliyelim. Bu yazıyı PDF olarak edinebilirsiniz.
|
|
KurumsalJava.com Otuzbininci İndirime (Download) Koşuyor Posted: 07 Oct 2009 07:54 AM PDT Hizmet vermeye başladığı günden beri yazılım sektöründe Java teknolojileri ile çalışanların yoğun ilgisini çeken KurumsalJava.com’un bünyesinde barındırdığı ve KurumsalJava.com yazarları tarafından kaleme alınan makalelerin indirilme (download) adedi 30.000 e yaklaştı. Otuza yakın makale tasarım şablonları, extreme programming, yazılım mimarisi, performans, yazılım testleri, open source, ve mimari başlıkları altında okuyuculara sunulmaktadır. KurumsalJava.com sunduğu yüzlerce sayfalık Java kaynak makaleler ile Türkiye’nin öncü Java eğitim ve tecrübe paylaşım platformu haline gelmiştir. PDF formatında olan makalelere Makaleler bölümünden ulaşabilirsiniz.
|
|
Facade (Cephe) Tasarım Şablonu Posted: 07 Oct 2009 06:42 AM PDT Profesyonel yazılım sistemleri birçok komponentin birleşiminden oluşur. Yazılım esnasında bir çok ekip birbirinden bağımsız, sistemin bütününü oluşturan değişik komponentler üzerinde çalışırlar. Bir komponent, belirli bir işlevi yerine getirmek için hazırlanmış bir ya da birden fazla Java sınıfından oluşmaktadır.
Bir komponentin sunmuş olduğu hizmetten yararlanabilmek için, komponentin dış dünya için tanımlamış olduğu giriş/çıkış noktaları (input/output interface) kullanılır. Komponent sadece bu giriş/çıkış noktaları üzerinden dış dünya ile iletişim kurar ve iç dünyasını tamamen gizler. Bu komunikasyon noktaları genelde Facade tasarım şablonu kullanılarak programlanır. Uml diagramında görüldüğü gibi Komponent1 isminde bir sistem komponenti, dış dünya ile komunikasyonu KomponentFacade interface sınıfı üzerinden sağlıyor. Kullanıcı sınıfın (client) tanıması gereken sınıflar FacadeFactory ve KomponentFacade sınıflarıdır. FacadeFactory ile, kullanıcı sınıfın kullanabileceği şekilde bir KomponentFacade nesnesi oluşturulur. Komponentin sunduğu hizmetlere, KomponentFacade interface sınıfında tanımlanmış metodlar aracılığıyla ulaşılır. Komponenti kullanmak için tanımlanan giriş noktası KomponentFacade.doSomething() metodudur. Bu yazıyı PDF olarak edinebilirsiniz. |
|
Posted: 06 Oct 2009 03:16 AM PDT Üzerinde çalıştığım açık kaynaklı JStorage projesinde Tomcat yerine, bir Java sınıfından koşturulabilecek Jetty containeri kullandım. Jetty, Tomcat gibi bir JSP/Servlet containeri. Aşağıdaki örnekte yer aldığı gibi Jetty bir Java (main()) program tarafından çalıştırilabilir.
package org.jstorage.tracker.impl.restfull;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jstorage.tracker.TrackerException;
import org.jstorage.tracker.impl.AbstractTracker;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.servlet.DispatcherServlet;
@Component("RestfullTracker")
@Scope("singleton")
public class RestfulTrackerImpl extends AbstractTracker
{
private final Log logger = LogFactory.getLog(RestfulTrackerImpl.class);
private Server server;
ApplicationContext applicationContext;
private int port;
private String ip;
public RestfulTrackerImpl()
{
}
protected void finalize() throws Throwable
{
super.finalize(); // not necessary if extending Object.
logger.info("Stopping storage node with jetty");
server.stop();
}
@Override
public void runInProductionMode()
{
try
{
logger.info("Starting storage node with jetty in PRODUCTION MODE");
server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping("org.jstorage.tracker.impl.http.TrackerServlet","/");
server.start();
server.join();
}
catch (Exception e)
{
throw new TrackerException(e);
}
}
@Override
public void runInTestMode()
{
try
{
Server server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });
Context context = new Context(server, "/", Context.SESSIONS);
ServletHolder servletHolder = new ServletHolder(new DispatcherServlet());
servletHolder.setInitParameter("contextConfigLocation","classpath:spring-restfulltracker.xml");
servletHolder.setInitOrder(1);
context.addServlet(servletHolder, "/*");
// Add the Spring context listener
context.getInitParams().put("contextConfigLocation","classpath:spring-restfulltracker.xml");
ContextLoaderListener listener = new ContextLoaderListener();
context.addEventListener(listener);
server.start();
context.getServletHandler().initialize();
}
catch (Exception e)
{
throw new TrackerException(e);
}
}
public ApplicationContext getApplicationContext()
{
if(applicationContext == null)
{
applicationContext = new ClassPathXmlApplicationContext("spring-restfulltracker.xml");
}
return this.applicationContext;
}
public int getPort()
{
int p = this.port;
try
{
p = Integer.parseInt(System.getProperty("port"));
}
catch (Exception e)
{
//ignore
}
return p;
}
public void setPort(int port) {
this.port = port;
}
public String getIp()
{
String ip = "127.0.0.1";
try
{
ip = (String)System.getProperty("ip");
}
catch (Exception e)
{
//ignore
}
return ip;
}
public void setIp(String ip)
{
this.ip = ip;
}
}
59. satırda org.jstorage.tracker.impl.http.TrackerServlet isimli Servlet sınıfını http://127.0.0.1/ adresinde erişilecek şekilde sisteme ekliyorum. Bu mapping işlemini genelde web aplikasyonlarında web.xml bünyesinde aşağıdaki şekilde gerçekleştiririz. <servlet> <servlet-name>ForwardedServlet</servlet-name> <servlet-class>tests.Filter.ForwardedServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ForwardedServlet</servlet-name> <url-pattern>/ForwardedServlet</url-pattern> </servlet-mapping> Yukarda yer alan programı çalıştırdıktan sonra Web tarayıcısı üzerinden http://127.0.0.1/ adresine bağlandığımız taktirde TrackerServlet.doGet() metodu işlem görecektir. -Dport=8080 şeklinde bir sistem değişkeni kullanarak (118. satır), Jetty’nin çalıştırılacağı portu değiştirebiliriz.
|
|
Posted: 01 Oct 2009 01:12 AM PDT JAXB (Java Xml Binding – https://jaxb.dev.java.net/) frameworkü kullanılarak Xml–>Java, Java–>Xml mapping işlemlerini gerçekleştirmek mümkündür. Bu yazılımcıya, Xml dosyalarının ihtiva ettiği verileri Java sınıflarıyla modelleme imkanı sunmakta ve verilerın işlenmesini kolaylaştırmaktadır. JAXB’nın nasıl kullanılabileceğini bir örnek üzerinde sizlere aktarmak istiyorum.
Aşağıda bir arabayı modelleyen bir DTD dosyası yer almaktadır. <?xml version="1.0" encoding="ISO-8859-1"?> <!ELEMENT Tasit (araba*)> <!ELEMENT araba (model, motor)> <!ELEMENT model (#PCDATA)> <!ELEMENT motor (beygirgucu, tipi)> <!ELEMENT beygirgucu (#PCDATA)> <!ELEMENT tipi (#PCDATA)> Bu DTD dosyasını kullanarak JAXB yardımı ile Java mapping sınıfları oluşturabiliriz. Bu işlemi Ant ile yapmak mümkündür.
<property name="jaxb.home" value="${basedir}/lib/jaxb" />
<path id="classpath">
<pathelement path="src" />
<pathelement path="classes" />
<pathelement path="schemas" />
<fileset dir="${jaxb.home}" includes="*.jar" />
</path>
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath refid="classpath" />
</taskdef>
<target name="tasit" description="">
<mkdir dir="gen-src" />
<xjc schema="${basedir}/etc/jaxb/test/Tasit.dtd" binding="${basedir}/etc/jaxb/test/Tasit.jaxb" destdir="${basedir}/gen-src">
<arg value="-dtd" />
<produces dir="gen-src" includes="**/*.java" />
</xjc>
</target>
jaxb.home isminde bir değişken ile JAXB paketinin ihtiva ettiği Jar dosyalarının yerini tanımlıyoruz. Xml den türetilen Java sınıflarını oluşturabilmek için com.sun.tools.xjc.XJCTask sınıfını kullanıyoruz. DTD dosyasından Java sınıflarını oluşturabilmemiz için tasit.jaxb isminde bir konfigürasyon dosyası oluşturmamız gerekmektedir. Bu dosyanın içeriği aşağıdaki şekildedir: <?xml version="1.0" ?> <!-- The syntax of the binding file for DTD is defined in the JAXB EA. See vendorSchemaLangs.html for details. --> <xml-java-binding-schema xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"> <!-- specify the package name for the generated code --> <options package="com.tasit.mapping" /> <!-- additional optional XJC specific extensions are available as follows --> <xjc:serializable uid="5" /> </xml-java-binding-schema> Java sınıflarını oluşturmak için Ant dosyasında tanımladığımız tasit isimli hedefi (target) koşturuyoruz. tasit hedefinde belirttiğimiz gibi Java sınıfları xjc tarafından oluşturulduktan sonra gen-src dizine yerleştirileceklerdir. JAXB tarafından oluşturulan sınıflar aşağıda yer almaktadır.
package com.tasit.mapping;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"model",
"motor"
})
@XmlRootElement(name = "araba")
public class Araba {
@XmlElement(required = true)
protected String model;
@XmlElement(required = true)
protected Motor motor;
/**
* Gets the value of the model property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getModel() {
return model;
}
/**
* Sets the value of the model property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setModel(String value) {
this.model = value;
}
/**
* Gets the value of the motor property.
*
* @return
* possible object is
* {@link Motor }
*
*/
public Motor getMotor() {
return motor;
}
/**
* Sets the value of the motor property.
*
* @param value
* allowed object is
* {@link Motor }
*
*/
public void setMotor(Motor value) {
this.motor = value;
}
}
package com.tasit.mapping;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"beygirgucu",
"tipi"
})
@XmlRootElement(name = "motor")
public class Motor {
@XmlElement(required = true)
protected String beygirgucu;
@XmlElement(required = true)
protected String tipi;
/**
* Gets the value of the beygirgucu property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getBeygirgucu() {
return beygirgucu;
}
/**
* Sets the value of the beygirgucu property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setBeygirgucu(String value) {
this.beygirgucu = value;
}
/**
* Gets the value of the tipi property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTipi() {
return tipi;
}
/**
* Sets the value of the tipi property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTipi(String value) {
this.tipi = value;
}
}
package com.tasit.mapping;
import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.tasit.mapping package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
/**
* Create an instance of {@link Motor }
*
*/
public Motor createMotor() {
return new Motor();
}
/**
* Create an instance of {@link Araba }
*
*/
public Araba createAraba() {
return new Araba();
}
/**
* Create an instance of {@link Tasit }
*
*/
public Tasit createTasit() {
return new Tasit();
}
}
package com.tasit.mapping;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"araba"
})
@XmlRootElement(name = "Tasit")
public class Tasit {
protected List<Araba> araba;
/**
* Gets the value of the araba property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the araba property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAraba().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Araba }
*
*
*/
public List<Araba> getAraba() {
if (araba == null) {
araba = new ArrayList<Araba>();
}
return this.araba;
}
}
Şimdi Tasit.dtd den türetilmiş bir Xml dosyasında yer alan verileri xjc tarafından oluşturulan Java sınıflarına nasıl yükleyebileceğimizi yakından inceleyelim. Tasit.dtd ye uyumlu bir Xml örneği aşağıda yer almaktadır: <?xml version="1.0" encoding="ISO-8859-1"?> <Tasit> <araba> <model>Ford Fiesta</model> <motor> <beygirgucu>75</beygirgucu> <tipi>benzinli</tipi> </motor> </araba> <araba> <model>Opel Agila</model> <motor> <beygirgucu>70</beygirgucu> <tipi>benzinli</tipi> </motor> </araba> </Tasit> Aşağıda yer alan program yardımı ile Xml dosyasında yer alan verileri elde edebiliriz:
package com.tasit;
import java.io.File;
import java.io.FileReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.EventFilter;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import com.tasit.mapping.Araba;
public class Main {
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
XMLInputFactory xmlif = XMLInputFactory.newInstance();
FileReader fr = new FileReader(new File("c:/temp/araba.xml"));
XMLEventReader xmler = xmlif.createXMLEventReader(fr);
EventFilter filter = new EventFilter() {
public boolean accept(XMLEvent event) {
return event.isStartElement();
}
};
XMLEventReader xmlfer = xmlif.createFilteredReader(xmler, filter);
StartElement e = (StartElement) xmlfer.nextEvent();
JAXBContext ctx = JAXBContext.newInstance("com.tasit.mapping");
Unmarshaller um = ctx.createUnmarshaller();
while (xmlfer.peek() != null)
{
Object o = um.unmarshal(xmler);
if (o instanceof Araba)
{
Araba araba = (Araba) o;
System.out.println(araba.getModel());
System.out.println(araba.getMotor().getBeygirgucu());
System.out.println(araba.getMotor().getTipi());
System.out.println("-----------------------------------");
}
}
fr.close();
}
}
Ekran çıktısı şu şekilde olacaktır: Ford Fiesta 75 benzinli ----------------------------------- Opel Agila 70 benzinli ----------------------------------- Xml–>Java mapping işlemlerinin nasıl yapıldığını gördük. Aşağıdaki Java örneği Java–>Xml mapping işleminin nasıl yapıldığını göstermektedir.
package com.tasit;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import com.tasit.mapping.Araba;
import com.tasit.mapping.Motor;
public class Main {
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
Araba araba = new Araba();
Motor motor = new Motor();
motor.setBeygirgucu("300");
motor.setTipi("dizel");
araba.setMotor(motor);
araba.setModel("Crossfire");
Marshaller marshaller = null;
marshaller = JAXBContext.newInstance("com.tasit.mapping").createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
StringWriter writer = new StringWriter();
marshaller.marshal(araba, writer);
System.out.println(writer.getBuffer().toString());
}
}
Programı koşturduğumuzda ekran çıktısı şu şekilde olacaktır:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<araba>
<model>Crossfire</model>
<motor>
<beygirgucu>300</beygirgucu>
<tipi>dizel</tipi>
</motor>
</araba>
|
|
Hibernate Session Problemi: Illegal attempt to associate a collection with two open sessions Posted: 10 Sep 2009 01:49 AM PDT BTSoru.com projesini modellerken aşağıdaki hata ile karşılaştım: org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
Hatanın oluşmasını sağlayan kod bölümü şu şekilde: // Soruyu ekleme bölümü BTUser user = new BTUser(); user.setId(WebSession.get().getUser().getId()); getQuestion().setUser(WebSession.get().getUser()); QuestionManagerResult result = qManager.addQuestion(getQuestion()); Kod 1.1 Birinci satırda yer alan BTUser bir Hibernate nesnesi. BTUser ile bir kullanıcıyı modelliyorum. UML diagramı aşağıda yer almaktadır:
Sistem bünyesinde her kullanıcının sıfır ya da birden fazla sorusu olabilir. Sorulan her soru sadece bir kullanıcıya aittir. BTUser gibi BTQuestion sınıfı da bir Hibernate nesnesi, yani ihtiva ettiği veriler bilgilbankasında saklanabilmekte. Kod 1.1 de yer alan kodun yedinci satırında qManager.addQuestion() metodu ile yeni bir soruyu (BTQuestion nesnesini) bilgibankasına ekliyorum. BTQuestion nesnesinin ihtiva ettiği verileri bir form üzerinden kullanıcı daha önce girdi. Gerekli validasyon işlemlerini daha önce gerçekleştirerek, BTQuestion nesnesini, yani kullanıcının sisteme eklemek istediği soruyu bilgibankasına ekliyorum. BTUser ile BTQuestion arasındaki ilişkiyi sağlamak için aşağıdaki şekilde BTQuestion bünyesinde bir ManyToOne ilişkisini oluşturuyorum. Bu şekilde BTQuestion nesnesi bir kullanıcıya atanmış olacaktır.
@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(
name="btsoru_user_questions",
joinColumns = { @JoinColumn( name="question_id", unique = false) },
inverseJoinColumns = @JoinColumn( name="user_id", unique = false)
)
private BTUser user;
Kod 1.2 @JoinTable anotasyonu ile kullanıcı ile sahip olduğu soruları ilişkilendirmek için yeni bir bilgibankası tablosu oluşturuyorum (btsoru_user_question). Bu tablo bünyesinde kullanıcının sahip olduğu tekil numara (user_id) ve soru nesnesinin tekil numarası (question_id) yer almaktadır. Bu şekilde kullanıcı ve soruyu ilişkilendirmiş oluyorum. Bu konfigurasyonda kod 1.1 altıncı satırda yer alan qManager.addQuestion() metodunu kullandığım taktirde, yukarda bahsettiğim Hibernate problemi oluşuyor. Bunun sebebi şudur: Kod 1.1, üçüncü satırda Hibernate tarafından kontrol edilen bir sınıfdan (BTUser) bir nesne oluşturarak, bu nesneye id değerini atıyorum. Buradaki amacım, bir BTQuestion nesnesini bilgibankasına eklemeden önce bir BTUser nesnesi ile ilişkilendirmek. Bunu getQuestion().setUser(WebSession.get().getUser()); ile gerçekleştiriyorum. Bunu yapmadığın taktirde, bilgibankasına herhangi kullanıcıya ait olmayan bir soru yer alacaktır ve bu istenmeyen bir durumdur. Dördüncü satırda WebSession.get().getUser().getId()); ile kullanıcı id sini session içinde bulunan BTUser nesnesinden ediniyorum. Id değişkenini kullanıcının kimlik numarası olarak düşünebilirsiniz. Bu işlemi gerçekleştirebilmem için kullanıcının soruyu sisteme eklemeden önce isim ve şifresi ile login yapmış olması gerekmektedir. Login işlemi ardından oluşturduğum BTUser nesnesini session içine yerleştiriyorum ve gerekli durumlarda WebSession.get().getUser()); ile bu nesneye, yani login yapmış kullanıcının bilgilerine ulaşıyorum. Sorun da ne yazik ki burada oluşmaktadır. Login işleminin ardından belirli bir id ye sahip bir BTUser nesnesi sistemde (session içinde) mevcuttur. Aynı id ye sahip ikinci bir nesne oluşturulduğu taktirde, Hibernate buna Illegal attempt to associate a collection with two open sessions hata mesaji ile karşı çıkacaktır. Böyle bir konstellasyonda Hibernate kontrolünde bulunan (Hibernate Cache) bir nesneye rakip olacak aynı id yi taşıyan ikinci bir nesne oluşturup kullanmamız mümkün değildir. Bu sorunu aşmak için BTQuestion bünyesinde bulunan BTUser değişkeninin mapping parametrelerini kod 1.3 deki gibi değiştirmemiz yeterli olacaktır.
@ManyToOne(fetch=FetchType.LAZY)
@JoinTable(
name="btsoru_user_questions",
joinColumns = { @JoinColumn( name="question_id", unique = false) },
inverseJoinColumns = @JoinColumn( name="user_id", unique = false)
)
private BTUser user;
Kod 1.3 @ManyToOne anotasyonundaki cascade=CascadeType.ALL elementini kaldırmamız yeterlidir.
|
|
BizimAlem.com – Java İle Yüksek Performanslı Web Platformu Posted: 21 Jun 2009 06:22 AM PDT 11-12 Nisan 2009 tarihlerinde CETURK tarafından Kıbrıs Doğu Akdeniz Üniversitesinde düzenlenen Java Etkinlikleri Günü’nde “Java ile yüksek performanslı web platformlarının oluşturulması” başlıklı bir sunum yaptım. Özcan Acar
|
|
Command (Komut) Tasarım Şablonu Posted: 17 May 2009 07:57 AM PDT Command tasarım şablonunu açıklamak için televizyonu uzaktan kumanda etme aletini metafer (mecazi örneklendirme) olarak kullanmak istiyorum. Kanalları değiştirmek için kumanda aleti üzerinde belirli bir tuşa basarız. Tusa basıldığı anda kumanda aleti televizyona bir komut göndererek, kanalın değişmesini sağlar. Aynı şekilde televizyonu açıp, kapatmak ve ses ve renk ayarlarını yapmak için değişik tuşlar kullanılır. Kullanıcı olarak tuşa basıldığında, televizyon bünyesinde ne gibi bir işlemin yapıldığı hakkında bilgi sahibi olmamız gerekmiyor. Bunu alıcının (televizyon) bilmesi yeterlidir. Bilmemiz gereken tek şey, hangi tuşun altında hangi komutun olduğudur. Bir nesne üzerinde bir işleminin nasıl yapıldığını bilmediğimiz ya da kullanılmak istenen nesneyi tanımadığımız durumlarda, Command tasarım şablonu ile yapılmak istenen işlemi bir nesneye dönüştürerek, alıcı nesne tarafından işlemin yerine getirilmesi sağlıyabiliriz
Bu yazıyı PDF olarak edinebilirsiniz.
|
|
Çanakkale 18 Mart Üniversitesi Çevik Süreç ve TDD Semineri Posted: 05 May 2009 11:28 AM PDT Kurumsal Java Akademisi olarak 4 Nisan 2009 tarihinde, Çanakkale 18 Mart Üniversitesinde Çevik Süreç ve TDD (Test Driven Development – Test Güdümlü Yazılım) konulu semineri düzenledik.
|
|
Oracle Sun ve Java’yı Satın Aldı Posted: 21 Apr 2009 04:20 AM PDT Geçtigimiz günlerde ürün portföyündeki bilgibankası ile meşhur Oracle firması Java’nın oluşturucusu Sun firmasını satın aldığını duyurdu. Kısa bir zaman önce IBM ile yaptığı satış görüşmelerini yarıda kesen Sun firması, Oracle tarafından sunulan milyarlık teklife hayır diyemedi. Sun açısından bu satışın gerçekleşmesi çok olumlu, çünkü Sun firmasının mevcut ürün portföyüyle ayakta kalması hemen hemen imkansızdı. Java açısından bakıldığında bu gelişme olumsuz olarak değerlendirilebilir. Kısa bir analiz yaparak, Java platformunun geleceği hakkında beraberce fikir yürütelim. Java dünyada en çok kullanılan bilgisayar dili konumunda. Java’nın geliimesine en büyük katkı açık kaynaklı (open source) ürünler üreten programcı kitlesi (community) oldu ve bu değişmeyecek. Java gerçek gücünü bu kitleden almakta. Oracle doğal olarak daha öncede yaptığı satın almalarla (Bea, MySQL) kendi portföyünü kuvvetlendirmek amacında. Sun’ı satın alarak Oracle firması tekelleşme yolunda. Bu kanunen aslında izin verilmemesi gereken bir durum. Demoktatik düzene sahip ülkelerde tekelleşmeyi devlet engellemekle sorumludur. USA için bunun geçerli olmadığı bir kez daha doğrulanmış oldu. Oracle Java’nın meydana getirdiği yenilikçilik ve açık kaynaklılık filozofisine bağli kalarak Java’nın önünü kesmeyeceğini söylüyor. Oracle’ın Java’nın önünü kesmesi kendi işini de gelmeyecektir, çünkü BEA gibi bir firmayı satın alarak çok güçlü bir şekilde Java ve SOA dünyasına girdi. Ama Oracle bunları tabiri caizse dedesinin hayrı için yapmıyor. Ön planda Java ve diğer yatırımları ile para kazanma amacı var ve Oracle Java’yı bir limon gibi sıkarak son damlasına kadar suyunu çıkarma girişiminde bulunabilir. Bunu zaman içinde görecegiz. Oracle, Java hakkında kararların alındığı JCP (Java Community Process) merkezi karar kurulunu da ele geçirmiş oldu. Bu Oracle’ın Java’nın geleceğinde daha fazla etkisi olabileceği anlamına gelir. Lakın kanaatimce bu gelişmelerin hiçbirisi Java’nın geleceğini olumsuz etkilemeyecektir. Oracle’ın karşısında bulunan en güçlü silah Java’yı bu günlere getiren gönüllü programcı kitlesidir (community). En büyük gelişme impulslarını Java her zaman bu kitleden almıştır, Oracle gibi büyük firmalardan değil. Bu sebepten dolayı Oracle’ın Java’yı tamamen ele geçirip, kendi amaçları için kötüye kullanabileceğini hiç düşünmüyorum. Java önümüzdeki yıllarda da kurumsal projelerde çok önemli bir rol oynamaya devam edecektir. Özcan Acar |
|
Doğu Akdeniz Üniversitesi Java Etkinlikleri Günü Posted: 08 Apr 2009 01:06 PM PDT 11-12 Nisan 2009 tarihlerinde CETURK tarafından Kıbrıs Doğu Akdeniz Üniversitesinde düzenlenen Java Etkinlikleri Günü’nde “Java ile yüksek performanslı web platformlarının oluşturulması” başlıklı bir sunum yapacağım. Detayları http://www.ceturk.com/etkinlikkayit.asp?id=71 adresinden temin edinebilirsiniz
Özcan Acar
|
|
Subversion İle Versiyon Kontrolü Posted: 07 Apr 2009 12:50 AM PDT İnsanoğlu okuma, yazmayı icat etmeden önce mağara duvarlarına resimler yaparak düşüncelerini şekillendirmeye başladı. Araştırmalara göre ilk yazının Sümer’liler tarafından İsa’dan önce 3500 civarında icat edildiği söylenmektedir. O devrin insanları yazı benzeri işaretler kullanarak ilk dokümanları oluşturmuş ve bu dokümanları iletişim aracı olarak kullanmışlardır. Günümüzde latin alfabesinde yer alan kelimeleri kullanarak bilgisayarda dijital dokümanlar oluşturuyoruz, arkadaşlarımıza email gönderiyoruz yada elimize kağıt ve kalem alarak mektup yazıyoruz. Bu işlemlerin sonucunda dijital olan yada olmayan bir doküman oluşuyor. Her doküman oluşturulduğu ilk saniyeden itibaren bir versiyon ihtiva eder. Bir versiyon, dokümanın geleceğe doğru olan yolculuğunda durak yaptığı istasyonlardan birisidir. Zaman içinde doküman değişikliğe uğrar. Her değişikliğin ardından dokümanın yeni bir versiyonu oluşur. Dokümanın herhangi iki versiyonu arasındaki fark, bu iki versiyonun oluşumu için geçen zaman diliminde, doküman üzerinde yapılan tüm değişiklikleri ihtiva eder. Bu yazıyı PDF olarak edinebilirsiniz. Konuyla İlgili Kitaplar
|
|
Çanakkale 18 Mart Üniversitesi Çevik Süreç ve TDD Semineri Posted: 30 Mar 2009 12:35 PM PDT Kurumsal Java Akademisi olarak 4 Nisan 2009 tarihinde, Çanakkale 18 Mart Üniversitesinde Çevik Süreç ve TDD (Test Driven Development – Test Güdümlü Yazılım) konulu semineri düzenliyoruz. Konuşmacı olarak ben (Özcan Acar) katılacağım.
Katılım ücretsizdir. Kendi üniversitelerinde bu tür seminerlerin düzenlenmesini isteyenler, bizim ile iletişime geçebilirler.
SEMİNERİN YERİ VE TARİHİ |
|
Yazılım Mimarisi Tasarımı Günü Posted: 23 Mar 2009 11:42 AM PDT CETURK tarafından 21.3.2009 tarihinde Elazığ Fırat Üniversitesinde düzenlenen Yazılım Mimarisi Tasarımı Günü‘ne konuşmacı olarak katıldım ve Tasarım Prensipleri ve Spring MVC konulu iki sunum yapım. Sunum dosyalarını aşağıdaki linkler üzerinden edinebilirsiniz. Düzenlediğimiz diğer seminerleri Kurumsal Java Akademisi sayfalarından takip edebilirsiniz. Tasarım Prensipleri sunumu Spring MVC sunumu .
|
|
Yazılım Mimarisi Tasarımı Günü Posted: 17 Mar 2009 01:42 AM PDT CETURK tarafından 21.3.2009 tarihinde Elazığ Fırat Üniversitesinde düzenlenecek olan Yazılım Mimarisi Tasarımı Günü‘ne konuşmacı olarak katılacağım ve Tasarım Prensipleri ve Spring MVC konulu iki sunum yapacağım.
Detayli bilgiyi CETURK etkinlik sayfasıdan edinebilirsiniz. Özcan Acar
|
|
Posted: 11 Mar 2009 08:32 AM PDT Adapter tasarım şablonu yardımı ile, sistemde mevcut bulunan bir sınıfın sunduğu interface (sınıf metodları) başka bir sınıf tarafından kullanilabilir şekilde değiştirilir (adapte edilir). Bu adapter yardımı ile birbiriyle beraber çalışamıyacak durumda olan sınıflar, birlikte çalışabilir hale getirilir.
Adapter tasarım şablonu kendi içinde sınıf ve nesne adapteri olarak ikiye ayrılır. Önce sınıf adapterini inceliyelim.
Bu yazıyı PDF olarak edinebilirsiniz. Konuyla İlgili Kitaplar |
|
Posted: 11 Mar 2009 07:57 AM PDT Spring çatısı hakkında geniş bilgiyi yeni kitabım Pratik Spring Core‘da bulabilirsiniz. |
|
Posted: 10 Mar 2009 02:06 PM PDT Günümüzde kullanılan birçok program web arayüzleriyle internet ve intranet ortamlarında kullanılmaktadır. Web tabanlı programların popüler olmasının sebepleri bir taraftan sadece bir web tarayıcısının (browser) yeterli olması, diğer taraftan da bu tür aplikasyonların bilgisayar üzerinde herhangi bir kurulum (install/setup) gerektirmemesidir.
Web tabanlı programlar kullanıcı (client) için herhangi bir kurulum gerektirmezken, server tarafında programın içinde çalışabileceği bir ortamın oluşturulması gerekmektedir. Bu ortamlar web protokolü olan HTTP1 üzerinden kullanıcı ile interaksiyona girebilecek yapıdadırlar. Gerekli bu ortamları oluşturmak için Apache2 ve Tomcat3 gibi server programları kullanılır. Java dünyasında web aplikasyonları oluşturmak için Servlet/JSP teknolojileri kullanılır. Bu teknolojilerle implemente edilmiş bir sistem Tomcat gibi bir Servlet Container içinde çalışabilir, çünkü Tomcat program için gerekli altyapı hizmetlerini sunmaktadır. Bu yazıyı PDF olarak edinebilirsiniz. Konuyla İlgili Kitaplar |
|
Java Test Güdümlü Web Yazılımı Semineri Posted: 10 Mar 2009 01:00 PM PDT 07.03.2009 tarihinde Hidayet hocam (Hidayet Tekneci) ve ben (Özcan Acar) Işık üniversitesinde çevik süreçleri anlatan ve test güdümlü bir web aplikasyonun nasıl implemente edildiğini pratik bir örnek üzerinde gösteren bir seminer düzenledik. Işık ünivesitesinin daveti üzerine yaptığımız bu seminere Işık üniversitesi ögrencileri yanısıra yazılım endüstrisinde çalışan arkadaşlar katıldı.
Düzenlediğimiz diğer seminerleri Kurumsal Java Akademisi sayfalarından takip edebilirsiniz. Seminerde kullanılan teknolojiler: 1.) Java 5.0 Web aplikasyonunda incelediğimiz uygulamalar: 1.) Wicket ile web yazılımı
|
|
Posted: 03 Mar 2009 10:52 AM PST 28 Şubat 2009 tarihinde İstanbul IBM Türk’de CETURK tarafından düzenlenen Çevik Süreç ve TDD seminerine konuşmacı olarak katıldım ve Çevik Java EE Web Uygulamaları ve Mimarileri isimli bir sunum yaptım. Sunum dosyasını aşağıdaki link üzerinden edinebilirsiniz. » Eclipse Java Projesi
TDD Uygulaması
|
|
Extreme Programming Hakkında Bazı Soru ve Cevapları Posted: 03 Mar 2009 02:14 AM PST Kullanıcı hikayesi (user story) nedir? XP projelerinde müşteri gereksinimlerinin yer aldığı kullanıcı hikayeleri oluşturulur. Bir kullanıcı hikayesi sistemin tipik bir özelliğini bir ya da iki cümle ile anlatan araçtır. Örneğin üye girişi olan bir sistemde, şöyle bir kullanıcı hikayesi düşünülebilir:
Kullanıcı isim ve şifreni kullanarak sisteme giriş yapar. Kullanıcı hikayeleri hikaye kartlarına (story card) yazılır. Bu kartlar sürüm ve implementasyon planları yapılırken kullanılır. Her kullanıcı hikayesinin implementasyon zamanı programcılar tarafından tahmin edilir. Müşteri kullanıcı hikayelerine öncelik sırası vererek implementasyon sırasını tayin eder. Programcılar tarafından yapılan tahmin ve müşteri tarafından belirlenen öncelik sırası kullanıcı hikayesinin üzerinde yer aldığı hikaye kartlarına not edilir. Ayrıca müşteri tarafından oluşturulan akseptans (onay/kabul) testleri hikaye kartlarının arka bölümüne not edilir. Bir kullanıcı hikayesinin büyüklüğü ne kadar olmalı? Bu sorudaki büyüklük sıfatı ile kullanıcı hikayesinin kaç günde implemente edilebilir olduğu kastedilmektedir. Programcılar tarafından kullanıcı hikayelerin implementasyon süreleri gün bazında tahmin edilir. Bir kullanıcı hikayesinin en fazla dört yada beş günde implemente edilebilir yapıda olması gerekmektedir. Daha fazla zaman gerektiren kullanıcı hikayelerinin müşteri tarafından bölünerek, küçültülmeleri gerekmektedir. Kullanıcı hikayelerini kim oluşturur? Kullanıcı hikayelerini müşteri oluşturur, çünkü gereksinimleri en iyi bilen müşteridir. XP projelerinde müşterin programcılarla beraber çalışması talep edilir. Müşteri kendi işini bırakıp, nasıl proje için çalışabilir? Başka işi yok mu? Bu genelde müşterinin kendi işini gücünü bırakıp, projede başka işlerle uğraşması gerektiği şeklinde değerlendirilir, ama durum öyle değildir. Müşterinin programcılara yakın bir yerde olması, programcıların oluşan sorulara kısa sürede müşteri yardımıyla cevap bulmalarını kolaylaştırır. Asıl maksatta budur zaten. Müşteri gün boyunca programcıların sorularına cevap verir. Bunun yanı sıra kendi günlük işlerini takip eder. Çoğu zaman günlük işleri yapabilmek için bir bilgisayar yeterli olacaktır. Müşteri kendi işlerini yaparken ara sıra programcılara zaman ayırarak, soruları cevaplar. Birden fazla müşteri varsa, hangisinin sözü geçerlidir? Proje ekibinin karşısında sadece bir müşteri olmalıdır. Eğer birden fazla müşteri varsa, bu şahıslar bir araya gelerek, tek bir şahıs gibi programcı ekibi ile iletişim kurmalıdırlar. Sürüm planını kim oluşturur? Sürüm planı müşteri ve programcılar tarafından ortaklaşa oluşturulur. Ne ve hangi sıraya göre yapılması gerektiği müşteri tarafından belirlenir. Bu yüzden sürüm planının oluşumunda müşterinin ağırlığı daha fazladır. Planlama için zaman tahminleri programcılar tarafından yapılır. Bu şekilde programcılar proje planlama sürecine aktif olarak katılarak sorumluluk alırlar. Sürüm planını ne oranda sabittir? Proje başında oluşturulan sürüm planı projenin çeşitli safhalarında değişikliğe uğrayabilir. Bu doğaldır. Müşteri yazılım sisteminin ilk sürümleriyle gereksinimlerinin ne olduğunu daha iyi anlayabilir ve mevcut kullanıcı hikayeleri üzerinde değişilik yapılmasını talep edebilir ya da yeni kullanıcı hikayeleri oluşturabilir. Bu durumda sürüm planının değiştirilmesi gerekmektedir. Sürüm ve iterasyon arasındaki fark nedir? Sürüm yazılım sisteminin belirli bir versiyondaki halidir. Her yeni sürüm yeni özelliklerin implemente edildiği ve müşteri tarafından produktif kullanılan program versiyonudur. XP projelerinde her yeni sürüm bir ile dört ay süren bir çalışma sonunda oluşturulur. Müşteri tarafından seçilen kullanıcı hikayeleri belirli bir zamansal uzunluğa sahip olan iterasyonlarda implemente edilir. İterasyonlar bir ile dört haftalık zaman birimini kapsarlar. Bir önceki resimde yer alan sürüm beş iterasyondan oluşmaktadır. Her iterasyon iki hafta sürmektedir. Sürüm onuncu haftanın sonunda oluşturulmaktadır. Hangi kullanıcı hikayesiyle işe başlanır? Bu sorunun cevabını sürüm ve iterasyon planı verir. Sürüm ve iterasyon planlarında müşteri tarafından belirlenen kullanıcı hikayeleri yer alır. Müşteri, kullanıcı hikayeleri için sahip oldukları değere göre öncelik sırası belirler. Sürüm ve iterasyon planlarında kullanıcı hikayeleri bu öncelik sırasına göre yer alırlar. Programcılar her iterasyonda, o iterasyon için seçilmiş olan kullanıcı hikayelerini öncelik sırası yüksekten düşüğe doğru implemente ederler. Buradaki ana amaç, müşteri için en değerli özelliklerin yer aldığı bir sürümü oluşturarak, müşteri tarafından kullanılabilir hale getirmektir. Bu yüzden her zaman müşteri açısından en değerli olan sistem özellikleri öncelikli olarak implemente edilir. Kullanıcı hikayesi implementasyonu ne zaman tamamlanmıştır? Programcılar bir kullanıcı hikayesini test güdümlü implemente ederler. Testler tamamlandıktan sonra akseptans testleri yapılmak üzere kullanıcı hikayesinin yer aldığı hikaye kartı (story card) testçiye (tester) devredilir. Testçi müşteri tarafından tanımlanmış olan akseptans (onay/kabul) testlerini implemente eder. Akseptans testlerini geçen bir implementasyon bitmiş olarak kabul edilir. Mevcut projeler üzerinde XP uygulanabilir mi? Bu büyük ölçüde projedeki JUnit testleri oluşturma alışkanlığına bağlı. XP test güdümlü implementasyonu şart koşmaktadır. Eğer programcılar tarafından programlara paralel olarak testler geliştiriliyorsa, test güdümlü implementasyona geçmeleri zor olmayacaktır. Bunun yanı sıra projedeki mevcut iletişim kültürü önemlidir. XP çok yönlü iletişimi gerekli kılmaktadır. Örneğin pair programming gibi tamamen iletişim ve takım işine bağımlı olan bir metot programcılar tarafından ne oranda uygulanabilir, bunun araştırılması gerekmektedir. Sürekli entegrasyon, test güdümlü yazılım, müşterinin projeye dahil edilmesi, kısa sürelerde yeni sürüm oluşturulması gibi konular XP’yi yeni başlamayan projeler için zor adapte edilebilir kılmaktadırlar. XP’nin yeni projelerde adaptasyonu çok daha kolaydır. Bir iterasyon süresi ne kadar olmalı? Bu yazılım sisteminin sahip olması gereken özelliklerle doğru orantılıdır. Eğer iki ay içinde ilk sürüm oluşturulması planlanıyorsa, iterasyon süresi bir ile iki hafta olacak şekilde seçilebilir. Bunun yanı sıra müşteri tarafından oluşturulan kullanıcı hikayelerinin implementasyonu için programcılar tarafından verilen tahminlerin dikkate alınması gerekmektedir. Örneğin programcılar tarafından ortalama her kullanıcı hikayesi için bir ile iki gün tahmin edilmişse, bir haftalık iterasyonda en az üç en çok beş kullanıcı hikayesi implemente edilebilir. Eğer kullanıcı hikayeleri ortalama dört veya üzeri günde implemente edilebilir durumda ise, o zaman interasyonun en az iki hafta olarak seçilmesi gerekmektedir. İterasyon süresi sabittir ve uzatılamaz, bu yüzden seçilen kullanıcı hikayelerinin seçilen sürede implemente edilebilir yapıda olmaları gerekmektedir. Akseptans (okay/kabul) testlerini kim oluşturur? Akseptans testleri kullanıcı hikayesini oluşturan müşteri tarafından tanımlanır. Akseptans testlerinin implementasyonunu programcılar yada testçiler üstlenir. Kaç tane JUnit testi hazırlanmalı? Kod paylaşımı nasıl yapılır? Kod paylaşımını kolaylaştırmak için bir versiyon kontrol sisteminin kullanılması şarttır. Subversion son zamanlarda kullanılan en popüler açık kaynaklı versiyon kontrol sistemi haline gelmiştir. Kurumsal Java Akademisi Subversion eğitimi » Pair programming yaparken tecrübeli bir programcı ile tecrübesiz bir programcının beraber çalışması zaman ve kaynak kaybı değil midir? Hayır! Pair programming tekniği ile programcıların teknik anlamda ayni seviyeye gelmeleri sağlanır. Tecrübeli programcılar can, tecrübesiz programcılar patlıcan değildir :) Tecrübesiz programcılar için seminer düzenlemek yerine, onlara pair programming seanslarında teknoloji ve proje hakkında bilgi transfer etmek daha mantıklıdır. Pair programming iki programcının bir kişilik iş çıkarması anlamına gelmez mi? Pair programming maliyetli bir yöntemdir. Lakin iki programcının beraber aynı implementasyon üzerinde çalışmasından sinerjiler doğar. Pair programming iş kalitesini yükseltir. Ayrıca pair programming ile kodun ve tasarımın iki değil dört göz ile kontrol edilmesini sağlanır. XP projelerinde mimariyi ve tasarım nasıl oluşur? Mimari (altyapı) proje öncesinde yapılan keşif safhasında (Exploration Phase) oluşur. Programcılar müşteri tarafından oluşturulan kullanıcı hikayelerini okudukça, neye ihtiyaç duyduklarını anlarlar ve ona göre altyapıyı geliştirirler. Proje öncesi detaylı tasarım oluşturulmaz. Tasarım test güdümlü implementasyon esnasında programcılar tarafından oluşturulur. Eğer programcılar implementasyon esnasında sorunlarla karşılaşırlarsa, refactoring yöntemleri kullanarak tasarım üzerinde değişiklik yaparlar. Unit testleri refactoring işleminin yapılmasını kolaylaştırır. Yapılması gereken değişiklikler sonraya bırakılmaz, çünkü bu ilerde maliyetin yükselmesine sebep olabilir. XP projelerinde mimariyi ve tasarımı kim oluşturur? Programcılar. Bilgi bankası olan bir sistemde test güdümlü yazılım nasıl uygulanır? Bunun çeşitli yöntemleri vardır. Öncelikle bilgi bankası işlemlerinin DAO (Dao Access Object) tasarım şablonu kullanılarak bir interface sınıf arkasında saklanması en mantıklı çözümdür. Mock sınıflar kullanılarak DAO katmanı simule edilebilir. Bu sistemin diğer bölümlerinin test güdümlü implementasyonunu kolaylaştırır. DAO kullanıldığı taktirde, gerçek bilgi bankasına olan bağımlılık azaltılır. DAO interface sınıfını değişik türde implemente ederek, bilgi bankası yerine başka bir yapıda kullanılabilir. Hibernate ve IBatis gibi bir framework kullanılması durumunda, test güdümlü yazılımı mümkün kılabilmek için bu frameworklerin sunduğu interface sınıflar (SessionFactory, Session vs.) mock nesneler ile simüle edilebilir. Planlama pokeri nedir? Programcı bir kullanıcı hikayesini implementasyon öncesi tüm detaylarıyla bilmek zorunda değildir. Bir kullanıcı hikayesini en son detayına kadar kavramaya çalışmak zaman kaybı olabilir, çünkü kullanıcı hikayesinde yeralan müşteri gereksinimi değişikliğe uğrayabilir. Bu genelde müşterinin programın ilk sürümlerini görmesiyle gerçekleşir. Çalışır bir program aracılığıyla müşteri gereksinimlerini daha iyi kavrayacak ve gerekli değişiklikleri talep edecektir. Bu sebepten dolayı implementasyona başlamadan önce kullanıcı hikayesinin ihtiva ettiği tüm detayları tespit etmek faydalı olmayacaktır, çünkü kullanıcı hikayesi değişikliğe ugrayabilir. Programcı ekibin kullanıcı hikayesi hakkında implementasyon için gerekli zamani tahmin edebilecek kadar bilgiye sahip olması yeterli olacaktır. Programcılar müşteri tarafından seçilen kullanıcı hikayesinin implementasyon süresini tahmin ederler. Bu tahminler planlama pokerinde yapılır. Planlama pokeri yapılmayan tahminler bazen sağlıklı sonuçlar vermeyebilir. Bir programcı tarafından yapılan tahmin diger programcıları etkiliyebilir. Yada bazı programcılar herhangi bir sebepten dolayı tahmin etme sürecine aktif olarak dahil olmayabilirler. Bu gibi sebeplerden dolayı oluşan tahmin süreleri yanıltıcı olabilir. Daha geçerli tahminler elde edebilmek için planlama pokeri oynanır. Planlama pokeri için kullanılan kartlar bir önceki resimde yer almaktadır. Her programcı bu kartların bir setine sahiptir. Planlama pokeri şu şekilde oynanır: Bir moderator ilk kullanıcı hikayesini okur. Müşteri bu kullanıcı hikayesi için implementasyon süresinin ne olduğunu sorar. Programcılar kısa bir zaman düşündükten sonra hep beraber planlama poker kartlarından birisini seçerek gösterirler. Çoğu zaman kullanılan kartlardakı değerler farklı olacaktır. En çok süreyi ve en az süreyi tahmin eden programcılardan bu sonuca nasıl vardıklarının açıklanması istenir. Verilen bilgiler dogrultusunda ortak bir değer bulunur. Tahminler için hikaye puanları (story points) kullanılır. 1 hikaye puanı örneğin 1 iş günü (8 saat) olabilir. Programcılar her kullanıcı hikayesini kendi başına tahmin etmek yerine, kullanıcı hikayelerini birbirleriyle kıyaslıyarak tahminde bulunurlar. Örneğin kullanıcı hikayesi A için 2 hikaye puanı tahmin edilmişse, kullanıcı hikayesi B bu değer göz önünde bulundurularak tahmin verilir. Eğer kullanıcı hikayesi B A dan üç katı daha büyükse, o zaman B için tahmin 2×3 = 6 hikaye puanı olarak verilir. Load Factor nedir? Bir kullanıcı hikayesinin ideal şartlarda implementasyonu için gerekli zaman dilimi ile normal şartlarda implementasyonu için gerekli zaman dilimi farklı olacaktır. Örneğin programcılar gün boyunca yazılım haricinde toplantı, bilgi alışverişi gibi işler için zaman ayırmak zorundadir. Bir programcının sekiz saatlik bir iş gününde sekiz saat program yazabilmesi ideal zaman dilimi olarak tanımlanır. Toplantı ve diğer işler için kullanılan zaman ideal zaman diliminde çikartıldığı zaman normal zaman dilimi elde edilir. Kullanıcı hikayelerinin tahminlerinde ideal ve normal zaman dilimlerinin göz önünde bulundurulması gerekmektedir, aksi taktirde kullanıcı hikayesi için yapılan tahmin gercekleri yansıtmıyacaktır. Gerçekci bir tahmin yapabilmek için load factor olarak bilinen değer kullanılır. Bu değer bir kullanıcı hikayesinin implementasyonu için kullanılan zamanın ideal zamana bölünmesiyle elde edilir. Örneğin bir kullanıcı hikayesi için 1 iş günü (8 saat) tahmin edilmiş ve programcı kullanıcı hikayesini 2 iş gününde tamamlamış olsun. Bu durumda load factor 16 / 8 = 2 olacaktır. Load factor için 2 ila 5 arasında bir değer normaldir. Tahmin yapılırken tahmin edilen implementasyon zamanı load factor ile çarpılır. Örneğin programcı bir kullanıcı hikayesini 2 iş gününde implemente edebileceğini düşünüyorsa, kullanıcı hikayesi için tahmin süresi 2 değil, 2 iş günü x 2 load factor = 4 olmalıdır. Bu şekilde daha gerçekci tahmin elde edilir. Programcılar load factor değerini göz önünde bulundurarak, tahminde bulunurlar. Spike solution nedir? Eğer programcılar bir kullanıcı hikayesi için tahminde bulunamazlarsa küçük çaplı bir demo implementasyonu yaparak implementasyon süresini tahmin etmeye çalışırlar. XP dilinde bu işe spike solution ismi verilir. İdeal şartlarda bu işlemin sürüm planlama oyunundan önce yapılmış olması gerekir. Buradan sürüm planlama oyunu için kullanıcı hikayelerinin oyun öncesi hazırlanmış olması gerektiği sonucunu çikartabiliriz. Programcılar yaptıkları denemeler sonunda kullanıcı hikayesi için tahmin verebilecek duruma gelirler. Özcan Acar |
|
Neden sürekli entegre edilmeli? Posted: 03 Mar 2009 02:05 AM PST Oluşturduğunuz yazılım sistemini sürekli entegre etmiyorsanız, zamanı gelince toptan entegre etmek zorundasınız. Bunun, neden yazılım hayatınızda karşılaşabileceğiniz en büyük sorun olabileceğini bir örnek vererek açıklamak istiyorum.
Şimdi şunu hayal edin: Yeni bir otomobilin tasarlanması projesinde yer aldınız. Otomobil tasarlandı ve otomobili oluşturan parçalar 5 değişik ülkede, 20 değişik firma tarafından üretildi. Bu ülkelerde kullanılan uzunluk ve ağırlık birimleri (metre, kg vs.) değişik olabilir. Görev dağılımı esnasında yanlış anlamalardan dolayı üretilen parçalar birbirine uyumlu olmayabilir. Eğer tüm parçalar üretildikten sonra bir çırpıda tüm otomobili oluşturmak isterseniz, üretim sürecinde meydana gelen hataları daha önceden tespit edemediğiniz için, yamuk yumuk bir otomobil ortaya çıkacaktır. Ama üretim esnasında koordineli bir şekilde otomobili parça parça bir araya getirip, parçalar uyuşuyor mu diye kontrol etmiş olsaydınız, meydana gelen uyuşmazlıkları çok erken tespit ederek, gerekli değişiklikleri yapabilirdiniz. Bu yazılım sektörü için de geçerli. Oluşturulan sistem komponentleri ne kadar erken entegre edilirse, oluşan uyuşmazlıklar o kadar erken tespit edilir ve gerekli değişiklikler yapılabilir. Sürekli entegrasyon çevik süreçlerde çok önemli bir yazılım metodudur. Sistem üzerinde yapılan her değişiklik sürekli entegrasyonu otomatik olarak gerçekleştiren sunucu tarafından kontrol edilir. Yazılımda kırılmalar oluşması (compile hataları, eksik sınıflar vs.) durumunda, tüm ekip sürekli entegrasyon sunucusu tarafından uyarılır. Bu geribildirim sayesinde entegrasyonun ne safhada olduğu anlaşılır. Sürekli entegrasyon hakkında diğer bir makalemi http://www.kurumsaljava.com/2008/11/26/surekli-entegrasyon-continuous-integration/ adresinden edinebilirsiniz. Özcan Acar |
|
Yazılımda Değişik Test Türleri Posted: 03 Mar 2009 01:42 AM PST Yazılım sürecinde oluşturulan sistemin kalite kontrolü unit testleri ile yapılır. Java tabanlı sistemlerde unit testleri JUnit olarak isimlendirilir. Bu isim aynı ismi taşıyan test frameworkü olan JUnit’den gelmektedir. Java’da unit testleri yazabilmek için JUnit frameworkü (http://www.junit.org) kullanılır.
Değişik türde unit testleri oluşturmak mümkündür. Bunlar:
Programcılar sisteme eklenen her Java sınıfı için bir JUnit test sınıfı oluştururlar. Test sınıfında, test edilen sınıfın her metodu için bir test oluşturulur. Unit ismi buradan gelmektedir. Unit testleri ile kendi içinde bütün olan bir kod ünitesi test edilir. Bunlar genellikle sınıfların metotlarıdır. Test sınıfı kullanılarak, test edilen sınıfın işlevlerini doğru olarak yerine getirip, getirmediği test edilir. Metot bazında hazırlanan testlere JUnit testleri diyoruz. Sınıf üzerinde yapılan her değişiklik ardından o sınıf için hazırlanan unit testleri çalıştırılarak, yapılan değişikliğin yan etkileri olup, olmadığı kontrol edilir. Testlerin olumlu netice vermesi durumda, sınıfın görevini hatasız yerine getirdiği ispat edilmiş olur. Bu açıdan bakıldığında unit testleri programcılar için kodun kalitesini korumak ve daha ileri götürebilmek için vazgeçilmezdir. Sistem üzerinde yapılan her değişiklik yan etkilere sebep olabileceği için, sistemin her zaman çalışır durumda olduğunu sadece unit testleri ile kontrol edebiliriz. Onlarca ya da yüzlerce sınıfın olduğu bir programı, her değişikliğin ardından elde kontrol etmek imkansız olduğu için otomatik çalışabilen unit testlerine ihtiyacımız vardır. Birçok komponentten oluşan bir sistemde, komponentler arası entegrasyonu test etmek için entegrasyon testleri oluşturulur. Entegrasyon testleri hakkında detaya girmeden önce, JUnit testleri hakkında bir açıklama daha yapma gereği duyuyorum. JUnit testleri, test edilen sınıfları kullandıkları diğer sınıflardan bağımsız olarak test ederler. Örneğin bir sınıf bilgibankasından veri edinmek için bir servis sınıfını kullanıyorsa, JUnit testinde bu servis sınıfı kullanılmaz, çünkü bu bilgibankasının çalışır durumda olmasını gerektirir. Eğer JUnit testi içinde bilgibankası kullanılıyorsa, bu JUnit testi değil, entegrasyon testidir, çünkü test edilen sınıf ile bilgibankası arasındaki ilişki dolaylı olarak test edilmektedir. Daha öncede belirttiğim gibi JUnit testleri metot bazında ve sınıfın dış dünyaya olan bağımlılıklarından bağımsız olarak gerçekleştirilir. Amacımız bir metot içinde bulunan kod satırlarının işlevini test etmektir. Bunun için bilgibankasına bağlantı oluşturulması gerekmez. Test edilen sınıfın bağımlılıklarını ortadan kaldırabilmek için Mock nesneler kullanılır. Bir Mock nesne ile servis sınıfı işlevini yerine getiriyormuşcasına simüle edilir. JUnit testinde, test edilen sınıf servis sınıfı yerine onun yerine geçmiş olan Mock nesnesini kullanarak, işlevini yerine getirir. Entegrasyon testlerinde Mock nesneleri kullanılmaz. Entegrasyon testlerindeki ana amaç sistemin değişik bölümlerinin (subsystem) entegre edilerek işlevlerini kontrol etmektir. Entegrasyon testlerinde test edilen sınıflar için gerekli tüm altyapı (bilgibankası, email serveri vs.) çalışır duruma getirilir ve entegrasyon test edilir. Sistem komponentleri bir veya birden fazla sınıftan oluşabilir. Komponent kullanımını kolaylaştırmak için interface sınıflar tanımlanır. Komponenti kullanmak isteyen diğer komponent ve modüller bu interface sınıfına karşı programlanır. Komponentler arası interaksiyon Arayüz (interface) testleri ile test edilir. Bu testlere fonksiyon testleri adı da verilir. Regresyon bir adım geri atmak anlamına gelmektir. Regresyon yazılım esnasında, programın yapılan değişiklikler sonucu çalışır durumdan, çalışmaz bir duruma geçtiği anlamına gelir. Regresyon testleri ile sistemden yapılan değişikliklerin bozulmaları neden olup, olmadığı kontrol edilir. Sistem üzerinde yapılan her değişiklik istenmeyen yan etkiler doğurabilir. Her değişikliğin ardından regresyon testleri yapılarak, sistemin bütünlüğü test edilir. Regresyon testlerinde sistem için kullanılan altyapı tanımlanmış bir duruma getirildikten sonra testler uygulanır. Örneğin test öncesi bilgibankası silinerek, test için gerekli veriler tekrar yüklenir. Her test başlangıcında aynı veriler kullanılarak, sistemin nasıl reaksiyon gösterdiği test edilir. Regresyon testlerinin uygulanabilmek için test öncesinde tüm altyapının başlangıç noktası olarak tanımlanan bir duruma getirilmesi gerekmektedir. Akseptans testleri ile sistemin bütünü kullanıcı gözüyle test edilir. Bu tür testlerde sistem kara kutu olarak düşünülür. Bu yüzden akseptans testlerinin diğer bir ismi kara kutu testleridir (black box test). Kullanıcının sistemin içinde ne olup bittiğine dair bir bilgisi yoktur. Onun sistemden belirli beklentileri vardır. Bu amaçla sistem ile interaksiyona girer. Akseptans testlerinde sistemden beklenen geri dönüm test edilir. Stres testleri tüm sistemin davranışını eksepsiyonel şartlar altında test eden testlerdir. Örneğin bir web tabanlı programın eşli zamanlı yüz kullanıcı ile gösterdiği davranış, bu rakam iki yüze çıktığında aynı olmayabilir. Stres testleri ile sistemin belirli kaynaklar ile (hardware, ram, işletim sistemi) stres altındaki davranışı test edilmiş olur. Performans testleri ile sistemin, tanımlanmış kaynaklar (hardware, ram, işletim sistemi) yardımıyla beklenen performansı ölçülür. Test öncesi sistemden beklenen davranış biçimi tayin edilir. Test sonrası beklentiler test sonuçlarıyla kıyaslanır ve kullanılan kaynakların beklenen davranış için yeterli olup olmadığı incelenir. Sistemin davranış biçimi kullanılan kaynakların yetersiz olduğunu göstermesi durumunda, ya sistem üzerinde değişikliğe gidilir ve sistemin mevcut kaynaklar ile yetinmesi sağlanır ya da kaynaklar genişletilerek sistemin istenilen davranışı edinmesi sağlanır. Çevik süreçlerde unit testleri büyük önem taşımaktadır. Extreme Programming bir adım daha ileri giderek, test güdümlü yazılım (Test Driven Development – TDD) konseptini geliştirmiştir. Test güdümlü yazılımda baş rolü unit testleri oynar. Sınıflar oluşturulmadan önce test sınıfları oluşturulur. Bu belki ilk bakışta çok tuhaf bir yaklaşım gibi görünebilir. Var olmayan bir sınıf için nasıl unit testleri yazılabilir diye bir soru akla gelebilir. TDD uygulandığı taktirde, oluşturulan testler sadece sistemde olması gereken fonksiyonların programlanmasını sağlar. Programcılar kafalarında oluşan modelleri program koduna çevirirler. Bu süreçte çoğu zaman sistemi kullanıcı gözlüğüyle değil, programcı gözlüğüyle görürler. Bu da belki ilk etapta gerekli olmayan davranışların sisteme eklenmesine sebep verebilir. Unit testlerinde bu durum farklıdır. Örneğin akseptans testlerinde tüm sistem bir kullanıcının perspektifinden test edilir. Bu testlerde sistemin mutlaka sahip olması gerektiği davranışlar test edilmiş olur. Eğer akseptans testleri oluşturarak yazılım sürecine başlarsak, testin öngördüğü fonksiyonları implemente ederiz. Böylece gereksiz ve belki bir zaman sonra kullanılabileceğini düşünerek oluşturduğumuz fonksiyonlar programlanmaz. TDD ile testler oluşturulan sisteme paralel olarak oluşur. Sonradan bir sistem için onlarca ya da yüzlerce unit testi oluşturmak çok zor olacağı için, unit testlerini oluşturarak yazılama başlamak çok daha mantıklıdır. TDD konseptini bir sonraki bölümde detaylı olarak yakından inceleyeceğiz. Özcan Acar |
|
Yazılımda Şelale (Waterfall) Yöntemi Posted: 25 Feb 2009 11:21 PM PST Şelale yönteminde yazılım geliştirme süreci analiz, tasarım, kodlama, test, sürüm ve bakım gibi safhalardan oluşur. Geleneksel yazılım metodlarında bu safhalar şelala modelinde olduğu gibi linear olarak işler. Her safha, baslangıç noktasında bir önceki safhanın ürettiklerini bulur. Kendi bünyesindeki değişikler doğrultusunda teslim aldıklarını bir sonraki safhanın kullanabileceği şekilde degiştirir.
Şelale modelinin özelliklerini şu şekilde sıralayabiliriz:
Proje başlangıcında her detayı göz önünde bulundurmak mümkün olmadığı için, şelale modeliyle geliştirilen yazılım sistemlerinin müşteri gereksinimlerini tam tatmin etmediğini görmekteyiz. Bunun önüne geçebilmek için projenin başlangıç safhasında analiz için çok zaman harcanır ve müşteri gereksinimleri en ince detayına kadar tespit edilir. Aslında proje başlangıcıyla oluşturulan dokümanlar obsolet (eskimiş) hale gelmiştir, çünkü müşteri gereksinimleri piyasa ve rekabet koşulları gereği değişikliğe uğramış olabilir. Ne yazik ki şelale modeli bunları dikkate almaz ve müşterinin talep ettigi değişiklikleri aza indirmeye çalışır. Bunun bir sebebide sonradan gelen değişiklik taleplerinin maliyeti yükseltmesidir, çünkü bu durumda şelale modelinde yer alan safhaların birkaç kere uygulanması gerekebilir.
Bu çerçeveden bakıldığında proje sonunda oluşan program müşterinin aktüel gereksinimlerini tatmin etmez durumdadır. Program daha çok müşterinin proje başlangıcında sahip olduğu gereksinimleri tatmin edecek şekilde tasarlanmıştır. Projelerin birkaç sene boyunca sürebileceğini düşünürsek, aslında bu süreç sonunda oluşan program aktuel değildir. Neden yazılımda şelale yöntemi kullanılmamalı?
Özcan Acar |
|
Posted: 15 Feb 2009 05:56 AM PST CETURK tarafından 28.1.2009 tarihinde düzenlenen Çevik Süreç ve TDD Günü’ne konuşmacı olarak katılıyorum. Günün tanıtım metni aşağıda yer almaktadır. Bugüne kadar çok farklı konu ve teknoloji ile ilgili yaptığı ücretsiz etkinliklerle biliğim sektörünün gelişmesine katkıda bulunan CETURK etkinliklerine devam ediyor. Şubat ayında 6. yaşını kutlayan CETURK, 7 Şubat’ta Microsoft Türkiye İstanbul Ofisi‘nde gerçekleştirdiği CETURK Microsoft Teknolojileri Etkinliği ardından 28 Şubat’ta IBM Türk‘te “CETURK Çevik Süreç ve TDD Günü ” etkinliği düzenliyor. Etkinlikte çok değerli konuşmacılar hem teknik sunumlar hem de örnek projeler ile bilgi ve tecrübelerini katılımcılarla paylaşacaklar. Ayrıca gün sonunda tüm konuşmacıların katılacağı “Çevik Süreçler ve Türkiye’de Kullanımı” konulu bir panel gerçekletirilecektir.
Etkinliğe katılım her zamanki gibi ücretsiz olacaktır. Ayrıca her zaman olduğu gibi etkinliğe katılanlardan 5 kişiye Özcan Acar’ın Extreme Programming isimli kitabı hediye edilecektir. Kayıt için : http://www.ceturk.com/etkinlikkayit.asp?id=64 |
|
Çevik Sürece Geciş Nasıl Olmalı? Posted: 13 Feb 2009 06:13 AM PST Extreme Programming ve Scrum gibi çevik süreçlerin popüler olmasının sebebi, müşteri gereksinimlerini tatmin edebilen yazılım sistemlerinin oluşturulma sürecini kolaylastırmalarında yatmaktadır. Bu böyle olunca, yazılım firmaları, yıllarca şelala (Waterfall) metodundan çektikleri sıkıntılardan kurtulmak amacıyla çevik süreçlerin adaptasyonuna yönelmektedirler. Doğal olarak burada firmaların çevik sürecin adaptasyonu esnasında kafalarında oluşan bazı sorular var. Bunlardan en önemli iki soru şöyle:
Bu soruların cevaplandırılabilmesi, firma ve sahip olduğu çalışanlarının çalışma kültürü ile doğrudan orantılıdır. Genelde çevik sürecin uygulanmasının önündeki en büyük engellerden birisi, çalışanların sahip oldukları alışkanları ve çalışma yöntemleridir. Çalışanlar yeniliklere ve değişikliklere içgüdüsel olarak karşı koyarlar. Bu yüzden çevik sürece geciş gibi çok yan etkileri olabilecek bir girişimin başarısı, çalışanların değişimle yaşayabilme özellikleriyle doğrudan orantılıdır. Çevik süreci adapte etmiş bir çok takım, yazılım esnasında içinden çıkılamaz problemlerle karşılaşmakta ve doğal olarak suçu çevik süreçte bulmaktadırlar. Acaba suç gerçekten çevik süreçte mi? Eğer bir takım “çevik süreci adapte ettik, lakin karşılaştığımız sorunlar günden güne artıyor, işin içinden çıkamıyoruz” şeklinde kendini savunuyorsa, o zaman o takımın, çevik süreci nasıl adapte ettiğinin incelenmesinde fayda vardır. İzlenimlerime göre, böyle takımlar tarafından çevik süreç ya yanlış ya da eksik olarak adapte edilmektedir. Örneğin birçok takım sadece Scrum kullanarak çevik olabileceklerini düşünüyorlar. Scrum hiçbir yazılım mühendisliği metodu ihtiva etmez! Eğer çevik olmak istiyorsanız, yazılım metotlarınızın çevik olması gerekmektedir. Bunu da en iyi sağlayayan Extreme Programming‘dir. Extreme Programming eşli programlama (pair programming – PP), sürekli entegrasyon (continuous integration – CI), test güdümlü yazılım (test driven development – TDD), müşteri ile beraber çalışma (on site customer) gibi birçok metoda sahiptir. Bu metotlar uygulandığı taktirde çevik çalışma ortamı oluşturulabilir ve oluşan sorunlar ortadan kaldırılabilir. Bu Scrum’ın gereksiz olduğu anlamına gelmez. Scrum proje planlama ve yönetme alanında kendine has yöntemleri ile çevik sürece katkıda bulunur. Scrum Extreme Programming ile birleştiği anda çevik süreç, gerçek ve oluşan sorunları aşabilen bir çevik süreç haline gelir, çünkü sorunları aşabilmek için ekibin elinde bir çok yeni teknik metot (PP, CI, TDD) bulunmaktadır. İşte çevik olmak isteyipte, olamayan takımların ana sorunu, PP, CI ve TDD gibi metotları tanımamaları ve bu yüzden dolayı uygulayamamaların da yatmaktadır. Atalarımızın da dediği gibi “alet çalışır, el övünür”. Eğer çekiciniz yoksa, çiviyi çakamassınız. Eğer sürekli entegrasyon yapmıyorsanız, o zaman müşteriye kısa aralıklarla, çalışan entegre bir prototip sunamazsınız. Test güdümlü çalışmıyorsanız, kodu yeniden yapılandırmanız (refactoring) hemen hemen imkansız hale gelir. Eğer kodu yeniden yapılandırmanız mümkün değilse, müşterinin yeni gereksinimlerini ya da istediği değişiklikleri implemente etmeniz imkansız hale gelir. Eğer eşli programlamıyorsanız, programcılar aynı seviyede koda hakım olamaz ve kod sahiplenmeleri başlar! Şelale modelinden XP gibi bir çevik sürece geciç, hiç unit test yazmamış bir programcının, birden bire TDD tarzı programlama ile yüzyüze gelmesi gibi birşeydir. Daha öncede belirttiğim gibi takımın bu gibi radikal değişikliklerle yaşayabilmesi ve kabullenmesi çok önemlidir. Şimdi çevik sürece geçişin nasıl olması gerektiğini, yazının başında yer alan iki soruya doğrudan cevap vererek inceleyelim. Çevik süreç adım adım mı yerleştirilmeli?Çoğu takım çevik sürece adım adım geçmeyi tercih etmektedir. Genelde takımın içinde bulunduğu ortam bu tür bir kararın verilmesini zorunlu kılmaktadır. Çevik sürece adım adım geçiş çogu zaman çok uzun sürebileceği için başarılı olmayacaktır. Buradaki başarısızlığın en önemli faktörü yine takım elemanlarının kendileridir. Takım elemanları sahip oldukları çalışma alışkanlıklarını bırakmakta çok zorlanacak ve dolaylı olarak istemeyerekte olsa yeni yerleştirilmeye çalışılan çevik süreci sabote edeceklerdir. Burada yeni çevik metotlar adapte edilerek, yazılımcı bir nevi soğuk suyun içine atılmalı ve yeni metotlarla yüzmeyi yani çalışmayı ögrenmesi sağlanmalıdır. Ancak bu şekilde sahış, değişikligi şok terapisi ile kabullenme eğiliminde olacaktır. Bu eski kuramların ve çalışma metotlarının tekneden atılmasını kolaylaştırır. Çevik sürece hemen ve tüm neticelerine katlanarak mı geçilmeli?Bu sorunun cevabı nettir: evet! Tamamen çevik bir yazılım modeline geçmek istiyorsanız, o zaman şimdiye kadar yaptıklarınızı unutmanız gerekiyor. Bu, şimdiye kadar çevik çalışmadığınız anlamına gelmez. Ama doğal olarak her çalışma ekibinin kendine has yöntemleri var ve bunları değiştirmeleri çok zor. Bu yüzden bir celsede çevik sürece geçip, uygulamaya başlanması gerekiyor, aksi taktirde çevik süreç işlemeyecektir. XP nin temel prensiplerinden birisi de cesaret. Ekibin ve firmanın yeni bir başlangıç için tüm gücünü kullanıp, tam anlamıyla çevik sürece geçmesi lazım, yarı gönülle bu iş ne yazık ki yürümez! Kısaca özetleyecek olursak:
|
| You are subscribed to email updates from KurumsalJava.com - Ozcan Acar
To stop receiving these emails, you may unsubscribe now. |
Email delivery powered by Google |
| Google Inc., 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States | |
|
Çalışan Bir Java Uygulamasında Bytekod Nasıl Değiştirilir? Posted: 02 Nov 2014 08:30 AM PST Çalışan Bir Java Uygulamasında Bytekod Nasıl Değiştirilir? başlıklı yazım. |