Kod Kalitesi : Kod Bağımlığına Dikkat

Yazılım dizayn etmenin iyi ya da kötü bir çok yöntemi var. Yazılım geliştirirken gözönüne alınması gereken en önemli konulardan biri de sürdürülebilirlik. Kötü dizayn edilmiş sistemleri geliştirmesi baştan kolay olabilir fakat değişen ihtiyaçlara göre bunları modifiye etmek gerçekten çok çok zor olabilir. Kötü dizayn edilmiş sistemler kırılgan olmaya yatkındır, yani yazılımın bir yerinde yaptığınız bir değişiklik alakasız başka yerleri etkileyebilir, bu yüzden kod üzerinde değişiklik yapmak hem zordur hemde vakit kaybettiricidir. Test sınıfları ekleyerek hatanın nereleri etkilediği bir ölçüde açığa çıkarılabilir fakat yinede hatayı düzeltmek kötü dizayn yüzünden zor ve vakit alıcı olacaktır.

Yazılmış kodu refactor ederek geliştirebilirsiniz. (refactor: bir bilgisayar programının çıktılarını ve işlevlerini değiştirmeden iç yapısının yeniden düzenlenerek geliştirilmesi) fakat birşeyleri değiştirmek kötü dizaynda yine de çok zordur. Daha en başından herşeyi düzgün yapmak daha kolay olmaz mıydı ? Burada geliştirdiğiniz yazılımın ileriye yönelik kod kalitesi ve sürdürülebilirliğini garanti altına alabileceğiniz bir teknik göreceğiz. Bağımlılık Değiştirme Prensibi ( Dependency Inversion Principle ) kaliteli, sürdürebilir yazılım geliştirme için temel tekniklerden biridir. Bu prensibin temelinde, objeler birbirlerine uygulama şekliyle değil, soyutlamayla (abstraction) bağlanmalıdır fiktir yatar.

Sıkı Bağımlılık

Object-oriented programlamada bağımlılık (coupling) kelimesini duymuşsunuzdur. Bağımlılık bir yazılımdaki bileşenlerin ve objelerin birbirleriyle ilişkileri birbirlerine bağımlılıkları demektir. Birbiriyle çok sıkı bağımlılıkları olmayan bileşenlerden veya objelerden oluşan bir uygulama, birbirine sıkı bağımlığı olan uygulamaya göre daha modülerdir. Bağımlılığı az olan uygulamada bileşenler ve objeler arası iletişim interface ve abstract class’lar üzerinden sağlanır, fakat sıkı bağımlı objelerden oluşan bir yazılımda, uygulama kodu içeren objeler doğruca birbirleriyle etkileşim içindedir.

Bağımlılıkla ilgili problemi görmek için aşağıdaki şekil oldukça yardımcı olacaktır.

Bağımlı Bir Sistem

Yukarıdaki sistemde kullanıcı arayüzü veritabanıyla sıkı bağlantılıdır. Yani veritabanı olmadan arayüzü çalıştırıp test edemeyiz. Yıllarca bu şekilde uygulamalar yazıyoruz, ve gayet başarılı bir şekilde işimizi de görüyorlar. Fakat iş test etmeye geldiğinde karşımıza engeller çıkmaya başlıyor.

Kırılgan mı Dediniz ?

Yukarıdaki resimdeki testi ve sürdürebilirliği birbirine sıkı sıkıya bağlı olan sistemde parçaları birbirinden izole etmek zordur. Kullanıcı arayüzünü test etmek için çalışan ve içi düzgün verilerle doldurulmuş bir veri tabanına, veri girişini kontrol etmek içinde düzgün çalışan bir kullanıcı arayüzüne ihtiyaç vardır. Kullanıcı arayüzünü TestNG-Abbot ( şuanki ismi FEST ) ile test edilebilir fakat, bu yinede veritabanının fonksiyonelliği hakkında size bir ipucu vermez.

Bu bağımlı dizaynı kod içinde görelim. Bir butona bağlı ActionListener objesi tanımlayıp getOrderStatus metodunun çağırarak veritabanıyla doğrudan bağlantı kuracağız.

