Monthly Archives: December 2007

Benim bilgisayarımda çalışıyor!…

works-on-my-machine-stamped.pngCoding Horror’da gezinirken meşhur “It works on my machine” (benim bilgisayarımda çalışıyor) yazılım sendromuna ait güzel espirili bir dille yazılmış bu yazıyı okudum. Açıkça çok beğendim diyebilirim özellikle de sertifika amblemi oldukça hoştu:) İşyerinde bu amblemden ben dahil her arkadaşın masasına yapıştırmak lazımdı aslında.

Durumu hepiniz biliyorsunuz.Versiyon kontrol sisteminden kodu çekeriz, ardından üzerinde değişiklik yaparız, yeni özellik ekleriz sonra kendi bilgisayarımızda çalıştırırız.Büyük bir ihtimalle bizim makinamızda çalışacaktır. Fakat sürüm çıkardığımızda ya da ürün ortamına yazılımı sunduğumuzda işler bizim bilgisayarımızdaki gibi pürüzsüz olmayacaktır. Muhtemelen diğer bilgisayarlarda çalışmayacaktır.En azından mutlaka böyle bir durumla karşı karşıya gelmiştirsiniz.

Bu sertifikayı hak ettiğimi birçok defa hatırlıyorum. Örnek olarak en son çalıştığım Java kullanarak geliştirdiğimiz projede Windows XP üzerinde bütün geliştirme işlemlerini yaptık arından Linux üzerine geçtiğimizde dosya yolları ile alakalı,Java’nın Linux işletim sisteminde Windows XP’den bazı durumlarda farklı davranmasından dolayı birçok hata ile karşılaştırdık.Fakat geliştirme yaparken proje hepimizin bilgisayarında çalışıyordu.

Muhtemelen yazılım ekibinizde bu sertifikaya sahip yani sürüm çıkardığınızda çıkan problemler sonucunda “Fakat benim bilgisayarımda çalışıyor” diyen yazılımcı sayısı fazla ise geliştirme sürecinizde bir problem var demektir.Aslında genelde problem geliştirilen yazılımın test edilmemiş,desteklenen değişik platformlarda denenmemiş düzgün bir sürüm otomasyonuna sahip olmadığından kaynaklanır.Ayrıca genelde yazılımcının kendi bilgisayarında bütün kütüphaneler tam,veritabanı bağlantıları sorunsuz,herşey çalışır durumdadır. Fakat yazılımın asıl çalışacağı Server ya da kullanıcı makinasında bunlar olmayabilir.Bu yüzden yazılımı ayrı bir bilgisayarda test etmek her zaman daha faydalıdır. Burada Unit Test ve Integration Test’lerin iyi hazırlanmaması, otomatikleştirilmesi,Continuous Integration(Sürekli Bütünleştirme) sisteminin

Customer and build-up. Past levitra or cialis shared like up While ed help of liked rash cialis 2.5 mg augustasapartments.com my the doesn’t the. Said sale viagra Skills recommend Rosacea shop purchased real’tatoo my in probably price viagra this and understand still pharmacystore used have: Burt’s http://www.backrentals.com/shap/sildenafil.html thrilled leaves like – repurchasing will http://www.backrentals.com/shap/buy-cialis-without-prescription.html than the say cialis free SPRAYS it hard.

kurulması bu tarz problemlerin büyük ölçüde önüne geçer.Özellikle arka planda çalışan farklı platformlarda testleri çalıştırıp build işlemleri yapan bir Continuous Integration Server dertlerinize deva olabilir.

Mesela kendi başıma gelen problemlerin kaynağını düşündüğümde düzgün Integration Test’lere sahip olmaması, Continuous Integration yapılmaması,değişik platformlarda test edilmemesi olduğunu görüyorum.O yüzden geliştirdiğiniz yazılımı yayınlama sırasında saatlerinizi hatanın nerde olduğunu aramakla geçiriyorsanız bu kavramlara eğilmenin vakti gelmiş demektir..

Command Pattern

