Category Archives: Yazılım Mühendisliği

Yazılım Mühendisliği Röportajı

Yazılım mühendisliği öğrencisi olan sayın Abdullah Gürsoy arkadaşımız benden bir roportaj talebinde bulunmuştu. Biraz geciktirmeme rağmen sonunda bitirebildim. Diğer yazılım dünyası adaylarına da oldukça faydalı olacağını düşünüyorum ve bunun için kendisine teşekkür ediyorum. Diğer arkadaşlarında okuması için kendi sayfama koymanın iyi olacağını düşündüm.Ayrıca yazının son kısmında Abdullah'ın diğer yazılım mühendisi olan arkadaşlarla olan roportajlarını da ilgili linkten bulabilirsiniz. İşte roportajımız..

1- Kendinizi ve Yazılım Mühendisliği ile olan geçmişinizi kısaca anlatabilir misiniz?

Kocaeli Üniversitesi Bilgisayar Mühendisliğinden mezun oldum. Bilgisayarlar ile lise yıllarımda ilk Intel 386 SX25 ve Windows 3.1 işletim sistemli bilgisayarımla tanıştım. Tabi o zamanlar oyun oynamanın ve DOS'da komut yazmanın dışında pek birşey yapmıyordum yazılım anlamında. İlk yazılım ile tanışmamda o zamanlar mahalledeki teknik lisede okuyan bir abinin bana pascal ile yazılmış programlarını göstermesi ile olmuştu. Daha sonra bana bir kitap vermişti ve oradaki birkaç kodu kopyalayıp denemiştim. Ama o zamanlar hiçbirşey anlamadığımı ifade edebilirim. Yazılım ile gerçek tanışmam üniversitenin 1. sınıfında programlama dersinde oldu. O andan itibaren yazılımı gerçekten sevdiğimi anladım. Bu derste ödev olarak ilk yazdığım “Çarkıfelek” oyununu ise ilk yazılımım olarak adlandırabilirim. Piyasaya sürmedim ama hala saklarım ve kuzenlerim oynar arasıra :). Okul bittikten sonrada özel sektörde yazılım mühendisi olarak göreve başladım ve hala özel sektörde çalışıyorum.

2- Sizce mühendisliğin tanımı nedir?

Bence mühendislik bilimsel olarak kanıtlanmış prensiplerin, kuralların doğruların Pratik olarak üretilen çözümlerde,problemlerde kullanılmasıdır.

3- Yazılım Mühendisliği'ndeki “Mühendislik” kelimesinin sebebi nedir? Moda olarak herseyin sonuna”mühendislik” kelimesi eklemekten mi geliyor, yoksa gerçekten “Mühendislik” ile alakası var mi?

Yazılım mühendislik midir yoksa sanat mıdır sürekli tartışılan bir konu. Bir inşaat ya da makina mühendisliği gibi olmadığı kesin. Çünkü onlar kadar kesin kurallar uygulanarak ortaya ürünler çıkmıyor. Ortaya çıkan ürünlerde programcının kendisinden kattığı, kendi stili ile geliştirdiği kodlar, ayrıca projenin yapısına gore değişen çok farklı yazılım tasarımları içeriyor. Aynı işi yapan iki yazılım arka planda birbirinden dağlarca farklı koda sahip olabilirler buda yazılımın içine gore insanın yazdığı koda verdiği şeklin etkisini gösteriyor. Bu bakımdan yazılımı sanat olarak görebiliriz.

Diğer yandan çoğu yazılım projesinde uygulanan ya da artık uygulanması neredeyse zorunlu olan analiz, tasarım, test gibi süreçler var. Problemlerle karşılaştıkça ortaya çıkan patterns, practices gibi kavramlar var. Bunlarda bize yazılımın mühendislik tarafını gösteriyor.

Yani bence yazılım mühendisliği hem mühendislik hemde sanat.

4- Çok yakin gibi görülen Bilgisayar Mühendisliği ile Yazılım Mühendisliğinin arasındaki farklar nelerdir? Bunları iş hayatından örneklerle ve tecrübelerinizle açiklayabilir misiniz?

Bugüne kadar iş hayatımda yazılım mühendisliği bölümünden mezun olan bir yazılımcı ile çalıştığımı söyleyemem.Bunu bilgisayar mühendisliğine göre daha yeni bir bölüm olduğu için haliyle mezun sayısının daha az olmasına bağlıyorum.
Bilgisayar mühendisliği mezunu olduğum için önce bu bölümde neler öğretiliyor ya da öğretildi ondan bahsetmek istiyorum. Bilgisayar mühendisliği bölümünde Matematik , Fizik gibi temel mühendislik derslerinin dışında Elektronik,Haberleşme, Donanım, Network, Güvenlik, Programlama, Yazılım alanlarında dersler alıyorsunuz. Bu derslerin içeriği okuluna göre değişmekle birlikte (mesela İTÜ mezunu arkadaşlarım genelde oldukça fazla donanıma yönelik yoğun dersler aldıklarını söylerler) temel olarak yukarıda bahsettiğim alanlardan temel bilgileri alırlar. Sonuçta mezun olanlar Network, Güvenlik, Yazılım ve Donanım alanında çalışabilirler. Bilgisayar mühendisliği bölümünden mezun kendi arkadaşlarımdan örnek verecek olursam çoğunluğu Yazılım mühendisi olarak çalışıyor. Fakat network, güvenlik, elektronik alanında çalışan arkadaşlarım da var.

Yazılım mühendisliği daha yeni olduğu için mezunlarıyla çalışamadım fakat ders programına baktığımda elektronik ve donanım dersleri dışında diğer derslerin çoğunun aynı olduğunu görüyorum. Yazılım mühendisliğinden farklı olarak gördüğüm “Yazılım Mimarisi, Tasarım Kalıpları,Yazılım Süreçleri,Proje Analiz ve Yönetimi” ile ilgili derslerin daha yoğun olması. Bunlarda yazılım mühendisi olmak isteyen birisinin piyasada oldukça işine yarayacak konular.Okulda bu derslerin üzerine ne kadar düşülüp nasıl projeler yapılıyor bilmiyorum ama bu dersler iyi değerlendirilip gerçekten bir şeyler öğrenilerek mezun olunursa piyasada oldukça faydalı olacaktır.

5- Yazılım Mühendisi, sadece uml bilen programcı mıdır ? Eğer değilse tam olarak kimdir ve geliştirilen projelerde nerededir?

Yazılım mühendisi yazılımın gerektiğinde analizini, tasarımını, geliştirilmesini yapabilen kişidir diye düşünüyorum. Bence yazılım mühendisinin programlamayı çok iyi bilmesi gerekir çünkü yazılım projelerinde çalıştığınız programlama diline gore üretilen çözümler , kullanılan tasarım değişebilir. Ama bana gore sadece programlama bilmek yetmiyor. Yazılımın doğru analizinin yapılması, programlanırken ileride bakım maliyetini azaltacak şekilde esnek programlanması, ileriye yönelik varsa eklenti noktalarının düşünülmesi, testi, performans darboğazlarının belirlenmesi bunları düşünerek geliştirme yapılması gerekmektedir. Sadece UML bilmek bunların çok çok küçük bir kısmı. Bu yüzden yazılım mühendisi sadece UML bilen programcı değildir.

Ayrıca programlamanında yazılımın en önemli ayaklarından birisi olup sonucu en çok etkileyen kriter olduğunu bilmek lazım. Programlamayı bu yüzden asla küçümsemeyin. Yazılımı nasıl programladığını geleceğini en çok etkileyen faktörlerden biridir.

Bunları söylerken her işin yazılım mühendisi gerektirmediğinide söyleyebilirim. Her projenin yapısı farklı olduğu için bazı projeler mühendislik gerektirirken bazıları gerektirmemektedir. Kendi geçmişime baktığımda büyük projeler daha çok yazılım mühendisine ihtiyaç duyuyor. Çalıştığım yapay zeka , simülasyon içeren projelerde çok ciddi analiz tasarım gerektirmekteydi. Bu tarz projelerde mühendislik daha fazla uygulandığı için yazılım mühendisine daha çok ihtiyaç vardı. Bununda birlikte sadece veri giriş çıkışı yapan herhangi bir kompleks iş modeli içermeyen işlerde de çalıştım bunlarda mühendislik açısından çok fazla gereklilik göremedim.

En son şunu belirtmek isterim burada konuştuğum Yazılım Mühendisi ve Programı mezun olunan okul ile çok bağlantılı değildir. Yazılım/Bilgisayar mühendisliği mezunu olup programcı olarak çalışan birçok arkadaşım olduğu gibi programlama(ya da diğer bölümlerden) mezunu olup yazılım mühendisi olarak çalışan iş arkadaşlarım oldu. Kişinin kendisini mühendis ya da programcı olarak yetiştirmesi kendisine bağlıdır.