Bir buton için ActionListener tanımlıyoruz

findWidgetButton.addActionListener(new ActionListener() {

 public void actionPerformed(ActionEvent e) {
  try {
   String value = widgetValue.getText();
     if (value == null || value.equals("")) {
       dLabel.setText("Please enter a valid widgetID");
     } else {
       dLabel.setText(getOrderStatus(value));
     }
  } catch (Exception ex) {
    dLabel.setText("Widget doesn't exist in system");
  }
}
//more code
});

Butona tıkladığımızda, bir siparişin durumu doğruca veritabanından alınıp gösterilir.

Kullanıcı arayüzü getOrderStatus metoduyla doğruca veritabanından bilgileri alıyor

private String getOrderStatus(String value) {
 String retValue = "Widget doesn't exist in system";
 Connection con = null;
 Statement stmt = null;
 ResultSet rs = null;
 try {
  con = DriverManager.getConnection("jdbc:hsqldb:hsql://127.0.0.1", "sa", "");
  stmt = con.createStatement();
  rs = stmt.executeQuery("select order.status "
     + "from order, widget where widget.name = "
     + "'" + value + "' "
     + "and widget.id = order.widget_id;");
  StringBuffer buff = new StringBuffer();
  int x = 0;
  while (rs.next()) {
  buff.append(++x + ": ");
  buff.append(rs.getString(1));
  buff.append("\n");
  }
  if(buff.length() > 0){
    retValue = buff.toString();
  }else{
    retValue = "Widget doesn't exist in system";
  }
 } catch (SQLException e1) {
   e1.printStackTrace();
 } finally {
  try {rs.close();} catch (Exception e3) {}
  try {stmt.close();} catch (Exception e4) {}
  try {con.close();} catch (Exception e5) {}
 }
 return retValue;
}

Yukarıdaki kod, hem doğruca veritabanına gittiği için, hemde hardcoded sql kodu kullandığı için en başından kaybetmiştir. Bu arayüzle ilişkilendirilmiş veritabanı kodunu modifiye edecek programcıyı bir düşünün. Sistemde yapacağı her değişiklik onun için zaman alıcı olacaktır. Veritabanında yaptığı her değişikliği arayüze yantsıtması gerekecek…

Bağımlılığı Azaltalım ( Loosely Coupled )

Aynı sistemi Dependency Inversion prensibini göz önünden bulundurarak tekrar tasarlayalım. Bir interface ve bi uygulama sınıfı ekleyerek aradaki bağımlılığı kaldıralım.

Az Bağımlı Bir Yapı

Yukarıdaki yapıda kullanıcı arayüzü veritabanı yerine, bir veri objesine (DAO,Data Object) bağlanır. Veri objesi ise, bir ugulama sınıfıyla veritabanına bağlanır. Ekle, değiştir, sil gibi işlemler uygulama sınıfı içeresinde bulunur.  Bu işlemleri yaparken uygulama sınıfı veri objesini kullanır ve onun içeriğine göre kayıt veya değişiklik yapar. Kullanıcı arayüzü kodu bunların hiç birine karışmaz. Üzerinde işlem yapacağımız bilgi veri objelerinde tutulur, bu veri objeleri üzerinde işlemleri uygulama sınıfları yürütür.

Bağımlılığı azaltan WidgetDAO interface’i

public interface WidgetDAO {
    public String getOrderStatus(String widget);
  //....
}

Kullanıcı arayüzünün ActionListener sınıfı, doğrudan uygulama sınıfını kullanmak yerine yukarıdaki WidgetDAO interface ini kullanarak soyutlamayı gerçekleştirir.

Kullanıcı Arayüzü Doğrudan Veritabanı Kullanmak Yerine Interface Sınıfları Kullanır

private String getOrderStatus(String value) {
  return dao.getOrderStatus(value);
}

Kullanıcı arayüzü uygulama sınıfından tamamen habersizdir, burada interface kullanarak soyutlamayı gerçekletiriyoruz.