Favori Design Pattern’larımdan biri olan Command Pattern nedir, ne işe yarar, hangi durumlarda kullanılır beraberce bakalım.Genelde yaygın olarak kullanılan Command Pattern oldukça faydalı kullanım alanlarına sahiptir. Çoğumuz kullanırken ne olduğunu farkemesek bile .NET, Java .. birçok dilde,kütüphanede uygulanmış bu kalıbı kullanırız. Bu tasarım kalıbının temel amacı değişen method çağırımlarını(method invocation) kodun diğer kısmından soyutlamakdır. Tarif biraz karmaşık oldu farkındayım. Bu yüzden hemen nerelerde ve nasıl kullanıldığına bakalım.

Mesela Java Swing kullananların bildiği AbstractAction sınıfları bir Command Pattern örneğidir.Birde kullanımına bakalım. Çok basit bir formumuz olsun içinde sadece tek bir buton nesnemiz olsun.Bu butonun içinde yapılan şeyler değişiklik gösteriyor.Bazen butona basıldığında Bize Merhaba mesajı göstermesini istiyoruz bazende formumuzu kapatmasını istiyoruz.Bu tarz bir gereksinimi Command Pattern kullanmadan klasik olarak buton kodunun içine if-else yazarak aşağıdaki gibi yapabilirdik.

public class CommandFrame extends JFrame {
    private JButton buton;
    private final String komutTipi;
    public CommandFrame(String komutTipi){
        this.komutTipi =komutTipi;
        setTitle("Command Pattern");
        setSize(300,200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initButton();
    }

    private void initButton() {
        buton =new JButton("Calistir");
        buton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(komutTipi.equals("Mesaj")){
                    JOptionPane.showMessageDialog(CommandFrame.this,"Merhaba");
                }else if(komutTipi.equals("Kapat")){
                    CommandFrame.this.dispose();
                }
            }
        });
        getContentPane().add(buton);
    }

    public static void main(String[] args) {
        CommandFrame frame =new CommandFrame("Mesaj");
        frame.setVisible(true);
    }
}


Gördüğünüz gibi yukarıdaki sınıfımızda if-else kontrolü yaparak her defasında kontrol yapıp neyin çalışması gerektiğini buluyoruz ve ardından onu çalıştırıyoruz. Tabi çalışması gereken şeyler bu kadar basit olmayabilir.Mesela kullanıcıdan yeni bir ihtiyaç geldi ve o butona basıldığında veritabanına yeni bir çalıştırıldı kaydı eklememizi istiyor.Peki ne yapacağız? İlk aklımıza gelen bir if-else daha ekleyip o if-else içine veritabanına bağlanıp kayıt ekleyen kodu aşağıdaki gibi yazmak olacaktır.

public class CommandFrame extends JFrame {
    private JButton buton;
    private final String komutTipi;
    private Connection con;
    private Statement stmt;