6- Yazılım Mühendisinin bilmesi gereken 5 önemli şey nedir?

5 tane madde çıkarabilirmiyim bilmiyorum ama aklıma gelen önemli bulduğum şeyleri sıralayayım. Bence en önemli şey öğrenmenin asla bitmeyeceğini kendimizi bir konuda veya alanda asla herşeyi biliyor olarak düşünmememiz gerektiğidir. Benim tecrübelerim ve iş hayatında yazılım mühendislerinde gördüğüm en büyük eksikliklerden birisi 3-5 sene sonra çoğu mühendisin artık öğrenecek birşey kalmadığını düşünüp giderek körelmesi ve problemlere yaratıcı çözümler bulamaması. Bu yüzden ilk sloganımız “Sürekli öğrenme”.

Ayrıca yaptığımız işleri bitti diyip bir kenara bırakmamalıyız sürekli nasıl iyileştirebiliriz diye bir yandan kafa yormalıyız çünkü her yazılım belli bir süre sonra bozulma eğilimindedir. Gözümüzün yazılımın tasarımını ve çalışmasını koruyabilmesi için sürekli onda olması ve iyileştirememiz gerekiyor. Kısacası refactoring de diyebiliriz.

Daha sonra yazılım mühendislerinin teknik olarak kendini çok iyi yetiştirmesi gerekiyor. Mühendis adam program yazmaz ya da programcı değildir algısını çok yanlış buluyorum. Yazılım mühendisi üzerinde çalıştığı platformu programlama dilini çok iyi bilmiyorsa problemlere çözüm üretmekte mutlaka zorlanacaktır çünkü her platformun problemlere kendine özgü bir çözümü var bu yüzden teknik programlama ,database vs.. gibi konuları çok iyi bilmelidir.

Programlama iyi bilmelidir dedim yukarıda ama yapılan diğer yanlış ise mühendisliği ya da yazılım geliştirmeyi sadece program yazmak olarak görmek. Yazılım tasarım analiz performans dökümantasyon gibi birçok bölümden ulaşamıyor. İnanın programlama önemli ama sadece bir kısmı. Yazılım mühendisi için yazılım tasarımını iyi bilmek olmazsa olmazlardan. Daha ucuz kaliteli ürünler ortaya çıkarmak için iyi tasarlanmış bir yazılım geliştirmek gerekiyor.

Platformlar problemi çözmek için sadece araçlardır. Yazılım mühendisinin platformu programlama dili olmaz. Problemi çözmek için en uygun platfor var ise o platformda yazılımı geliştirir tasarlar. En çok yapılan yanlışlardan biri takım tutar gibi platform tutup .NET vs Java tartışmaları yapmaktır. Kendimden örnek verecek olursam Java, .NET,Delphi Web, Desktop,Simulasyon gibi birçok platformda yazılım geliştirdim platformlar farklı olsada yazılım tasarımı problemlere çözüm yaklaşımı hepsinde aynı bu yüzden platform bağımsız mühendis olmak oldukça önemli. Sonuçta kullandığımız platformların çoğu 10-20 sene içinde var olmayacak.

Bilgisayar nasıl çalışır? . Bende dahil yazılım mühendislerinin büyük eksiklerinden biri daha. Bir mühendisin yazılımın,bilgisayarın nasıl çalıştığını en azından temel şekilde bilmesi gerekiyor. Yazdığımız yazılım nasıl derlenir nasıl belleğe yüklenir nasıl CPU tarafından işletilir bu komutlar nasıl elektrik sinyallerine dönüşür vb.. Bunlar çok fazla önemli gelmeyebilir ama problemlere farklı çözümler üretmede üzerinizdeki platformun sınırlarını bilmeniz ve gerektiğinde zorlamanız konusunda çok fazla yardımcı olurlar. Örnek IE 6 Javascript memory leak problemini IE 6 için uygulanmış Garbage Collection algoritmasının nasıl olduğunu bilerek birçok performans problemini çözebilirsiniz.

Oldukça uzun oldu şöyle özetlersek

1. Sürekli Öğrenme
2. Sürekli iyileştirme
3. Teknik olarak bilgili(Programlama, database vb..)
4. Yazılım tasarımı(Patterns,Practices vb..)
5. Platform bağımsızlığı
6. Bilgisayarın doğası.

7- Bir öğrenci olarak bakıldığında, belli dertlerin, belli çözümleri getirdigi, Yazilim Mühendisliği'nin de bu çözümlerden biri olduğu görünüyor. Çözümler uluslar-arası olsada bazen bu dertler yerel oluyor ve bu yerel dertler, çözümleri başka biryerden ithal edenler için farklılıklar arz ediyor. Sizin de yazılım mühendisliği ile ilgili kaynaklarda okuduğunuz fakat Türkiye'de işlerin farklı yürüdüğü şeyler var mi? Var ise bunlar nelerdir?

Aslında geçenlerde yazdığım yurtdışı tecrübelerimden kısaca bahsettiğim yazımdan alıntı yaparak bu soruya cevap verebilirim. 1,5 senelik çalıştığım firmanın bir yurtdışı şubesine ve orda yaptığım projede tecrübem oldu. Hem kitaplardan bolca gördüğüm hemde dışarıda uygulanıp bizde dışarısı kadar ciddi uygulanmayan şeyleri şöyle sıralayabilirim.

  • Ürün çıktımı, tam anlamıyla çıkmış oluyor. En ufak ayrıntısı bile atlanmamış çıkması planlanmış şeylerin font seçimine kadar her şey düşünülmüş çıkıyor.Ürün çıktığında kurulumu, ayarları her şey paket olarak geliyor. Aldıktan sonra kurulum içinde bir 10 gün harcamıyorsunuz.
  • Çok profosyonel Dökümantasyon yapılıyor. Bunu yazılım geliştirici kendi yazdığı özellik için yapması zorunlu. Ayrıca müşteri dökümantasyonu için ayrı bir ekip çalışıyor. Yazılım geliştirilirken dökümantasyon da aynı anda gelişiyor ve özellik bittiğinde müşteri ve yazılım dökümantasyonu hazır oluyor.
  • Test işine çok önem veriliyor. Bizdeki gibi ekrana iki gir çık sonra tamam anlayışı yok. Sistem test, performans test, entegrasyon test ekipleri var. Manuel ve otomatik testler sürekli çalışıyor. Testerlar yeri geldiğinde çıkıcak özellik konusunda yazılımcıdan daha bilgili oluyor.
  • İşin tam anlamıyla bitirilmesi için gerekli zamanı harcıyorlar. Ülkemizdeki gibi acelemiz var yetişmesi lazım diye test aşamasını atlama, dökümantasyonu es geçme gibi birşey kesinlikle olmuyor. Bir şey yapılacaksa ne kadar zaman alırsa alsın kuralına göre herşeyiyle birlikte yapılıyor.

8- Üniversiteyi içerisi, iş hayatını da dışarısı olarak kabul ederseniz,dısarıda yani sektörde işler nasıl yürüyor? Hala içeride bulunan oğrenciler için, dışarı çıkmadan önce neler yapmalarını tavsiye ederseniz?

Üniversite hayatında gerçek dünyada işler nasıl yürüyora dair çok az şeyler öğrenerek mezun oluyoruz. Dışarıda projeler büyüyor, zaman kısıtı içine giriyor, müşteriler , hergün değişen ve kullanılan farklı faklı birçok teknoloji vb. Listeyi uzatabiliriz. Kısacası işler dışarıda daha kompleks.
Yapılan projeler açısından dışarıda işler haliyle üniversitelerden daha büyük oluyor. Dolayısıyla üniversitede yazılan programlarda “maintainability” yani yönetilebilirlik çok fazla önemli olmuyor. Ama dışarıda projeler üniversiteye gore oldukça büyük olduğundan yazılım kalitesi oldukça önemli.Özellikle sonradan hatalar geldikçe yazılımı ne kadar iyi yazdıysanız size o kadar zamandan tasarruf sağlıyor. Bununda birlikte dışarıda da malesef işlerin çok amatör yürüdüğü birçok şirket var.

