Tag Archives: Design Patterns

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 :)