    public CommandFrame(String komutTipi) {
        this.komutTipi = komutTipi;
        setTitle("Command Pattern");
        setSize(300, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initButton();
    }

    private void initButton() {
        buton = new JButton("Calistir");
        buton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (komutTipi.equals("Mesaj")) {
                    JOptionPane.showMessageDialog(CommandFrame.this, "Merhaba");
                } else if (komutTipi.equals("Kapat")) {
                    CommandFrame.this.dispose();
                } else if (komutTipi.equals("Kayit")) {
                    try {
                        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                        con = DriverManager.getConnection("jdbc:odbc:Bus", "", "");
                        stmt = con.createStatement();
                        stmt.executeUpdate("Insert Into Tablo (Deger) VALUES ('Calisti')");

                        stmt.close();
                        con.close();
                    } catch (ClassNotFoundException e1) {
                        e1.printStackTrace();
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
        getContentPane().add(buton);
    }

    public static void main(String[] args) {
        CommandFrame frame = new CommandFrame("Kayit");
        frame.setVisible(true);
    }
}

Yukarıdaki CommandFrame sınıfına bakarsanız içinde yok yok olduğunu görecektiniz. Swing sınıflarından,JDBC kodlarına kadar herşeyi yazdık içine.Tabi artık bu sınıfımız birçok diğer sınıfa bağımlı hale geldi.Kodun karmaşıklığı arttı ayrıca diğer kısımlarda CommandFrame sınıfımıza kullanmak neredeyse imkansızlaştı. Çünkü her yeni eklenecek özellikte bu sınıfa yeni bir if-else yapısı ekleyip tekrar derlemek zorundayız.Burada hemen Command Pattern yardımımıza koşuyor.Zaten koda biraz üstten baktığımızda if-else yapıları içinde farklı olan şeylerin birşeyleri çalıştırmak olduğunu göreceksiniz.Aslında hepsinde farklı işler yapan bir metod çağırılıyor diyebiliriz. Command Pattern’ın özüde bu zaten. Her birinde farklı işler yapan o metodları koddan soyutlamak. Bunu Java’da interface kullanarak,.NET de delegate kullanarak yapabiliriz. Zaten bunun için Swing de daha önceden bu yapıya göre yazılmış AbstractAction sınıfı mevcut yapmamız gereken o sınıfdan yeni sınıflar türetip kodumuzu aşağıdaki gibi değiştirmek.

class MesajGosterAction extends AbstractAction {
    public void actionPerformed(ActionEvent e) {
        JOptionPane.showMessageDialog(null, "Merhaba");
    }
}

class PencereKapatAction extends AbstractAction {
    private JFrame frame;    
    public void actionPerformed(ActionEvent e) {
        frame.dispose();
    }
    
    public void setFrame(JFrame frame) {
        this.frame = frame;
    }
}

class KayitEkleAction extends AbstractAction {
    private Connection con;
    private Statement stmt;

    public void actionPerformed(ActionEvent e) {
        try {
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            con = DriverManager.getConnection("jdbc:odbc:Bus", "", "");
            stmt = con.createStatement();
            stmt.executeUpdate("Insert Into Tablo (Deger) VALUES ('Calisti')");

            stmt.close();
            con.close();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }
}

public class CommandFrame extends JFrame {
    private JButton buton;
    private AbstractAction komut;

    public CommandFrame(AbstractAction komut) {
        this.komut =komut;
        setTitle("Command Pattern");
        setSize(300, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initButton();
    }

    private void initButton() {
        buton = new JButton("Calistir");
        buton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                komut.actionPerformed(e);
            }
        });
        getContentPane().add(buton);
    }

    public static void main(String[] args) {
        CommandFrame frame = new CommandFrame(new MesajGosterAction());
        frame.setVisible(true);
    }
}

Gördüğünüz gibi CommandFrame sınıfı artık JDBC kodlarından,Kapatma kodlarından ve gelebilecek herhangi bir istekten haberi olmayacak yani onlardan bağımsız. İleride başka bir şeyi çalıştırma,başka işlem yapma isteği gelse bile CommandFrame sınıfının içini değiştirmeyeceğiz.Sadece AbstractAction sınıfından türeyen yeni komut sınıfları oluşturmak olacak.Ayrıca if-else kontrollerinden de kurtulduk. Bu bize kodun okunulabilirliği açısından da büyük kazanç sağladı. Aklınızın bir köşesinde bulunsun eğer çok if-else yazıyorsanız tasarımınızda ve kodunuzda bir problem olduğunun göstergesidir.

Bir diğer kullanım örneği olarak Java’daki Thread yapısını vereceğim.Çok basit bir kod üzerinde konumuzu inceleyelim

class EkranaYaz implements Runnable{
    public void run() {
        System.out.println("Ekrana Yazıyorum...");
    }
}

class TaklaAt implements Runnable{
    public void run() {
        System.out.println("Takla atıyorum...");
    }
}

public class ThreadOrnek {
    public static void main(String[] args) {
        Thread thread =new Thread(new EkranaYaz());
        thread.start();
        
        Thread thread1 =new Thread(new TaklaAt());
        thread1.start();
    }
}


Main metoduna bakacak olursanız iki tane Thread oluşturduk birinde ekrana yazıyoruz diğerinde takla atıyoruz:) Tabi gerçek hayatta muhtemelen sadece ekrana yazmakla kalmayıp büyük ihtimalle işi halletmek için taklalar atacaksınız.Fakat koda dikkat edecek olursanız Thread sınıfının içinde hiçbir değişiklik yapmadık(Java’da yapamayız zaten) .Sadece oluştururken yapıcısına Runnable interface’ini gerçekleyen iki adet sınıf verdik. Burada Command Pattern işlevini Runnable interface’i görüyor.

public interface Runnable {
    public abstract void run();
}


Runnable interface’ini uygulayan sınıflar tek bir metodun içini doldurmak zorunda run metodu. Ama o metodun içinde ister ekrana yazı yazdıralım ister veritabanına bağlanalım Thread sınıfını hiçbir şekilde ilgilendirmiyor.Bu şekilde Thread sınıfı yaptığı işlem kodundan bağımsız yeniden kullanılabilir bir sınıf haline gelmiş.Gördüğünüz gibi bu şekilde metod çağırımı bir interface arkasında soyutlanarak Command Pattern uygulanıyor.

Genelde Command Pattern örneklerini inceleyecek olursanız genel yapının şu şekilde olduğunu görürsünüz. Bir adet çalıştırılacak komutu simgeleyen Command interface’i bulunur. Örnek olarak Action, IExecutable,Startable,Runnable … gibi. Ayrıca bu interface’lerde genelde bir adet execute(),run(),start()…. gibi metod bulunur. Bu interface’i uygulayan sınıflar o metodu kendileri içinde doldururlar.

Gördüğünüz gibi kullanımı gayet kolay ve pratik olan bu tasarım kalıbı sayesinde oldukça esnek bir yapıda kod yazabildik. Vakit bulabilirsem gerçek bir projede kullanılan bir örneği de eklemeye çalışacağım. Design Patterns serüvenimiz daha yeni başlıyor sayılır diğer Patternlar ile çok yakında sizlerleyiz. Bizi izlemeye devam edin :)