Üniversitede iken işin teorisini iyi öğrenmek size ileride problem çözmede çok yardımcı olacaktır. Belki iş hayatında birebir kullanılmasada problem çözmede işlerin arka planda nasıl yürüdüğünü bilmek oldukça ufkunuzu açıyor. Örnek olarak yukarıda da bahsettiğim temel garbadge collection algoritmasını bilmek, IE6 da nasıl uygulandığına bakmak Javascript yazarken memory leak problemlerini çözmeme çok yardımcı olmuştu.
Benim yeni mezun olarak arkadaşlar için tavsiyem asıl öğrenmenin dışarıda başladığını bilerek bu sektöre atılmalarıdır. Kendinizi sürekli güncel tutup yeni şeyler öğrenmezseniz bir sure sonra paslanıyorsunuz. O yüzden “Ben mezun oldum, olay burda bitti.” şeklinde sakın düşünmeyin asıl macera iş hayatında başlıyor. Zaten adım attığınızda bilmediğiniz ve öğrenilecek ne kadar çok şeyin olduğunu anlıyacaksınız. Yılmadan öğrenmeye ve çalışmaya devam ederseniz başarıya ulaşacaksınız. Çevremde sene olarak 10 seneden fazla tecrübesi olup yeni mezun arkadaşlar kadar gerçek tecrübesi olan birçok mühendisle çalıştım.Bu yüzden yeni mezun arkadaşlar eğer kendilerini okulda iyi yetiştirdiyse kendilerine güvenerek çok iyi işler yapabilirler. Önemli olan sürekli ÖĞRENMEK ve ÇALIŞMAK .

Diğer Yazılım Mühendisliği Roportajları

765qwerty765

Basit Bir Abstraction Örneği

Object Oriented tasarım ve programlamada soyutlamanın her zaman önemini vurgulamışımdır. Yazılım geliştirirken soyutlamalar sayesinde yazılımı daha yönetilebilir parçalara, modüllere ayırıp daha esnek yazılımlar geliştirebiliyoruz. Buna geliştirdiğim yazılımdan küçük bir örnek vermek istedim.

Geçenlerde Hibernate Log dosyalarını parse eden bir program yazdığımı burada belirtmiştim.  Program belirli bir dizideki .log uzantılı dosyaları parse edip içlerinden SQL cümlelerini çıkarıyordu. Ekranda bulunan bir butonun altında yazılan kodda seçilen log dizininin altındaki log dosyalarını alıp Parser sınıfına işlemesi için gönderiyordu. Kodun ilk yazılan şekli aşağıdaki gibiydi.

private void openFileActionPerformed(java.awt.event.ActionEvent evt) {
    try {        
       //log dosyalarını seçili dizinden alıyoruz

       File directory =fc.getSelectedFile();
       FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".log");
            }
        };
        File[] files =directory.listFiles(filter);

        //*****************
 
        for (File file : files) {
            Parser parser = new Parser(file);   
            sqlLogs = parser.parse();
            for (SqlLog sqlLog : sqlLogs) {
                listModel.addElement(sqlLog);
            }
        }

    } catch (Exception ex) {
        showMessage(ex.getMessage());
    }
}

Yukarıdaki basit bir işlem fakat metodun iki işi bir arada yaptığını fark ettiniz umarım. Log dosyalarını listeleme ve bu dosyaları parse etme. Ayrıca uygulamamı düşündüğümde şöyle bir ihtiyacım olduğunu anlıyorum.Uygulamam “Log dizinindeki dosyaları parse etme” işlemini yapıyor. Bu yüzden basit ama kodu ve yönetimi arttıran yeni bir sınıf ekliyorum. Aslında kısacası soyutlama yapıyorum. Uygulamam da daha önceden geçen “Log Dizini” dediğim kavram için bir sınıf oluşturuyorum. Kodu aşağıdaki gibi değiştirdim.

public class LogDirectory {
    private File directory;

    public LogDirectory(File directory) {
        this.directory = directory;
    }

    public File[] getLogFiles() {
        FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".log");
            }
        };
        return directory.listFiles(filter);
    }
}

Yukarıdaki kodda basit bir soyutlama yaptım. Log dizinindeki dosyaları getiren işi ayrı bir sınıf olarak uygulamaya ekledim.Metod aşağıdaki hale geldi.

private void openFileActionPerformed(java.awt.event.ActionEvent evt) {
    try {       
        LogDirectory logDirectory =new LogDirectory(fc.getSelectedFile());        
        File[] files = logDirectory.getLogFiles();

        for (File file : files) {
            Parser parser = new Parser(file);
            sqlLogs = parser.parse();
            for (SqlLog sqlLog : sqlLogs) {
                listModel.addElement(sqlLog);
            }
        }

    } catch (Exception ex) {
        showMessage(ex.getMessage());
    }
}

Küçükde olsa bu tarz soyutlamanın uygulama için oldukça önemli olduğunu düşünüyorum.

Affet bizi SOA seni yanlış anladık

Geçenlerde danışman olarak bulunduğum bir firmada kısa bir Desing Patterns,Principles sunumu yapıyordum. Sunumun asıl sebebi geliştirilen büyük çaplı Java projesinde sınıflarında ortalama  20.000, 30.000 satırlardan oluşan gerçekten büyük sınıflar olmasıydı.Ve sınıflar neredeyse sadece statik metodlardan oluşuyordu. Şuana kadar çalıştığım projelerde en fazla 5000’i görmüştüm orda 30.000 satırı görünce artık 5000,10.000 satır kod içeren sınıflara küçük sınıf gözüyle bakmaya başladım diyebilirim :)

Hal böyle olunca bu konuda firmaya bu sayının çok büyük bir rakam olduğunu başlarının ağrımaması için düşürülmesinin iyi olacağını belirttik. Onlarda bizden neler yapılabilir bunun hakkında bir sunum yapmamızı istediler. Bende büyük sınıfların düşmanı olan Design Patterns, S.O.L.I.D Principles konularını uygulamalı olarak anlatan bir sunum yaptım.Sunumdan sonra birkaç ilginç diyalog yaşadım o yüzden sizlerlede paylaşmak istedim.

Yazılımcı arkadaşlardan birkaç tanesi projelerinin Service Oriented olduğunu bu yüzden benim anlattığım bu tarz problemleri önleyecek Object Oriented desing ve prensiplerin projelerinde uygulanamayacağını belirttiler. Biraz şaşırdım diyebilirim aslında. Çünkü SOA ve OOP hakkında büyük bir yanlış anlaşılma olmuş.

SOA yani Service Oriented Architecture bir uygulama mimarisidir.SOA uygulamayı birbirinden bağımsız, belirli fonksiyonlara bölünmüş, tekrar kullanılabilir olacak şekilde servisler halinde tasarlamaktır. SOA size uygulamanızı şu teknolojilerle,şu şekilde yapacağınızı söylemez altında yatan teknolojiyi istediğiniz gibi seçebilirsiniz. İster RMI, ister Web Servisleri,ister WCF kullanarak geliştirin bu size kalmış.

Object Oriented uygulama geliştirirken kullandığını bir teknik fakat SOA üst seviye bir uygulama mimarisi. Service Oriented bir uygulama geliştirirkende ister Functional Programming kullanın, isterseniz Object Oriented Programming kullanın servislerin ne ile geliştirildiğinin hiç bir önemi yok.Yani herhangi bir programlama teknolojisi gibi Object Oriented teknikleri SOA uygulama geliştirirken rahatlıkla kullanabiliriz. Bu yüzden Object Oriented Programming/Design ile SOA’yı karşılaştırmak elma ile armudu karşılaştırmak gibi birşey olsa gerek. Bu yüzden üzülerek SOA’dan bizi affetmesini istiyorum ve birdaha elma ile armudu karıştırmayacağımıza söz veriyorum :)

Fluent Interface Örneği

Örnek Kodlar

Fluent Interface kavramı ile ilk olarak 2007 yılında  Martin Fowler’ın bu yazısını okuyunca tanışmıştım. Hatta örnekte gösterdiği

TimeInterval meetingTime = fiveOClock.until(sixOClock);

kodu beni oldukça etkilemişti. Tabi kısa bir an bir kendi yazdığım koda birde şekilde yazılan koda bakakaldım diyebilirim. Okunabilir kod deyip dururuz ya; bu kodu okuyunca “İşte okunabilir kod budur”  dediğim anlardan biridir.

Duymayanlar,bilmeyenler aslında çok fazla birşey kaçırmıyor.Fluent Interface kısaca kodun okunulabilirliğini artırmak için kullanılan bir API tasarım stili diyebiliriz. Ne tasarımınıza bir esneklik katıyor, ne de daha az kod yazmanızı sağlıyor. Amacı kodun okunulabilirliğini arttırmak ki, bence bunu da çok iyi yapıyor.

Kullandığınız birçok kütüphanede de farketmesenizde bu şekilde tasarlanmış API’ler ile karşılaştığınızı düşünüyorum. Mesela benim aklıma gelenler .NET’de kullandığım Rhino Mocks,Ninject.. Java’da kullandığım Nonblocking IO,EasyMock.. gibi birçok kütüphane bu stil ile tasarlanmış. Dikkat edecek olursanız bu kütüphanelerde yazdığınız kod aynı satırda sırayla birbirini çağıran metodlar şeklindedir.(method chaining).

