Tekrar selamlar! İş dergiye yazı yetiştirmeye gelince bir ay oldukça çabuk geçiyor! Tasarım desenleri yazı serisinin bu ikinci sayısında sizlerle delegation Pattern hakkında bilgi paylaşımında bulunacağım. Ancak bundan önce iki tane önemli tasarım prensibinden bahsetmek istiyorum. Umarım beğenirsiniz ve sizin için faydalı olur.

Cohesion (kohezyon), belli bir yazılım modülünün, bir paketin ya da en basitinden bir sınıfın odaklılığını, yani sahip olduğu sorumlulukların birbirleriyle olan yakınlığını ifade eder. Yüksek kohezyonlu bir modülün sorumlulukları birbirleriyle oldukça alakalıdır; dolayısıyla da oldukça spesifiktir. Hızlı implementasyon için olanak sağlar. Ayrıca yüksek kohezyonlu sistemlerde hataların tespiti ve düzeltilmesi kolaydır. Bu da geliştirme maliyetlerini azaltacaktır.

Coupling (eşleşme), iki modülün birbiri arasındaki doğrudan ya da dolaylı ilişkileri ifade eder. Bu eşleşmeler ne kadar düşük/gevşek olursa, gereksiz bağlantılar o kadar az demektir. Böylece modüllerin sorumlulukları o kadar odaklı olur. Dolayısıyla düşük eşleşme, yüksek kohezyonun oluşturulma olanağını da beraberinde getirir. Yer ve zaman tasarrufu sağlanmış olur.

Coupling ve Cohesion ilkeleri, verimli tasarımlar için vazgeçilmez ilkelerdir. Gevşek Eşleşme (Loose Coupling) ve Yüksek Kohezyon (High Cohesion) karakteriği taşıyan tasarımlar, üretmeye çalıştığınız çözümü faydalı, verimli ve kalıcı kılar. Bir tasarım desenini uygularken de, uygulamak istediğiniz sistemde bu özellikleri azami ölçüde sağladığına emin olmalısınız. Bundan sonra yazacağım tasarım desenlerinde de Loose Coupling ve High Cohesion açısından da analiz edeceğim.

Gelelim asıl konumuza :)

Delegation Pattern

Yazılımınızın bir kısmını (genellikle bir sınıfını) tasarlarken belli bir işlevi yerine getirmesini istiyorsunuz. Bu işlevi benzer bir şekilde gerçekleştiren bir başka sınıfın varolduğunu keşfettiniz. Aynı kodu tekrar yazmak istemezsiniz; çünkü ekonomik bir çözüme ihtiyacınız var. Bu yüzden o sınıftaki kodu bir şekilde yeniden-kullanmak faydalı olur. İşte bu noktada Tasarım desenimiz devreye giriyor.

Dikkatinizi çekmek isterim ki şemada belirtilmiş olan sınıfların arasında inheritence ilişkisi bulunmamaktadır. Nitekim desenimiz, aralarında inheritence olmaksızın ilişki kurulmak istenen parçalar için kullanılmaya daha elverişlidir.

Karşı Desenleri

  • Görevlendirici'nin Vekil'i inherit etmesi. Vekil'deki kullanılması istenen metod Görevlendirici'ye aktarılır; ancak Vekil'deki diğer gereksiz veriler Görevlendirici'de kopyalanmış olur.
  • Genel çizimde Görevlendirici'nin görevlendiriciMetod'u ile Vekil'in metod'u arasındaki ilişkiyi oldukça basit bir şekilde gösterdim. Sırf Vekil'in metod'unu çağırmak için bir tane boş gorevlendiriciMetod yazmak hoş olmayabilir. Vekil'in metod'u, Görevlendirici'nin diğer metodlarında da kullanmak isteyebilirsiniz. Bir başka deyişle Delegation deseni, blabla, geri dönüşümlü kod kullanımını ne kadar etkin bir şekilde sağlarsanız o kadar isabetli bir tercih haline dönüşecektir.
  • Bir kere bu deseni uyguladınız mı, artık sınıflar arasındaki iletişimlerde daha özenli olmak gerekiyor. Doğrudan bağlantıları bulunmayan iki sınıf arasında iletişim yazmamalıyız; örneğin;
this.getDidinin().getDidisi().getOzellik();

yerine doğrudan

this.getDidinin().getOzellik();

diyebilmeliyiz. “getDidinin()” ile bize dönen objenin “getOzellik()” diye bir metodu zaten mevcut olmalı ve kodun geri kalan kısmından beklediğimiz işlevi yerine getirmelidir.

Şimdi biraz daha canlı bir örnek ile uğraşalım, elimizi hem tasarıma hem de biraz koda bulaştıralım... Not: Kodları Java dilinde yazdım, C++ ya da C#'a çok kolay çevrilebilir. Öncelikli amacım çalışır bir koddan ziyade fikir vermesi için parça parça kod yazmak olduğundan, diğer işlemlerin yapıldığını varsaydığım pek çok yere (...) koydum.

Bir önceki “tasarım desenleri” yazımda ele aldığım örneğe dönelim. Orada bir “Üniversite” ve “Bölüm Dersi” sınıflarının varlığından söz etmiştim. Aynı sisteme bir Öğrenci Kaydı altsistemi yaratalım.