WidgetDAO arkasındaki uygulama sınıfı kullanıcı arayüzünden soyutlanmış durumda

private WidgetDAO dao;
//...
private void initializeDAO() {
  this.dao = WidgetDAOFactory.manufacture();
}

Yukarıda gördüğünüz gibi kullanıcı arayüzü sınıfı (GUI) sadece veri objesi interface ine (WidgetDAO ) referans ediyor. Interface in uygulama sınıfı arayüzde ne import ediliyor ne de kullanılıyor. Esnek bir yapı kurmanın temelinde uygulama sınıflarını interfaceler ile izole etme yatar, bu sayede kullanıcı arayüz kodunu hiç değiştirmeden arka taraftaki uygulama kodlarını değiştirebiliriz. Mesela bundan sonra verileri veri tabanından değilde, bir xml dosyasından okuyacağız diyelim kullanıcı arayüz koduna dokunmamıza gerek yok sadece bunun için WidgetDAO implement eden yeni bir uygulama sınıfı yazmamız yeterli bu uygulama sınıfı da diğerinden farklı olarak verileri veri tabanından değilde xml dosyasından okuyacak fakat arayüz kodunun bundan haberi bile olmayacak en ufak bir değişikliğe ihtiyaç duymayacak.

Factory Sınıfı, Uygulama Sınıfının Detaylarını GUI’den gizler

public class WidgetDAOFactory {
  public static WidgetDAO manufacture(){
  //..
  }
}

Yukarıda bahsettiğim gibi, kullanıcı arayüz sınıflarının interface e referans etmesi size farklı uygulama sınıfları yaratma esnekliği sağlar. Bir sınıfınız verileri veritabanından alıyorken bir diğeri xml dosyasında bir diğeri text dosyasından alıyor olabilir fakat kullanıcı arayüz sınıfı bunu bilmez.
Uygulama Sınıfı

public class WidgetDAOImpl implements WidgetDAO {
 public String getOrderStatus(String value) {
  //...
 }
}

Yukarıdaki kodd uygulama sınıfının içini doldurmadık, dediğimiz gibi bu önemli değil, veri, veritabanından da geliyor olabilir, xml dosyasından da text dosyasından da.

Kullanıcı Arayüz (GUI) Kodunu İzole Etme

GUI kodu bir soyutlamaya bağlıdır ve factory sınıfı sayesinde, interface in arkasındaki uygulama objesine erişir. Rahatça GUI’yi izole eden veritabanı ya da dosya sisteminden bağımsız bir sınıf oluşturabilirsiniz. Bu şekilde veri girişini, GUI kullanmadan test edebilirsiniz.

public class MockWidgetDAOImpl implements WidgetDAO {
 public String getOrderStatus(String value) {
   //..
 }
}

GUI izolasyon kodunu ekleme, sürdürebilir bir sistem yaratmadaki son adımımızdı. Bu sayede GUI doğrudan kullanmadan veri girişini de test edebiliyoruz.

Sonuç
Şuanda bunun farkında değilsiniz fakat, yazdığınız kodlar veya design ettiğiniz sistemler sizden çok daha uzun süre yaşayacak. Sizler başka projelere geçeceksiniz, belki başka şirketlerde çalışmaya başlayacaksınız fakat yazdığınız kod (COBOL), dizayn ettiğiniz sistemler yıllarca kullanılmaya devam edecek.

Hemfikir olduğumuz bir konu; iyi yazılmış kod, sürdürülebilir koddur. Dependency Inversion prensibi iyi kod yazmanın temelinde yatan en önemli fikirlerden biridir. Veri objeleri (DAO) ve interface sınıflarını kullanarak yazacağınız kodu sizin rahatça değiştirebilmenizin yanında diğer yazılım geliştiricilerde rahatça değiştirebileceklerdir.

Not: Yazının orjinali için tıklayın

Share and Enjoy:
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • Linkter
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati

1 Comment »

  1. Aysen Furhoff said,

    January 19, 2010 at 1:45 pm

    cok iyi, emeginize saglik.

    /Aysen

RSS feed for comments on this post

Leave a Comment