İlk karşılaştığımda oldukça etkilendiğimi söylemiştim. Tabi ne zaman böyle etkilensem hemen kendi kodumda kullanmak için yer aramaya başlarım. Fakat ne kadar uğraşsamda çok fazla uygulayabileceğim yer bulamamıştım. Fluent Interface’i kodun heryerine uygulamak biraz zahmetli ve gereksiz olabiliyor. Genelde en uygun kullanım şeklinin sınıfların,kütüphanelerin konfigürasyon işlemleri olduğunu düşünüyorum.(Fluent NHibernate buna çok güzel bir örnek.).

Lafı fazla uzatmadan örneğimize dönelim.Geçenlerde geliştirdiğim bir kodda Fluent Interface şeklinde bir sınıf yazmanın oldukça faydasını gördüm sizinlede paylaşayım.

Genelde bazı uygulamalarda ekranda seçtiğiniz bir seçeneğe göre bazı alanların,girilmesi gereken kontrollerin enable,visible… olması, bazılarının ise o seçeneğe göre disable,hidden… olması gerekir. Benimde yaptığım örnek kısaca bu tarz bir uygulamaydı. ASP.NET Webforms ile geliştirilen bu uygulamada ekranda bulunan bir adet DropDownList kontrolü ile yapacağınız işlem türünü seçiyorunuz. Ardından o işlem türüne göre ekranda bazı alanlar enable, bazı alanlar disable oluyor. Vede seçtiğimiz türe göre veritabanından bazı kontrolleri dolduruyoruz,kısacası bazı işlemleride yapıyoruz.

Öncelikle akla ilk gelen yöntem seçtiğiniz kontrolün altında if-else kontrolü yapıp uygun seçeneğe göre uygun kontrolleri enable,disable edebiliriz. Fakat bu çözüm hem bu kodun diğer ekranlarda kullanılması, hemde esnekliği açısından hoş bir çözüm olmaz bu yüzden hemen aklımızdan çıkarıyoruz :)

Daha uygun olarak daha önceden bahsettiğim Table Driven Methods yöntemini kullanabiliriz. Bu şekilde if-else kontrollerinden kurtulup daha genel bir yapı tasarlamış oluruz ve bu yapıyı bu şekilde çalışan diğer ekranlarda da kullanabiliriz.

        private void ConfigureScreen()
        {
            screenState = new ScreenState();
            
            ControlState onay =new ControlState();
            onay.AddControlToEnable(pnlOnay);
            onay.AddControlToDisable(pnlItiraz);
            onay.AddControlToDisable(pnlYonlendirme);

            screenState.AddControlState(ONAY, onay);

            ControlState yonlendirme = new ControlState();
            yonlendirme.AddControlToEnable(pnlYonlendirme);
<div style="display: none"><a href='http://essay-service-best.com/'>illegal immigration essay</a></div>            yonlendirme.AddControlToDisable(pnlItiraz);
            yonlendirme.AddControlToDisable(pnlOnay);
            yonlendirme.Do(()=>BirimleriYukle());

            screenState.AddControlState(YONLENDIRME, yonlendirme);

            ControlState itiraz = new ControlState();
            itiraz.AddControlToEnable(pnlItiraz);
            itiraz.AddControlToDisable(pnlYonlendirme);
            itiraz.AddControlToDisable(pnlOnay);
            itiraz.Do(() => UrunleriYukle(false));

            screenState.AddControlState(ITIRAZ, itiraz);

            ControlState secim = new ControlState();
            secim.AddControlToDisable(pnlYonlendirme);
            secim.AddControlToDisable(pnlOnay);
            secim.AddControlToDisable(pnlItiraz);
            

            screenState.AddControlState(DEFAULT, secim);

        }

Her durum için bir ControlState sınıfı oluşturuyoruz. Ona enable,disable etmesi gereken kontrolleri veriyoruz,ardından yapması gereken işlemleri söylüyoruz. Ardından bu sınıfı ekranı temsil eden ScreenState sınıfına ekliyoruz.

ScreenState adındaki sınıfımız ekranın bütün durumlardaki durumunu simgeliyor. ControlState ise belirli bir seçim sırasındaki durumunu simgeliyor. Sınıfları şuanda çok fazla düşünmeyin. Yukarıdaki gördüğünüz koddaki okunabilirliği, ve kodun yazılım şekline dikkat etmenizi istiyorum.

Bu kodda herhangi bir problem yok gayet güzel çalışıyor. Fakat birde olaya biraz daha akıcılık katıp Fluent bir API ile yukarıdaki kodu tekrar yazalım bakalım hangisi gözümüze daha hoş gözükecek :)

        private void ConfigureScreen()
        {
            screenState = new ScreenState();

            screenState
                .WhenStateIs(ONAY)
                .EnableControls(pnlOnay)
                .DisableControls
                (
                    pnlItiraz,
                    pnlYonlendirme
                );

            screenState
                .WhenStateIs(YONLENDIRME)
                .EnableControls(pnlYonlendirme)
                .DisableControls
                (
                    pnlItiraz,
                    pnlOnay
                )
                .Do(() => BirimleriYukle());

            screenState
                .WhenStateIs(ITIRAZ)
                .EnableControls(pnlItiraz)
                .DisableControls
                (
                    pnlYonlendirme,
                    pnlOnay
                )
                .Do(() => UrunleriYukle(false));

            screenState
                .WhenStateIs(DEFAULT)
                .DisableControls
                (
                    pnlItiraz,
                    pnlOnay,
                    pnlYonlendirme
                );
        }

Yukarıdaki kodu ingilizce olarak okumaya çalışın. Mesela ikincisini sizin için ben okuyayım.Ekran durumu YONLENDIRME olduğunda pnlYonlendirme’yi enable et,pnlItiraz,pnlOnay disable et ve Birimleri yükle. Okunuşu diğerine göre daha akıcı değil mi? Peki okunuşu bizim için neden önemli?

Yukarıdaki kodda da gördüğünüz gibi Fluent Interface tekniği ile kodun okunulabilirliğini oldukça geliştirdik. Aslında küçük bir Internal DSL yaptık diyebiliriz. Uygulamamızın küçük bir bölümüne göre bir dil tasarladık yani.Fluent Interface olarak API geliştirmek için aslında çok değişik bir teknik yok. Metodların sonunda void yerine sınıfın kendisini döndürüyoruz. Bu şekilde noktadan sonra o sınıfın başka metodlarınıda çağırabiliyoruz. Özellikle sınıflara konfigürasyon yapılırken işimizi oldukça kolaylaştırdığını düşünüyorum.Fluent NHibernate bunun çok güzel bir örneği. XML konfigürasyon dosyalarından tamamen kurtulup Fluent Interface şeklinde NHibernate mapping konfigürasyonunu yapabiliyorsunuz.Fluent Interface tekniğinin aklınızın bir köşesinde bulunmasını tavsiye ederim.

Örnek Kod : Visual Studio Class, Table Generator Add-In

Visual Studio’da Add-In geliştirmeyi öğrenmek için küçük bir proje geliştireyim dedim. Projede basit olarak bir Solution Explorer’dan seçtiğiniz sınıfı Server Explorer üzerindeki veritabanına tablo olarak ekliyor.Ayrıca

Server Explorer üzerindeki eklenmiş veritabanı bağlantısı üzerinden seçtiğiniz tabloyu projenize sınıf olarak ekliyor.

Kaynak kodunu

Hit This dry ed medication matters a keeps Amazon http://www.backrentals.com/shap/cialis-free-samples.html only. Certain looking five http://www.mordellgardens.com/saha/sildenafil-citrate-tablet.html Sure moisturizering my it! Started http://www.creativetours-morocco.com/fers/viagra-from-india.html waterproof. Distribute That’s: the review http://augustasapartments.com/qhio/cialis-online-overnight-shipping went never volume “click here” hilobereans.com eyebrows active when this. Large http://augustasapartments.com/qhio/low-dose-cialis The, and seeing skin, this prescription viagra online inspected to mistake would, http://www.goprorestoration.com/ordering-viagra-online information Thank caramel total! cialis no prescription Hair only and side effects for cialis teddyromano.com managable you provided My usuage buy cialis vermontvocals.org they that usually days.

ve setup dosyalarını aşağıdan indirebilirsiniz. Proje Visual Studio üzerinde Add-In geliştirmek isteyenler için faydalı olabilir. Ayrıca projeyi küçük bir Test Driven Development ve OOP örneği olarak inceleyebilirsiniz yeni başlayanlar için faydalı olabilir..Gerçekte işinize yararsada kullanıp kodu istediğiniz gibi değiştirmekte serbestsiniz.İçerisinde bug,ve eklenmesi gereken özellik olabilir, kendiniz ekleyebilir veya benden eklememi isteyebilirsiniz :) .