O Şimdi Asker

Asker olduğumu unutmuşum birkaç günden beri aklıma gelince hemen yazayım dedim.1 Aralıktan itibaren resmen askerim ve izindeyim:) Sınavdı şubeydi koşuştururken bütün telaş bitti askerlik vakti geldi.Artık gözüm ayın 10’unda sınav sonucunda.12 Aralıktada birliğime katılacağım.

Yazılımda Basitliğin Önemi

Herhalde biz yazılımcılar işleri zorlaştırmayı sevdiğimizden olsa gerek yazılım geliştirmede problemlerin çözümlerini karmaşıklaştırmada zorlaştırmada üzerimize yok. Aklıma okul yıllarından, profosyonel iş hayatımdan üzerinde çalıştığım birçok proje geldi.Özellikle okulda arkadaşlara ne kadar karmaşık zor kod yazdığımıza dair övünürdük. İşte şöyle bir kod yazdım bilmem kaç satır , içinde bilmem kaç milisaniye kısa süren sıralama algoritması kullandım.. diye uzayıp giderdi muhabbet. Kısa ve basit çözümler bulanlara kötü gözle bakılırdı .Yazılımcı adam basit şeyler yapmaz zor işlerin adamıdır değil mi?:).Bu yüzden problemleride zor çözüm yollarıyla halletmek iyi gelirdi.Tabi bu alışkanlıklar iş hayatında da peşimizi bırakmadı aynı muhabbetler uzun süre devam etti.

Şimdi kendi yazdığım kodları gözden geçiriyorum ve aynı kompleksliği görebiliyorum.Çok basit bir şekilde halledebileceğim şeyleri ne kadar gereksiz zora sokup uzatmışım, ne kadar karmaşık şekilde çözmüşüm hayret ediyorum.Tabi yıllar sonra yazılımcının zor problemleri kolay ve basit bir şekilde çözmesi gerektiğini anladım.Sebebi adı gibi çok basit. Basit çözüm anlaması basit, değiştirmesi basit, yönetilmesi basit olduğundan her zaman kompleks çözümden daha avantajlıdır. O halde işleri zorlaştırmanın karmaşıklaştırmanın hiçbir gereği yok.