ÖSS'yi kazanan gençlerimiz, tercihlerine ve puanına uyan bir üniversitenin bölümüne kaydolurlar. Bu kayıt sürecine “Üniversite”nin ilgili bileşenleri dahil olur. Bu altsistemle iletişim içinde bulunan kullanıcılar/diğer altsistemler, bütün bu işlemler dahilinde sadece “Üniversite” ile bağlantı halindedir; geri kalanıyla ilgilenmez. Aynı şekilde “Üniversite”, bileşenlerinin yaptığı işlerin detaylarıyla ilgilenmez; o işleri yapmaları için o bileşenlere görev ataması yapar.

Bir önceki sayıdaki sınıflara ek olarak “Öğrenci”, “Öğrenci İşleri” ve “Bölüm” sınıfları tanımlayalım. Varsayımlarla dolu, çalakalem ama en azından doğru yolda olma hissini veren bir önçalışmanın sonucunda elimize sağdakine benzer bir şema çıkar:

Dikkat ederseniz, “BolumDersi” sınıfını bu sefer “Bolum” sınıfı ile eşleştirdim. Bu şekilde bir önceki sayıdaki tasarım birazcık daha mantıklı ve tutarlı oldu =)

Birbirleriyle eşleşen sınıfların ilgili alanları şu iskelet kodlara benzeyecektir:

// Universite.java, sadece gerekli değişiklikler...
public class Universite
{
 .... 
 public boolean ogrenciKaydi(String ogrenciAdi, int yasi, 
      String cinsiyeti, 
      String kaydolunacakBolum)
 {
  boolean islemTamamlandi = true;
  Ogrenci o = new Ogrenci(ogrenciAdi,
      this.ogrenciNoYarat(), yasi, cinsiyeti,
      kaydolunacakBolum);
  ....

// Üniversitemizin öğrenci işleri vardır...
/* 
    Universite(Görevlendirici) – OgrenciIsleri (Vekil)
    arasında Delegation deseni 
    oluşturalım; ogrencinin askerlik, harç ve diğer
    işlemlerini yapması için Universite, OgrenciIsleri'ne
    görev ataması yapar...
  */
  islemTamamlandi = islemTamamlandi &&
       this.getOgrenciIsleri().
        ogrenciIslemleri(o);
  /* 
    Universite (Görevlendirici) – Bolum (Vekil)
    arasında Delegation deseni oluşturalım; ogrencinin
    bilgilerinin bölüm veritabanına eklenmesi ve öğrenciye
    zorunlu bölüm müfredatının verilmesi için Universite,
    Bolum'e görev ataması yapar...
  */

  islemTamamlandi = islemTamamlandi &&
      this.getBolum(kaydolunacakBolum).
        ogrenciEkle(o);
  /* 
   getBolum(String s) muhtemelen bölümler listesinden
   ilgili 'Bolum' objesini çağırmak içindir...
  */
  // ardından geriye kalan işlemler, tabii varsa...
  ....
  return islemTamamlandi; // işlem başarıyla yapıldı..
 }
}
// OgrenciIsleri.java; sadece ilgili kod parçaları..
public class OgrenciIsleri
{
 ....
 public boolean ogrenciIslemleri(Ogrenci o)
 {
  return  this.askerlikIslemleri(o) && 
    this.okulHarciIslemleri(o) && 
    this.digerIslemler(o);
  // bu metodların içleri de gerekli ayrıntılarla
  // doldurulur.
 }
}
// Bolum.java; sadece ilgili parçalar...
public class Bolum
{

 public boolean ogrenciEkle(Ogrenci o)
 {
  // Bölümün veritabanına öğrenci bilgilerinin girişi
  // Öğrenciye müfredat yüklemesi (muhtemelen BolumDersi
  // sınıfının yardımıyla), vs.
  return  veritabaniIslemleri(o)
         && mufredatYukleme(o);
 }
}

“Askerlik İşlemleri” ya da “Diğer İşlemler” gibi karmaşık olabilecek işlemler için benzeri yapılanmalar da oluşturulabilir. İşlemlerin veritabanıyla alakalı kısımları içinse daha uygun desenler ve uygulamalar bulunmaktadır.

Sistemimizi bu şekilde yapılandırdığımızda elimize işlevsel bakımdan özelleşmiş ve birbirleriyle minimal bağlantıları olan sınıflar geçiyor. Yani Yüksek Kohezyonlu, Gevşek Eşleşmeli bir sonuca ulaştık. OgrenciIsleri ve Bolum sınıfları, sadece belirli işlevleri yerine getirmekte ve birbirlerinin işlerine karışmamaktadır. Universite sınıfı ise, kayıtla ilgili kendi özel işlemleri varsa onları yapar; OgrenciIsleri ve Bolum sınıflarına atadığı görevlerin sonuçlarını birleştirerek genel sonucu oluşturur.

Genel Değerlendirme

Problem koşullarının, bu deseni uygulayabilmemiz için uygun olduğunu garantilememiz gerektiğini tekrar hatırlatmak isterim. Delegation desenini doğru şekilde uyguladığımızda, işlevlerin ayrıntılarını birbirlerinden başarılı bir şekilde ayırırız. Bu sayede sınflarımız güçlü ve işlevsel odaklara dönüşecektir; her sınıf kendisine atanan işlevleri yerine getirecek ve kendisinin dahil olmadığı sistem ayrıntılarından soyutlanacaktır. Deseni uygulamadan önceki duruma göre sınıflar arasındaki bağlantılar azalmış durumda olacak; böylece hem Yüksek Kohezyon hem de Gevşek Eşleşme ilkeleri gerçekleştirilmiş olacaktır.

Bir sonraki yazıya kadar, her şey gönlünüzce olsun...

Kaynaklar