Proje Visual Studio 2008 üzerinde çalışıyor. Eğer kodu kullanmak ve değiştirmek istiyorsanız bilgisayarınızda Visual Studio 2008 SDK kurulu olması gerekli.

Tablodan Sınıf Oluşturma

Adım 1 : Server Explorer’dan Çalıştığınız Veritabanı bağlantısını ekleyin ve ekledikten sonra sınıfını oluşturmak istediğiniz tabloyu seçin

TableToClassAdim1

Adım 2 : Gelen ekran üzerinden sınıfın otomatik olarak oluşturulmuş özellikleri üzerinde değişiklik yapın.Sınıf adını değiştirin, yeni özellik ekleyin,özellik silin,özellik adını güncelleyin,özellik tipini güncelleyin…

TableToClassAdim2

Sınıftan Tablo Oluşturun

Adım 1 :Solution Explorer üzerinde veritabanına tablo olarak eklemek istediğiniz sınıfın üzerinde sağ tıklayın.

ClassToTableAdim1

Adım 2: Gelen ekran üzerinden veritabanına eklenecek tablo özelliklerini otomatik olarak görüntüleyecektir. Oradan ekran üzerinde oluşturulacak tablonun primary key, allow null gibi özelliklerini değiştirin, kolon ekleyin, kolon silin, kolon adını değiştirin…

ClassToTableAdim2

Tell Don’t Ask Principle

oneRing Herşey prosedürel programlamayla başladı. Bazılarımız büyük programlar yazdı, bazılarımız ise küçük birer hesap makinası yaparak kıyısından geçiverdi :) Ama hepimiz prosedürel programlamayı çok sevdik. Programlarımızı yazarken güzel güzel yapılar(struct,record),veri tipleri(int,double,char…) kullanırdık, yapılarımızı,verilerimizi metodlarımıza parametre olarak gönderirdik onlarda gereken işi güzelce yapardı.Tabi sonradan yazdığımız programlar 100.000 satırlar boyutuna ulaşmaya başlayınca o masum masum kod dosyalarımızın üst tarafında tanımlı yapılar bize düşman görünmeye, en az 5 tane parametre alan fonksiyonlarımız da yönetilmeme ye ve pekde hoş gözükmemeye başladı. Ardından kulaklarımıza Mordor diyarından şu nağmeler gelmeye başladı. (Yüzüklerin efendisini çok sevdiğim belli oluyor herhalde:) )

One Object to rule them all, One Object to find them,
One Object to bring them all and in the Class bind them

Nağmelerde, hepsini ; yani veri ve fonksiyonları bir araya getirecek, ondan sonra onları Class(sınıf) içerisinde birleştirecek bir güç olan nesneden bahsediyordu. Hepimiz bu nağmelerin içerisnde olan nesnelerin büyüsüne kapıldık ve o aşamadan sonra kendimizi Object Oriented rüzgarının önünde buluverdik.  Artık hepimiz bu gücü kullanıp daha yönetilebilir, okunabilir, esnek yazılımlar geliştirmek için can atıyorduk. Fakat prosedürel programlamayı o kadar çok sevmiştik ki eski alışkanlıklarımızdan bir türlü vazgeçemiyorduk. İlk aşklar unutulmaz derler herhalde ondan olsa gerek :) Artık benim PObject Oriented olarak adlandırdığım nesneler ile prosedürel programlama yapıyorduk.

Her ikisinin sevdiğimiz özelliklerini kullanıp güzel güzel programlar yazmaya başlamıştık. Verileri bu sefer struct,record gibi yapılar içerisine değilde sınıflar içerisine koymaya başladık.Fonksiyonlarımızıda uygun bir yer bulabiliyorsak oraya değilse sağa,sola yerleştiriyorduk. Fakat yine işler projeler büyüdükçe çığırından çıkmaya başladı. Projeler büyüdükçe, değişiklikler yapılmaya başladıkça işler oldukça zahmetli olmaya başladı. Küçük bir talepteki değişiklik uygulamanın birçok yerinde değişiklik yapmamızı gerektiriyor ve bu değişiklik sonucunda ortaya çıkan hatalarla baş edememeye başlıyorduk. Aslında biraz da kafamız karışmıştı. Nağmelerde duyduğumuz o güçlü nesneye yönelik programlama aslında pekde güçlü olmadığını düşünüyorduk.

Kaptırmışım kendimi yüzüklerin efendisi falan araya girince hikayeyi fazla uzattım kusura bakmayın :) Aslında problem bu noktada geliştirdiğimiz uygulamada iş mantığının nesnelerin içinde değilde uygulamanın geneli içine dağılmış olması. Yani en temel nesneye yönelik programlama prensibi verilerin ve verileri işleyen metodların bir arada bulunması gerçekleşmediğinden dolayı uygulama geneline artan Bağımlılık(Dependency,High Coupling) sorunlarının yol açtığı problemlerdir.

Bu sorunların önüne geçebilmek ve sınıflara sorumluluğu daha düzgün atayıp, iş mantığını gerçekten olması gereken yere koymak için kullanılan prensiplerden biride Tell Don’t Ask Principle yani “Sorma, söyle” prensibidir.

Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.
— Alec Sharp

Alec Sharp’ın dediği gibi prosedürel kod veriyi alır ve o veri üzerinden kararlar verir. Object oriented kod ise nesnelere yapmaları gereken şeyleri söyler. Bunu meşhur gazeteci çocuk, cüzdan problemi üzerinde örneklerle inceleyelim. İlk olarak nerede okumuştum hatırlamıyorum ama bu prensibi anlamama kod ve kavramsal olarak oldukça yardımcı olmuştu. Aynı örnek üzerinden devam edelim.

Örnek

Kodları buradan indirebilirsiniz : TellDontAsk.rar

Gazeteci arkadaşımız her sabah bisikletine atlayıp ev ev dolaşıp gazete dağıtıyor. Haftalıkta müşterilerinden gazete paralarını topluyor.Para toplayacağı müşterilerin listesi elinde alıp kapı kapı dolaşıyor ve ücretleri alıyor. Ardından topladığı hasılatı patronuna teslim ediyor.İlk olarak programımızı şu şekilde yazalım.

public class Cuzdan {
    private double para;

    Cuzdan(double para) {
        this.para =para;
    }

    public double getPara() {
        return para;
    }

    public void setPara(double para) {
        this.para = para;
    }  
}
public class Gazeteci {
    private double hasilat =0;
    public void odemeAl(Musteri musteri,double miktar){
        if(musteri.getCuzdan().getPara()<miktar){
            throw new RuntimeException("Cüzdanki para yeterli değil.");
        }else{
            musteri.getCuzdan().setPara(musteri.getCuzdan().getPara()-miktar);
            hasilat +=miktar;
        }        
    }

    public double getHasilat() {
        return hasilat;
    }
}
public class Musteri {
    private String adi;
    private Cuzdan cuzdan;

    public Musteri(String adi,double para) {
        this.cuzdan =new Cuzdan(para);
        this.adi =adi;
    }    

    public Cuzdan getCuzdan() {
        return cuzdan;
    }

    public String getAdi() {
        return adi;
    }
}
public class Main {
    public static void main(String[] args) {
        Musteri ahmetAmca =new Musteri("Ahmet",100);
        Musteri bakkalMehmet =new Musteri("Mehmet",50);
        Musteri kasapAli =new Musteri("Ali",80);
        
        List<Musteri> musteriler =new ArrayList<Musteri>();
        musteriler.add(ahmetAmca);
        musteriler.add(bakkalMehmet);
        musteriler.add(kasapAli);
        
        Gazeteci cihat =new Gazeteci();
        
        for(int i=0; i<musteriler.size(); i++){
            Musteri musteri =musteriler.get(i);
            try{
                cihat.odemeAl(musteri, 60);
            }
            catch(Exception ex){
                System.out.println("Tahsilat yapılamadı! Müşteri adı : "+musteri.getAdi());
            }
        }        
        System.out.println("Toplanan hasılat miktarı : "+cihat.getHasilat());
    }
}

Prosedürel stili o kadar çok seviyoruz ki muhtemelen yukarıdaki kodda pek bir problem göremeyeceğiz.Yanılmıyorum değilmi?Çünkü ilk başlarda bu tarz kodlar bana süper Object Oriented kodlar gibi geliyordu ve açıkçası biri bana o zaman bu kodda problem var dese gülerdim :)

Probleme teknik olarak değilde gerçek hayat problemi olarak bakalım. Evinize gelen gazeteciye cüzdanınızı verip içerisinden ne kadar lazımsa al kardeş dermisiniz? Eğer öyleyse sizin gazeteciniz olmayı isterdim :) Çoğumuzun cüzdanını vermeyeceğini düşünüyorum açıkcası.Gazeteciye “kardeş ne kadar lazım söyle” diye sorup ona göre parayı cüzdanımızdan çıkarıp öyle veririz. Bu koddaki problemde bu. Gazeteciye aslında cüzdan nesnesi ile cüzdanımızı veriyoruz gazeteci önce bi içerisine bakıyor gerekli para varmı ardından varsa içerisinden gereken parayı alıyor.