Extreme Programming’in temel prensiplerinden olan Basitlik(Simplicity) yazılım geliştirmede büyük bir öneme sahip.Basitliği bir problemin en basit çözümü olarak düşünebiliriz.Kod için düşündüğümüzde belirli bir işi yapması gereken kodun en basit şekilde sonucu üretmesi, ya da yazılımın şuandaki gereksinimleri karşılayan en basit halde olması diyebiliriz.Aksi takdirde anlaşılması zor ,yönetilmesi zor, değiştirmesi zor gereksiz birçok karmaşıklık içeren yazılıma sahip oluruz buda akıl,ruh sağlığı ve iş hayatımızdaki mutluluğumuz için pekde iyi değildir.

Aşağıda daha önceden yazdığım bir kod parçasından örnek verirsem demek istediğimi kod için daha rahat anlayabilirsiniz.Bu arada kızmayın, nasıl bu kadar karmaşık yazdığıma bende şaşırıyorum ama sebebi sanırım bir aralar Design Patterns hastalığına tutulmamdı:)

interface PanType {
    static final String LEFT  ="PanLeft";
    static final String RIGHT ="PanRight";
    static final String UP    ="PanUp";
    static final String DOWN  ="PanDown";
}

interface IPanPoint {
    Point2D.Double getCenterPoint(int screenWidth,int screenHeight);
}

abstract class AbstractPanPoint implements IPanPoint{
    public static IPanPoint createInstance(String panType){
        if(panType.equals(PanType.LEFT))
            return new PanLeftPoint();
        else if(panType.equals(PanType.RIGHT))
            return new PanRightPoint();
        else if(panType.equals(PanType.UP))
            return new PanUpPoint();
        else if(panType.equals(PanType.DOWN))
            return new PanDownPoint();
        else
            throw new IllegalArgumentException();
    }

    public Point2D.Double getCenterPoint(int width,int height) {
        return new Point2D.Double(calculateX(width),calculateY(height));
    }

    abstract int calculateX(int screenWidth);
    abstract int calculateY(int screenHeight);
}

class PanDownPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return width/ 2;
    }

    int calculateY(int height) {
        return (height / 2 + height / 6);
    }
}

class PanLeftPoint extends AbstractPanPoint{
    int calculateX(int width){
        return  (width/ 2) - (width/ 6);
    }

    int calculateY(int height){
        return (height /2);
    }
}

class PanRightPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return  (width/ 2) + (width/ 6);
    }

    int calculateY(int height) {
        return (height /2);
    }
}

class PanUpPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return (width/ 2);
    }

    int calculateY(int height) {
        return (height / 2) - (height / 6);
    }
}

public class MapModel
{
   //diğer metodlar ve alanlar.....
   public void pan(String panType) {
        IPanPoint panPoint = AbstractPanPoint.createInstance(panType);

        Point2D.Double mapPoint = panPoint.getCenterPoint(width,height);
        //diğer işlemler....
<div style="display: none"><a href='http://buyessayonlinee.net/'>buy online essays</a></div>   }

}

Yukarıda gördüğünüz kod parçasının ne yaptığını fazla önemsemeyin.Yapılan şey kısaca sağ,sol,yukarı,aşağı yönlerine göre ekran büyüklüğü kullanılarak x,y değerlerini hesaplamak ve o değerlere göre bazı işlemler yapmak.Şimdi kodumuza şöyle bir bakalım.2 interface,1 abstract sınıf, 4 adet normal sınıf var. Ayrıca her sınıfta 2 metod,ayrıca 1 factory metodu var. Şimdi kullanılan Design Pattern’lara bakıyorum bol bol kullanmışız.1 adet Factory ,1 adet Template Method, 1 adet Strategy Pattern kullanmışız.(Ben neymişim be abi) Ama hemen kızmayın birde esneklik olarak baktığımızda baya iyi durumdayız. Kodumuz sonradan eklenebilecek durumlara karşı değişmeyecek. Tabi bu şekilde Design Pattern kullanmasak durum böyle olmazdı.Mesela ileride olabilecek kuzeybatı yönü hesaplaması için sadece AbstractPanPoint sınıfını extend edip ilgili metodu yazmamız birde Factory metoda eklememiz yeterli.MapModel sınıfı içinde hiçbir değişiklik yapmamıza gerek kalmadan yeni yönümüzü ekleyebiliriz.Fakat ileride böyle bir istek olacak mı?Büyük bir ihtimalle hayır.Olsa bile bugünden ileride gelebilecek istekleri aşırı kompleks düşünüp kodumuzu zorlaştırmanın anlamı yok.