Tell Don’t Ask(sorma, söyle) presibinin belirttiği gibi aslında biz burada Musteri ve Cuzdan nesnelerine surekli sorular soruyoruz ve ardından sorduğumuz soruların cevabına göre bazı işlemleri o nesneler yerine biz yapıyoruz. Musteriye sordugumuz soru getCuzdan(), Cuzdan nesnesine sordugumuz soru ise getMiktar() metodları.Bu yüzden Musteri nesnesinin iç yapısını öğrenmiş oluyoruz.Mesela müşterinin parayı cüzdanında taşıdığını biliyoruz. Ya müşteri bize parasını evdeki kasasından vermek isterse?Gazeteciye tutup “Al kasayı kardeş içerisinden ne kadar lazımsa o kadar al ” mı diyeceğiz? :) Dolayısıyla nesneler arasındaki bağımlılık(coupling) artıyor. Uygulamada bu şekilde müşteri nesnesinin içerisindeki alanlara soru sorup ona göre karar verip çeşitli işlem yaptıran sınıflar ne kadar artarsa müşterinin iç yapısını değiştirmemiz o kadar zorlaşıyor. Örnek olarak müşterinin artık parayı Cüzdan’dan değilde kredi kartından vereceğini düşünelim. Hangi sınıfları değiştirmemiz gerekir onlara bakalım.

Şimdi yeni sınıfımız Kredi kartına göre sınıflarımızın düzeltilmiş halini yazalım. Bakalım bir sınıfdaki değişiklik yüzünden kaç sınıfı değiştirmek zorunda kalacağız.Ana sınıf değişmediği için onu yazmıyorum. Diğer değişen sınıfları yeni halleri ile aşağıya yazıyorum.

public class Gazeteci {
    private double hasilat =0;
    public void odemeAl(Musteri musteri,double miktar){
        if(musteri.getKrediKarti().getLimit()<miktar){
            throw new RuntimeException("Cüzdanki para yeterli değil.");
        }else{
            musteri.getKrediKarti().setLimit(musteri.getKrediKarti().getLimit()-miktar);
            hasilat +=miktar;
        }        
    }

    public double getHasilat() {
        return hasilat;
    }
}
public class Musteri {
    private String adi;
    private KrediKarti krediKarti;

    public Musteri(String adi,double para) {
        this.krediKarti =new KrediKarti();
        this.adi =adi;
    }    

    public KrediKarti getKrediKarti() {
        return krediKarti;
    }

    public String getAdi() {
        return adi;
    }
}
public class KrediKarti {
    private double limit =1000;

    KrediKarti() {
        
    }

    public double getLimit() {
        return limit;
    }

    public void setLimit(double limit) {
        this.limit = limit;
    }  
}

Gördüğünüz gibi Main sınıfı hariç bütün sınıfları basit bir değişiklik yüzünden değiştirmek zorunda kaldık. Burada aslında sadece Müşteri yani cüzdana sahip sınıfın etkilenmesi gerekirdi fakat prosedürel stilde sürekli nesnelerin iç yapısını sorgulayarak ona karar veren kodumuz yüzünden Gazeteci sınıfıda bu değişiklikten etkilendi.

Peki bu problemi nasıl düzeltebiliriz ? Tell Don’t Ask yani işimizi nesnelere soru sorararak değilde onlara ne yapması gerektiğini söyleyerek yapabiliriz. Aslında bu yeni birşey değil OOP’nin en temel kuralını kullanıyor alıyor yani Encapsulation. Nesnelerin iç yapısını çok fazla dışarı açtığımızda kendi üzerine düşen işleri başkaları yaptığında bu tarz problemler kaçınılmaz. Bu küçük bir örnek belki çok fazla problem olmaz fakat büyük bir uygulamada bu tarz iç yapısını dışarı açan bir nesnede değişiklik olduğunu düşünün? Belkide yüzlerce sınıfı değiştirmek,hatalarını düzeltmek,test etmek zorunda kalacaksınız. Şimdi bu kodu Tell Don’t ask prensibine uygun olarak aşağıdaki gibi yazalım.

public class Cuzdan {
    private double para;

    Cuzdan(double para) {
        this.para =para;
    }

    double cek(double fiyat) {
        if(fiyat>para)
          throw new RuntimeException("Cüzdanki para yeterli değil!");
        else{
            para =para-fiyat;
            return fiyat;
        }
    }
}
public class Musteri {
    private String adi;
    private Cuzdan cuzdan;

    public Musteri(String adi,double para) {
        this.cuzdan =new Cuzdan(para);
        this.adi =adi;
    }    
    
    public double odemeYap(double fiyat){
        return cuzdan.ver(fiyat);
    }

    public String getAdi() {
        return adi;
    }
}
public class Gazeteci {
    private double hasilat =0;
    public void odemeAl(Musteri musteri,double miktar){
        hasilat +=musteri.odemeYap(miktar);     
    }

    public double getHasilat() {
        return hasilat;
    }
}

Şimdi Tell Don’t Ask prensibine göre ile yazılmış yeni kodumuzu inceleyelim. Gazeteci sınıfına bakın artık müşterinin ne cüzdnından haberi var nede kredi kartından. Ona sadece yapması gereken şeyi söylüyor : “Bana şu kadar ödeme yap”. Müşteri nesneside aynı şeyi yapıyor cüzdana bana şu kadar para ver diyor. Ardından cüzdan da kendi içerisinde olan parayı istenen miktarla karşılaştırıyor ve ona göre veri parayı veriyor. Yani herkes yapması gereken işi yapıyor. Artık cüzdan nesnesi içerisinde sadece veri bulunan bir aptal sınıf yada Anemic Domain Model değil, onu nasıl işleyeceğini bilen bir sınıf.

Şimdi bu yapıda yazılmış bir kod üzerinde Cuzdan yerine paramızı kredi kartı üzerinden ödemek için değişmesi gereken sınıfları tekrar yazalım.

public class Musteri {
    private String adi;
    private KrediKarti krediKarti;

    public Musteri(String adi,double para) {
        this.krediKarti =new KrediKarti();
        this.adi =adi;
    }    
    
    public double odemeYap(double fiyat){
        return krediKarti.cek(fiyat);
    }

    public String getAdi() {
        return adi;
    }
}
public class KrediKarti {
    private double limit =1000;

    KrediKarti() {
        
    } 
    
    double cek(double fiyat) {
        if(fiyat>limit)
          throw new RuntimeException("Limit yeterli değil!");
        else{
            limit =limit-fiyat;
            return fiyat;
        }
    }
}
public class Gazeteci {
    private double hasilat =0;
    public void odemeAl(Musteri musteri,double miktar){
        hasilat +=musteri.odemeYap(miktar);     
    }

    public double getHasilat() {
        return hasilat;
    }
}

Gazeteci sınıfında yukarıda gördüğünüz gibi hiçbir değişiklik yapmadık. Yani sadece iç yapısını değiştirdiğimiz sınıflar etkilendi. O da müşteri sınıfı ama diğer şekilde hem gazeteci hemde müşteri sınıfı etkileniyordu.

Tabi bunun dışında gördüğünüz gibi kodun okunulabilirliği ve yönetimi oldukça kolaylaştı. Çünkü herkes kendi sorumluluğunu yerine getiriyor, nesneler kendi içerisindeki veriler üzerinde işlem yapıyor. Bu şekilde uygulamada değişiklik sadece gereken yerde yapılmış oluyor. Ama diğer şekilde iş mantığı uygulamanın bütün yerine dağılmış olduğu için değişiklik sonucunda birsürü yerde değişiklik yapmamız gerekiyor. Buda hata riskini ve değişim maliyetini oldukça arttırıyor.

İlk başlarda bu prensibi okuduğumda oldukça şaşırmıştım ve açıkçası nesneye yönelik programlamanın en temel ilkesini o zaman anlamıştım. Veri ve veri üzerinde işlem yapan metodların bir arada olması. Prosedürel mantığa o kadar alışmışızki nesneye yönelik bir programlama dilinde onun etkisinden hala kurtulamıyoruz. Bu prensip anlaması ve alışması belkide en zor temel nesneye yönelik programlama prensiplerinden birisi. Ben bile hala bazı yerlerde içgüdüsel olarak nesnelere soru sorup bazı işleri yaptırmaktan kendimi alıkoyamıyorum. Ama sürekli kodu gözden geçirip “Acaba bu işi bu sınıf mı yapmalı?, Bu işin ait olduğu yer burası mı?” diye sorgularsanız sorumluluğun gerçekten ait olduğu yeri belirleyebilirsiniz. Eğer nesnelerinizde sadece getter,setter(java) yada property(C#) bulunuyorsa nesnelerinizden şüphelenmenizin ve tekrar gözden geçirmenizin vakti gelmiştir.

Ortak Düşmanımız : Bağımlılık (Dependency,High Coupling)

domino1

Bu yazımızda yazılım geliştirirken karşımıza çıkan en büyük problemlerden biri olan bağımlılık (Dependency,Coupling) konularına değineceğim. Neden az olanı makbüldür onu anlatmaya çalışacağım.

Yazılım geliştirirken gerçek dünyadaki gibi bağımlılık (Dependency,Coupling) başımıza çoğu zaman dert açıyor.Bağlanılması zararlı bir şeye bağlandınızmı nasıl ondan kurtulmak onsuz birşey yapmak imkansız oluyorsa aynısı yazılım geliştirirkende geçerli oluyor. Yazılımda bağımlılığı tanımyacak olursak iki metodun,sınıfın,modülün bir işi gerçekleştirmek için birbirine ihtiyaç duymaları kısacası birbirlerini kullanmalarıdır diyebiliriz.

Peki bağımlılığın ne gibi zararları var neden düşman ilan ettik birazda ondan bahsedelim. Öncelikle iki sınıfın birbirine bağımlı olmasının en büyük problemlerinden biri herhangi birindeki değişikliğin diğerini etkilemesidir bunu yandaki domino taşlarına benzetebilirsiniz. Herhangi bir yerden taşın birini düşürdüğünüz zaman birbiri ardına olan bütün taşlar devrilmeye başlar. Aynı şekilde geliştirdiğimiz yazılımda sınıflar,modüller… arasındaki bağımlılık ne kadar fazla ise domino taşı gibi herhangi birinde meydana çıkan hataların,değişikliklerin bağımlı olan diğer sınıfları etkileme olasılığı o kadar artıyor.

Üstadlardan Robert Martin amcamızın internet üzerinde dinlediğim The Principles Of Agile Design konferansında nesneye yönelik tasarım(Object Oriented Design) nedir diye izleyicilere bir soru yöneltiyordu. İzleyiciler “Gerçek dünyayı modelleme”,”Kavramların ayrılması” .. gibi değişik cevaplar verdi. Fakat Robert Martin kısa ve öz olarak nesneye yönelik tasarımı şu şekilde özetledi “Bağımlılığı yönetmek(managing dependency)”. Gerçekten çok yerinde bir tespit nesneye yönelik tasarımın başarısı için uygulamada bağımlılığın iyi yönetilmesi ve mümkün olduğunca az olması gerekiyor. 

Aslında bağımlılığı hem kod üzerinde, hemde projeler üzerinde izlemek ve anlamak çok kolay. Bunun için JDepend,NDepend gibi birçok gelişmiş olsada basit olarak bağımlılığı ölçmek için iki yöntem kullanabilirsiniz. Mesela aşağıdaki gibi bir koda bakalım ve nelere bağımlı gözden geçirelim.

		private void tbiMesajiGonder_Click(object sender, System.EventArgs e)
		{
			Database.VeriTabani clsDatabase=new SMSNET.Database.VeriTabani();
			string strSQLTel="SELECT ID,Adi+' '+Soyadi AS AdSoyadi,GSM FROM Kisiler WHERE ID IN ("+strMesajGonderilecekIDs+")";
			DataTable dtTel=clsDatabase.GetDataTable("Telefon",strSQLTel);
			PosterClass clsPosterClass=new PosterClass();
			string strSQLParametre="SELECT * FROM Parametreler WHERE ID IN (1,2,3,4) ORDER BY ID";
			DataTable dtParametreler=clsDatabase.GetDataTable("Parametreler",strSQLParametre);

			string strFirmCode="",strUserName="",strPassword="",strOriginator="";
			string strErrDefinition="";
			int iErrCode=0;
			string strMSGID="";

			if(dtParametreler.Rows.Count==4)
			{
				strFirmCode=dtParametreler.Rows[0]["Degeri"].ToString();
				strUserName=dtParametreler.Rows[1]["Degeri"].ToString();
				strPassword=dtParametreler.Rows[2]["Degeri"].ToString();
				strOriginator=dtParametreler.Rows[3]["Degeri"].ToString();
			}
			else
			{
				MessageBox.Show("Sistem SMS gönderebilmek için gerekli parametrelere ulaşamadı. Lütfen Sistem Tanımlarınızı kontrol ediniz...");
			}

			int k=0;

			for(int i=0;i<dtTel.Rows.Count;i++)
			{
				if(dtTel.Rows[i]["GSM"].ToString()!=""&&dtTel.Rows[i]["GSM"].ToString()!=null)
				{
					clsPosterClass.AddToSmsBasket(tbSMSMesaji.Text,dtTel.Rows[i]["GSM"].ToString());
					k++;
					if(k>100)
					{
						strMSGID=clsPosterClass.sendSms(strFirmCode,strUserName,strPassword,strOriginator,null,ref iErrCode,ref strErrDefinition);
						clsPosterClass.ClearSmsBasket();
					}					
				}
			}
			strMSGID=clsPosterClass.sendSms(strFirmCode,strUserName,strPassword,strOriginator,null,ref iErrCode,ref strErrDefinition);
			clsPosterClass.ClearSmsBasket();
		}

Şimdi yukarıdaki kod baktığımızda pek de iyi şeyler görmediğimiz belli oluyor galiba. Kodu yaklaşık 4 sene önce ben yazmama rağmen pek birşey anlamıyorum açıkcası :) Muhakkak yukarıdaki kodda birsürü problem var fakat diğer problemlerinde büyük oranda sebebi olan en büyük problemlerden birisi kodun birçok sınıfa bağımlı olması(High Coupling).

Şimdi kodun nelere bağımlı olduğuna bakalım.Dediğim gibi bunu anlamanın çok basit bir yolu var bu da kodun çalışması için gerekli olan modüllerin,sınıfların, metodların sayılması. Bunu yapabilmek için öncelikle kodun Import kısmına bakmanızı tavsiye ederim. Import kısmında olan modüllere zaten direk olarak bağımlıdır. Projeler arasındaki bağımlılığı ölçmek içinde referans verdiği Dll,Jar gibi kütüphaneleri sayarak anlayabilirsiniz.Sınıflara olan bağımlılığıda o sınıf çıktığında kod çalışmaya devam edecek mi oradan anlayabilirsiniz.

  • Yukarıdaki koda baktığımızda ilk satırlarda Database nesnesi üzerinden SMS atılacak kişilerin SQL ile alındığını görüyorsunuz.Kodumuz Database’e bağımlı.
  • DataTable nesnesi üzerinden Telefonlar alınıyor. Kodumuz DataTable nesnesine yani arkaplanda olan ADO.NET teknolojisine bağımlı
  • 3. parti PosterClass nesnesi üzerinden SMS gönderimi yapılıyor. Yani PorterClass nesnesine ve bunu satın aldığımız firmaya bağımlıyız.

Şimdi bu bağımlılığın yol açtığı problemler neler onlara bakalım.

  • SMS sistemimiz sadece veritabanı üzerinden çalışacak. Mesela oldukça yaygın olan XML üzerinden aldığımız kayıtlara SMS göndermek istediğimizde bunu kodu değiştirmeden yapamayacağız.
  • Kodumuz DataTable yani Microsoft’ın sık sık değiştirmeyi sevdiği veri erişim katmanı olan ADO.NET alt yapısına bağlı. Mesela LINQ to SQL işimizi baya kolaylaştırırdı fakat onuda kodumuzu değiştirmeden kullanamayacağız.
  • Bir firmadan satın aldığımız PosterClass üzerinden SMS gönderiyoruz. Bunda bir problem yok fakat yarın birgün firma bu kütüphaneye destek vermediğinde ya da daha iyi alternatifler çıktığında yine değişik alternatifleri kullanmak hiçde kolay olmayacak.