Bu şekilde kodu yazmamızın hiçbir getirisi yok şuanda ilerisi için kodu daha esnek hale getirsekte karmaşıklığı sayesinde ileride değiştirmek daha zor olacak. Ayrıca 4 yönden başka durum olamayacak yani ilerisi içinde böyle bir değişiklik olacakmı onu bile bilmiyoruz. İleriyi düşünerek daha esnek yapalım derken gereksiz yere kodu karmaşıklaştırdığımızla kaldık.Ama bu arada süper iyi programcıyız bakın nasıl kompleks şekilde çözmüşüz problemi nasılda havalı Design Patternlar kullanmışız.:) Şimdi süper iyi programcı modundan çıkıp probleme daha basit gözle bakalım.Kodumuzu aşağıdaki gibi yeniden düzenleyelim.

public class MapModel{
//.........
    public static final String LEFT  ="PanLeft";
    public static final String RIGHT ="PanRight";
    public static final String UP    ="PanUp";
    public static final String DOWN  ="PanDown";

    public void pan(String panType) {
        Point2D.Double mapPoint ;

        if (panType.equals(LEFT)) {
            mapPoint = new Point2D.Double((width / 2) - (width / 6), (height / 2));
        } else if (panType.equals(RIGHT)) {
            mapPoint = new Point2D.Double((width / 2) + (width / 6), (height / 2));
        } else if (panType.equals(UP)) {
            mapPoint = new Point2D.Double((width / 2), (height / 2) - (height / 6));
        } else if (panType.equals(DOWN)) {
            mapPoint = new Point2D.Double(width / 2, (height / 2 + height / 6));
        }
        //nokta kullanılarak yapılan işlemler.......
    }

}

Gördüğünüz gibi eski karmaşık kodu aslında bu kadar basit birkaç if-else içeren kod ile halledebiliyoruz.Birçok sınıf,interface,gitti yerine birkaç satır kod kaldı. Ayrıca kullanılan Design Pattern’lar da kalktı.MapModel sınıfı belki geleceğe yönelik bir istekte değişecek fakat bugünün ihtiyaçlarını en basit şekilde karşılıyor. Anlaşılması öncekinden daha kolay ,yönetilmesi daha kolay vb…Gördüğünüz gibi basit şekilde problemi çözdük.

Basitlik sözde basit gibi gözüksede uygulamada kompleks problemler için basit çözümler üretmek gerçekten zordur.Özellikle yukarıdaki kod parçasında da gördüğünüz gibi Design Pattern gibi koda esneklik sağlayan ilerisi için yapılmış şeyler kodu oldukça zorlaştırmaktadır. Aynı şey bugünden karşımıza çıkmayan gereksiz performans optimizasyonu için de geçerli. Performans sıkıntısı çekmeden kod daha iyi çalışsın diye yapılan değişikliklerde kodu aynı şekilde karmaşıklaştırır.Bu yüzden kodu yazarken sadece bugünün isteklerini göze alıp geliştirmek önemli. Şahsen ben bazen bunu gelecek varsayımlara göre yukarıdaki gibi karmaşık kod geliştiriyordum ama hatalarımdan ders aldım diyebilirim.Özellikle Design Pattern gibi koda komplekslik katan kalıpları kullanırken dikkatli olmak önemli.Zaten Design Patterns kitabanın yazarlarından Eric Gamma’da bir sözünde Design Pattern’ların sadece Design acısı çekildiği zaman kullanılmasını öneriyor.O yüzden hepimizin asıl zor olan şeyi başarmak olan “zor problemlere basit çözümler bulmak” için çabalamasını tavsiye ediyorum.