Yukarıdaki kodda modüller, sınıflar arasındaki bağımlılığı gördük. Fakat bağımlılık bu kadarla bitmiyor açıkcası. Birazda daha sinsi olan metodlar,alanlar üzerindeki bağımlılığa bakalım. Aşağıdaki kodu inceleyelim

    public class Field
    {
        private string name;
        private bool allowNulls;
        private bool isPrimaryKey;
        public string DataType { get; set; }        

        public Field(string name, string dataType)
        {
            this.name = name;
            DataType = dataType;
            IsPrimaryKey = false;
            AllowNulls = true;
        }
        
        public bool IsPrimaryKey
        {
            get { return isPrimaryKey; }
            set {isPrimaryKey = value;}
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        
        public bool AllowNulls
        {
            get { return allowNulls; }
            set { allowNulls = value; }
        }
    }

class Program
{
    public void SaveFields()
    {
        IList<Field> fields=new List<Field>();
        for (int i = 0; i < fields.Count; i++)
        {
            Field field = fields[i];
            string sqlString = GetFieldAsSQLString(field);
            //....
        }
    }

    public string GetFieldAsSQLString(Field field)
    {
        string sqlString = field.Name + " " + field.DataType;
        if (field.IsPrimaryKey)
            sqlString += " PRIMARY KEY";
        if (field.AllowNulls)
            sqlString += " NULL";
        else
            sqlString += " NOT NULL";
        return sqlString;
    }
}

Yukarıdaki koda baktığımızda ilk başta çok fazla problem görmeyebilirsiniz fakat Field sınıfının içerisindeki alanların kodun çoğu yerinde bu şekilde kullanıldığında kodumuz bu sefer de Field sınıfının alanlarına bağımlı oluyor. Mesela GetFieldAsSQLString metoduna baktığımızda Field sınıfının alanları üzerinde bazı işlemler yapıp bir string oluşturuyor. Şunu düşünün DataType alanı artık string olarak değilde bir sınıf olarak tutacağımızı düşünün. Kodumuzun çoğu yeri GetFieldAsSQLString metodu gibi Field sınıfının alanlarını bu şekilde kullanıyorsa DataType alanını değiştirdiğimizde birçok yerde hata verecek ve kodun çoğu değişmek zorunda kalacak.

Kısaca bağımlılığın zararlarını sıralayacak olursak :

  • Üzerinde çalışan yazılım geliştiricilerin,özellikle benim gibi şuanda şanssız kesimden olan yani kodu kendi yazmayıp yönetmek zorunda olanların sistemin bütününü anlamak zorunda olmalarıdır. Çünkü bütün sınıflar,modüller birbirine bağımlıdır.
  • Yapmak zorunda olduğunuz değişikliklerin birçok sınıfı etkilemesi dolayısıyla daha uzun sürmesi vede ne kadar süreceğinin tahmin edilememesi. Herhalde ne demek istediğimi anladınız. Proje yöneticiniz gelip size bu değişiklik ne kadar sürer dediğinde çoğu zaman “Biryerde bir problem çıkmazsa şu kadar sürer..” dediğinizi hatırlıyorsunuzdur.Çoğu zamanda eğer bağımlılık fazla ise biryerde birşeyler çıkıp işinizi uzatacığına emin olun :)
  • Değişen teknolojiye adaptasyon,farklı alternatifler arasında geçiş hiçte kolay olmayacaktır. Çünkü kodun heryeri o teknolojiye bulaşmış durumdadır.

Nesnelerin birbirleri ile iletişim halinde görevlerini yerine getirmelerinden dolayı sıfır bağımlılık genelde mümkün olmasada bağımlılığın az olması çoğu durumda daha makbüldür.Bu yüzden bağımlılığı iyi bir şekilde yönetmek hem kalite yönünden,hemde zaman bakımından bize oldukça kazanç sağlayacaktır.

Peki bunları niye yazdım? :) Aşırı bağımlılığın çoğu durumda kötü olduğunu size ispatlayabilmek için :) Umarım sizinde kafanıza yatmıştır. Yukarıdaki kodların bağımlılığı azaltılmış şekilde nasıl yazarıza şuanda değinmeyeceğim çünkü  yazdığım ve yazacağım birçok konu bununla alakalı aslında. Dependency Inversion,Dependency Injection… birçoğunun adını duymuştursunuz. Bundan sonraki birkaç yazımda bağımlılığı azaltan, kodun kalitesini arttıran işlerimizi kolaylaştıran birkaç yönteme değineceğim.O yüzden bu kodlar işimize yarıyacak gibi :)

Premature Optimization

Erken performans optimizasyonunun yol açtığı belalar yazılım dünyasında oldukça iyi biliniyor. Bu konuda daha önceden kendi yaşadıklarımı da burada biraz aktarmıştım. Bugün internette aşağıdaki resme rastladım oldukça hoşuma gitti o yüzden burayada koymak istedim.

premature-6

Resimdeki şeytanmı yoksa başka birşeymi bizi sürekli bunu yapmamız için dürtüyor bilmiyorum ama ne ise ona aldanmayın derim :)

Composition Over Inheritance (Kalıtım yerine Birleşim)

Uzun zamandır işlerin yoğunluğundan dolayı yazamıyordum ama telafisi olarak düşündüğüm Object Oriented programlama açısından hayati konulara değinerek kendimi affettireceğimi umuyorum :)

Şimdi neden önemlidir ondan biraz bahsedelim. Öncelikle Object Oriented programlama dillerinde kodun tekrar kullanımını(code reuse) iki şekilde sağlayabilirsiniz. Birincisi inheritance(kalıtım) ikincisi ise Composition(güzel bir türkçe isim bulana kadar “birleşim” :) ) yöntemidir. Şimdi bu iki kullanımın neler olduğuna bakalım. Önce UML sınıf diyagramı olarak aralarındaki farka bakalım.
Devamını Burudan Okuyabilirsiniz

Sınıf Tasarımında Kontrol Edilecekler

Bu günlerde iki kere okunması gereken kitaplardan Code Complete 2‘yi tekrar gözden geçiriyorum.Kitabın Chapter 6.6’da bulunan kaliteli sınıf tasarımı için kontrol edilmesi gerekenler başlığı altında yazdıklarını buraya yazmadan geçemedim. Sınıflarımızı tasarlarken aşağıdaki maddelere dikkat etmemiz sınıfların ve yazılımın kalitesini oldukça arttıracaktır. Türkçeye çevirerek yazıyorum.Aşağıdaki listede sınıfın arayüzü(class interface) kavramını java ya da C# daki interface kavramları ile karıştırmayın. Sınıfın arayüzü derken dışarıya kullanıma açtığı metodları kast edilir.Mesela bir sınıfın 4 metodu varsa bunlardan 3’ü private 1’i public ise sınıfın arayüzünü bu dışarıya açılan public metod temsil eder.

Soyut Veri Tipleri

    Programınızdaki soyut veri tiplerinin neler olduğunu düşünüp bunların kullanım açısından arayüzlerini değerlendirdiniz mi?

Soyutlama

  • Sınıfın belirli bir amacı var mı?
  • Sınıf iyi isimlendirilmiş mi ve ismi asıl amacını tanımlıyor mu?
  • Sınıfın kullanım arayüzü iyi bir soyutlama sunuyor mu?
  • Sınıfın arayüzü onun nasıl kullanılacağını belli ediyor mu?
  • Sınıfın arayüzüne baktığınızda içinin nasıl implemente edildiğini düşündürmeyecek kadar iyi soyutlanmış mı?Sınıfı bir kara kutu(black box) olarak düşünebilirmisiniz?
  • Sınıflar kendi iç yapılarını diğer sınıfların ulaşmasına izin vermeyecek şekilde iyi koruyor mu?
  • Sınıflardan amaçları ile alakalı olmayan bilgiler çıkartılmış mı?
  • Sınıfı başka sınıflar,bileşenlere ayırmayı düşündünüz mü?Sınıfları sadece tek sorumluluğu yerine getirecek kadar fazla ayırdınız mı?
  • Sınıfın içini değiştirirken sınıf arayüzünün değişmemesine özen gösteriyormusunuz?

Encapsulation

  • Kalıtım(inheritance) sadece IS-A(dır,dir..) ilişkileri için kullanılıyor mu?Kalıtım yapan sınıflar Liskov Substitution Principle’a uyuyormu?
  • Sınıf dökümantasyonu kalıtımın nasıl yapılacağını tanımlıyor mu?
  • Diğer sınıflardan kalıtım yapan sınıflar üzerine yazılamayacak metodların(non overridable) üzerine yazmaktan kaçınıyor mu?
  • Kalıtım yapısı fazla derin değil mi?(Derin olmaması daha iyi)
  • Kalıtım yapılan temel sınıflardaki bütün field lar private mı?

Diğer Konular

  • Sınıf en fazla 7 veya daha az field içeriyor mu?
  • Sınıf diğer sınıfların metodlarını minumum sayıda çağırıyor mu?
  • Sınıf diğer sınıflar ile sadece gerçekten gerektiği anda iletişime geçiyormu?
  • Sınıfın bütün elemanları yapıcı metodlarda oluşturuluyormu?

Aslında her madde ayrı ayrı bir yazı başlığı altında incelenebilir yeri geldikçe bunlar ile alakalı yazmaya çalışacağım. Fakat liste iyi sınıf tasarımı için yapılması gereken çoğu şeyi içeriyor. Sınıflarınızı listeyi kontrol ederek tekrar gözden geçirmenizde fayda var.