Bu site emekli olmuştur. Arşiv amaçlı olarak BT AKADEMİ sponsorluğunda yayın hayatına devam etmektedir.




C#nedir?com
 
YAZAR HAKKINDA
Koray Dakan
Koray Dakan
http://www.csharpnedir.com/
İletişme geçmek için tıklayın.
1 Makalesi yayınlanmakta.
Yazar hakkında detaylı bilgi için tıklayın.
Yayınlanan diğer makaleleri için tıklayın.
İlgili etiketler: bagimli bagimliligi bagimlilik birtakim border=0 cellpadding=5 cellspacing=0 degisiklik interface servis2 sinifi sinifinin siniflar siniflara tekrar Yazılım Müh. Koray Dakan
 
YAZI HAKKINDA
Türü : Makale
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
Seviyesi : Orta
Kategori : Yazılım Müh.
Yayınlanma Tarihi : 8.12.2007
Okunma Sayısı : 43487
Yorum Sayısı : 2     yorum yaz
Site İçi AramaSİTE İÇİ ARAMA
Üye Girişini AçÜye GİRİŞİ
Üye girişi için tıklayın.
Kullanıcı Adı
Şifre
 
Beni her zaman hatırla
Bir hafta boyunca kullanıcı bilgilerinizi kullanıcı çıkışı yapana kadar hatırlar. (Paylaşılan bilgisayarlarda önerilmez.)
 
Şifremi / Kullanıcı Adımı unuttum.
 
.net TV RSS Serbest KÖŞE (?)
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
emre TAŞ
Silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
Makale Gönder Bende Yazmak İstiyorum
.net TV RSSBlogroll
Turhal Temizer
Conda install environment.yml Package 24.11.2024
Turhal Temizer
Mac OS/X Removing CUDA 24.11.2024
Burak Selim Şenyurt
Rust ile ECS Yaklaşımını Anlamak 24.11.2024
Burak Selim Şenyurt
Birlikte Rust Öğrenelim Serisi 24.11.2024
  Diğer Herşey
Sponsorlar
BT Akademi
Medya Portakal
Video Hosting Sponsoru
Csharpnedir.com bir Ineta üyesidir
Uzman Abi
Her Yönüyle C# - Sefer Algan
OOP'nin Temel Prensipleri ve Factory Method ile Adaptor Tasarım Desenleri
 
Kapat
Sayfayı Yazdır Sık Kullanılanlara Ekle Arkadaşıma Gönder MySpace Del.Ico.Us Digg Facebook Google Mixx Reddit StumbleUpon
Programlamanın en temel prensibi, kendini tekrar etmemektir, prosedür/fonksiyon, yani altprogramlara zaten bu yüzden subroutine denmiştir, yani rutin tekrar tekrar yapılan işlemler... Önceden yazılmış bir kodu "copy-paste" etmemeli, "cut-paste" ederek bir alt rutine taşımalı veya nesneye yönelik programlama tekniğinde (OOP) bir üst sınıfa taşımalıyız. Böylelikle kodu merkezi şekilde yeniden kullanılır hale getirebiliriz. Bu prensibin prosedürel dillerde uygulama alanı, genel altrutinler yani kütüphane fonksiyonları tasarlamak ile sınırlıdır, ancak OOP’de sınıfların üst sınıflardan türemesi ve türeyen sınıflarda fonksiyonların üst sınıftakini ezebilmesi ve türeyen sınıfın üst sınıf yerine kullanılabilmesi anlamında varolan polimorfizm (çok biçimlilik) özelliği nedeniyle nesneye yönelik programlama dillerinde daha etkin olarak uygulanabilir. Bundan sonraki kısımların anlaşılabilmesi için OOP temellerinin, yani polimorfizmin bilinmesi gerekiyor, ki burada anlatmayacağım. Şimdi ise nesneye yönelik tasarımın ilk ve en önemli prensibini ele alalım. OCP - Open Closed Principle (Açık Kapalı Prensibi) Bir sınıf genişlemeye açık ama değişikliğe kapalı olmalıdır. Çünkü bir sınıfta değişiklik yapmak, buna bağlı bulunan (bundan türeyen veya bunu kullanan) diğer tüm sınıfları da buna uygun hale getirmeyi veya en azından yeniden derlemeyi, versiyon atlatmayı ve müşteriye yeniden yüklemeyi gerektirir. if-else/case-switch karar yapısı bu ilkeye terstir, çünkü bu yapıya yeni bir case (hal) eklenmesi gerektiğinde sınıftaki kodu değiştirmek gerekir. Bu yapı yerine interface veya soyut sınıf ve bunu çeşitli case’ler için implemente eden somut sınıflar kullanılırsa, varolan sınıflar değişmeden sadece yeni bir somut sınıf eklenerek istenen genişleme yapılabilir. Örneğin aşağıdaki yapıyı ele alırsak:
class Servis
{
   void IslemYap(int p)
   {
      if(p == 1)
         Console.WriteLine("1. hal için birtakım işlemler...");
      else if(p == 2)
         Console.WriteLine("2. hal için birtakım işlemler...");
   }
}

class Client
{
   static void Main(string[] args)
   {
      Servis s = new Servis();
      s.IslemYap(2);
   }
}

Yukarıdaki yapı yerine aşağıdaki şu yapı kullanılırsa:
abstract class Servis
{
   abstract void IslemYap()
   {
   }
}

class Servis1 : Servis
{
   void IslemYap()
   {
      Console.WriteLine("1. hal için birtakım işlemler...");
   }
}

class Servis2 : Servis
{
   void IslemYap()
   {
      Console.WriteLine("2. hal için birtakım işlemler...");
   }
}

class Client
{
   static void Main(string[] args)
   {
      Servis s = new Servis2();
      s.IslemYap();
   }
}

Bu şekilde, Servis kütüphanesine p==3 hali, herhangi bir sınıfta değişiklik yapmadan kolaylıkla yeni bir somut sınıf eklenerek şu şekilde gerçekleştirilebilir:
class Servis3 : Servis
{
   void IslemYap()
   {
      Console.WriteLine("3. hal için birtakım işlemler...");
   }
}

Böylelikle Servis sınıf kütüphanemize dokunmadan, production (üretim) da denen, müşteri ortamına sadece yeni Servis3 sınıfının yüklenmesi yeterlidir. Bu aynı zamanda, pratikte programların bakımını ve yeni ilaveleri çok zorlaştıran içiçe birçok if else yapılarından da bizi kurtararak programları okunur ve rahat takip edilebilir hale getirir ve programların bug oranını, bakım maliyetini düşürerek müşteriye daha hızlı destek verilmesini de sağlar. Pratikte bütün if else switch yapılarının bu şekle getirilmesi de maliyetli bir iş olmakla beraber, en azından çok kullanılan bu yüzden çok az değişiklik yapılması gereken altyapı ve ana kütüphane fonksiyonlarında bu prensibin sıkı sıkıya uygulanması çok faydalıdır. Zaten bu bir tasarım prensibidir, iş bittikten sonra sağlıksız bir kodu dönüştürmek de genellikle kodu sıfırdan yazmak kadar maliyetlidir. Bu prensip, farklı şekillerde ileride bahsedeceğim design pattern’larda da (tasarım desenleri veya tasarım kalıpları) tekrar tekrar karşımıza çıkacak. 

Şimdi de nesneye yönelik tasarımın en önemli ikinci prensibini ele alalım. DIP - Dependency Inversion Principle (Bağımlılığı Tersine Çevirme Prensibi) Aslında DIP (Dependency Inversion Principle yani Bağımlılığı Tersine Çevirme Prensibi), OCP’nin (Açık Kapalı Prensibinin) direk bir sonucudur. DIP prensibine göre, ileride değişmesi muhtemel somut sınıflara direkt erişmek yerine, bu sınıflara arayüz veya soyut sınıflar üzerinden erişmemiz gerekir. Bu yolla somut sınıflara ve bunların eriştiği tüm diğer somut sınıflara olan bağımlılığı yokedebiliriz. DIP prensibine aykırı yapılar, birbirine bağımlı sınıflar doğurur, ki bu da temelde OCP, yani değişmeden genişleyebilme prensibine terstir. Çünkü bir sınıfta yapılacak olan değişiklik, diğer tüm sınıflarda değişiklik yapmayı gerektirebilir. Zaten OCP’yi anlatırken geliştirdiğimiz örneğin son hali de DIP’a uygundu, çünkü hatırlarsanız orada geliştirdiğimiz Servis1, Servis2, ... sınıflarına bir soyut sınıf (arayüz de olabilirdi) aracılığıyla erişerek ServisN sınıflarına bağımlılığı yokederek varolan kodun değişmeden genişleyebilmesini sağlamıştık. Şimdi DIP’ın isminin açılımında da yeralan, bağımlılıkları tersine çevirmekten tam olarak ne kastedildiğini anlayabilmek için, önceki OCP örneğimizden daha kapsamlı bir örnek verelim.
Örneğin, A sınıfının B1 ve B2 sınıflarına, B1 sınıfının C1, C2 sınıflarına, B2 sınıfının C1, C3 ve C4 sınıflarına erişim yaptığı (yani bu sınıfları kullandığı) aşağıdaki örneği ele alalım:
class A
{
   void IslemYap()
   {
      Console.WriteLine("A sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(B1 b1, B2 b2)
   {
      Console.WriteLine("A sınıfı için başka birtakım işlemler...");
      b1.IslemYap();
      b2.IslemYap();
   }
}

class B1
{
   void IslemYap()
   {
      Console.WriteLine("B1 sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(C1 c1, C2 c2)
   {
      Console.WriteLine("B1 sınıfı için başka birtakım işlemler...");
      c1.IslemYap();
      c2.IslemYap();
   }
}

class B2
{
   void IslemYap()
   {
      Console.WriteLine("B2 sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(C1 c1, C3 c3, C4 c4)
   {
      Console.WriteLine("B2 sınıfı için başka birtakım işlemler...");
      c1.IslemYap();
      c3.IslemYap();
      c4.IslemYap();
   }
}

class C1
{
   void IslemYap()
   {
      Console.WriteLine("C1 sınıfı için birtakım işlemler...");
   }
}

class C2
{
   void IslemYap()
   {
      Console.WriteLine("C2 sınıfı için birtakım işlemler...");
   }
}

class C3
{
   void IslemYap()
   {
      Console.WriteLine("C3 sınıfı için birtakım işlemler...");
   }
}

class C4
{
   void IslemYap()
   {
      Console.WriteLine("C4 sınıfı için birtakım işlemler...");
   }
}

Bu örnekteki sınıfların birbirine erişimini ve buna bağlı olarak ortaya çıkan bağımlılık zincirini kısaca şöyle gösterebiliriz: A->B1->C1
         ->C2
  ->B2->C1
         ->C3
         ->C4 Bu gösterimde -> işareti okun gittiği yöne olan bağımlılığı gösterir, yani A sınıfı, B1, B2’ye, B1 sınıfı C1 ve C2’ye, B2 sınıfı C1, C3 ve C4’e, yani dolaylı yoldan A sınıfı malesef B1, B2, C1, C2, C3, C4 sınıflarının hepsine bağımlı haldedir. Bu sınıflardan herhangi birinde yapılacak bir değişiklik direkt olarak A’yı etkiler ve muhtemelen A sınıfının değiştirilmesini veya yeniden derlenmesini gerektirir. Görüldüğü gibi, A sınıfının bağımlılıkları bir sarmaşığın kolları gibi, erişebildiği tüm sistemi kaplıyor.
Bu durum, prosedürel dillerde prosedür/fonksiyonların birbirlerini çağırdığı ve en tepedeki prosedür/fonksiyonun(muhtemelen main() fonksiyonunun), daha aşağı seviyede çağırılan tüm diğer prosedür/fonksiyonlara bağımlı hale geldiği durum ile çok benzerdir. Peki bu bağımlılık zinciri (bağımlılık ağacı de denebilir) ve burada herhangi bir sınıfın tetikleyeceği bir zincirleme kazayla nasıl başa çıkabiliriz? Bunun cevabı, erişen sınıf ile erişilen sınıf arasına soyut sınıf/arayüz yerleştirerek, somut sınıflar üzerine varolan bağımlılığın, soyut sınıf/arayüzlere dönüştürülmesidir.
Yani, önceki örneğimizdeki şu bağımlılıklar: A->B1->C1
         ->C2
  ->B2->C1
         ->C3
         ->C4 Şuna dönüşür: A->IB1<-B1->IC1<-C1 
                  ->IC2<-C2 
  ->IB2<-B2->IC1<-C1 
                  ->IC3<-C3 
                  ->IC4<-C4  Bu yeni halinde A sınıfı sadece IB1 ve IB2 arayüzlerine bağımlıdır, yani tüm B1, B2, C1, C2, C3, C4 sınıflarına olan bağımlılığı ortadan kalkmıştır. Yeni durumda B1 sınıfıda yalnızca implemente ettiği IB1 arayüzüne ve erişim yaptığı IC1 ve IC2 arayüzlerine bağımlıdır. Gördüğümüz gibi tepeden aşağıya tüm bağımlılık tersyüz edilmiş, her somut sınıf sadece onu çevreleyen arayüzlere bağımlı hale gelmiştir, ve arayüzler değişmediği sürece (ki arayüzlerin değişmesine genelde ihtiyaç da olmaz, değişikliğe implementasyonlarda ihtiyaç duyulur) bu bağımlılıkların sisteme hiçbir etkisi yoktur.
Yeni haliyle kodumuzu yazacak olursak:
class A
{
   void IslemYap()
   {
      Console.WriteLine("A sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(IB1 b1, IB2 b2)
   {
      Console.WriteLine("A sınıfı için başka birtakım işlemler...");
      b1.IslemYap();
      b2.IslemYap();
   }
}

interface IB1
{
   void IslemYap();
   void BaskaIslemYap(IC1 c1, IC2 c2);
}

class B1 : IB1
{
   void IslemYap()
   {
      Console.WriteLine("B1 sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(IC1 c1, IC2 c2)
   {
      Console.WriteLine("B1 sınıfı için başka birtakım işlemler...");
      c1.IslemYap();
      c2.IslemYap();
   }
}

interface IB2
{
   void IslemYap();
   void BaskaIslemYap(IC1 c1, IC3 c3, IC4 c4);
}

class B2 : IB2
{
   void IslemYap()
   {
      Console.WriteLine("B2 sınıfı için birtakım işlemler...");
   }

   void BaskaIslemYap(IC1 c1, IC3 c3, IC4 c4)
   {
      Console.WriteLine("B2 sınıfı için başka birtakım işlemler...");
      c1.IslemYap();
      c3.IslemYap();
      c4.IslemYap();
   }
}

interface IC1
{
   void IslemYap();
}

class C1 : IC1
{
   void IslemYap()
   {
      Console.WriteLine("C1 sınıfı için birtakım işlemler...");
   }
}

interface IC2
{
   void IslemYap();
}

class C2 : IC2
{
   void IslemYap()
   {
      Console.WriteLine("C2 sınıfı için birtakım işlemler...");
   }
}

interface IC3
{
   void IslemYap();
}

class C3 : IC3
{
   void IslemYap()
   {
      Console.WriteLine("C3 sınıfı için birtakım işlemler...");
   }
}

interface IC4
{
   void IslemYap();
}

class C4 : IC4
{
   void IslemYap()
   {
      Console.WriteLine("C4 sınıfı için birtakım işlemler...");
   }
}

Aslında A sınıfının da bir yerlerden çağrılacağını düşünürsek bunun için de IA gibi bir arayüz yazılabilir. Örnekte gördüğümüz bütün arayüzler birer yalıtım duvarıdır. Soyut sınıflar ve Arayüzler, OCP prensibinde gördüğümüz gibi, değişmeden genişleyebilir yapılar oldukları için, sistemin en sabit yapıtaşlarıdır. Bu yüzden, bu tür tasarlanmış sistemlerde soyut sınıf ve arayüzlere olan bağımlılıklar zararsızdır, yani üst yapıyı etkilemez.
Adapter ve Factory Method Tasarım Kalıpları ile Bağımlılığı Yoketmek Yukarıdaki son örnekte gördüğümüz B1 ve B2 sınıfları, aslında bir anlamda ICn arayüzlerini IBn arayüzlerine çeviren birer adaptör gibi görülebilir. C1, C2, C3, C4 sınıflarının ve IC1, IC2, IC3, IC4 arayüzlerinin bizim kontrolümüz dışında bir kütüphane olduğunu ve arasıra tasarımının değiştiğini düşünelim. Bu kötü bir kütüphane tasarımı veya zorunluluk gereği, örneğin yeni ve gelişimini tamamlamamış bir teknoloji veya bir donanıma bağımlılık nedeniyle olabilir. Zaman içinde değişkenlik gösteren bu C katmanını bizim altyapımıza uyarlamak için B katmanındaki sınıflar gibi sınıflara ihtiyacımız olur, bu kullanım sık sık karşımıza çıktığı için buna literatürde adapter deseninin nesne biçimi (adapter pattern - object form) denmiştir. Adapter deseni iki biçimde karşımıza çıkar, bu örnekteki nesne biçiminin dışında, bir de sınıf biçimi vardır. Adına nesne biçimi denen kullanımda, B sınıfı IB arayüzüzünü implemente eder ve C sınıfını (veya IC arayüzünü veya soyut sınıfını) kullanır. Sınıf biçimindeki kullanımda ise, B sınıfı hem IB arayüzüzünü hem de IC arayüzünü implemente ederek IC arayüzünü veya soyut sınıfını kullanır.
Önemli bir ikinci nokta da, dikkat ederseniz örneğimizdeki A, B, C sınıflarımızda hiç bir sınıftan nesne yaratmadık, nesneler metodlarımıza parametre olarak geçildi. Eninde sonunda bu altyapıyı kullanacak bir sınıfta, parametre olarak aktarılacak olan bu sınıflardan nesenelerin yaratılması gerekiyor, ancak nesneleri new operatörüyle yaratılırsak, yarattığımız somut sınıfa bağımlılık ortaya çıkar.
Yani, yazımızın ilk başlarında OCP için verdiğimiz örneğe benzeyen aşağıdaki örneği incelersek:
interface IServis
{
   void IslemYap();
}

class Servis1 : IServis
{
   void IslemYap()
   {
      Console.WriteLine("Servis1 için birtakım işlemler...");
   }
}

class Servis2 : IServis
{
   void IslemYap()
   {
      Console.WriteLine("Servis2 için birtakım işlemler...");
   }
}

class Client
{
   static void Main(string[] args)
   {
      IServis s = new Servis2();
      s.IslemYap();
   }
}

Bu örnekte Client sınıfımız ilk satırda new işlemiyle Servis2 somut sınıfına (ve tabii IServis arayüzüne) bağımlı hale geliyor. Halbuki biz sadece IServis arayüzüne bağımlı kalmak istiyoruz. İşte bu devrede creational patterns (yaratım desenleri) devreye girer. Bu desenler Singleton, Factory Method, Abstract Factory ve Builder desenleridir. Bunlardan Singleton şu anki konumuzun dışında olduğu için ona şu an değinmeyeceğim. Nesnenin sınıfına bağımlı kalmadan nesneyi yaratmak için kullanabileceğimiz desenlerden ilki, Factory Method pattern’dir (fabrika metodu deseni). Bu desen çok basit olarak, nesneyi üretme işini başka bir fonksiyona veya sınıfa havale etmektir.
Örneğin yukarıdaki kodu şöyle yazalım:
interface IServis
{
    void IslemYap();
}

class Servis1 : IServis
{
   void IslemYap()
   {
      Console.WriteLine("Servis1 için birtakım işlemler...");
   }
}

class Servis2 : IServis
{
    void IslemYap()
   {
      Console.WriteLine("Servis2 için birtakım işlemler...");
   }
}

class ServisFactory
{
   IServis Servis1Yarat()
   {
      return new Servis1();
   }

   IServis Servis2Yarat()
   {
      return new Servis2();
   }
}

class Client
{
   static void Main(string[] args)
   {
      ServisFactory sf = new ServisFactory();
      IServis s = sf.Servis2Yarat();
      s.IslemYap();
   }
}

Aslında bu örnekte sadece bağımlılık zincirinde araya yeni bir halka, yani yeni bir sınıf daha ekledik, gerçekte bağımlılığı yok etmedik. Bağımlılığı tam olarak kaldırmak için tek yapılabilecek şey, Factory Method’larda, yani örnekteki Servis1Yarat() ve Servis2Yarat() metodlarında Servis1 ve Servis2 sınıf isimlerini hardcode şeklinde yazmamak, yani nesneyi reflection kullanarak sınıf ismini konfigürasyondan, yani dosya veya veritabanından okuyarak yaratmaktır.

Özellikle, başkalarının kullanımına yönelik sınıf kütüphaneleri tasarlayan veya böyle kütüphaneleri kullanan herkesin kafasında, kullanılan yapılara dair oluşabilecek soru işaretlerinin, bu yazı ile azalacağını umuyor, iyi çalışmalar diliyorum. Daha kapsamlı bilgi edinmek için, http://www.objectmentor.com adresinden Robert Martin’in bu konulardaki makalelerine ulaşabilirsiniz.

Makale:
OOP'nin Temel Prensipleri ve Factory Method ile Adaptor Tasarım Desenleri Yazılım Mühendisliği Koray Dakan
  • Yazılan Yorumlar
  • Yorum Yaz
ARA
9
2007
güzel bir makale olmus. eline saglik. OOP Principles ve Design Patterns konularinin devami gelir umarim. tesekkürler.
Sayfalar : 1 
Yorum yazabilmek için üye girişi yapmalısınız. Üye girişi için tıklayın.
Üye değilseniz Üyel Ol linkine tıklayarak üyeliğinizi hemen başlatabilirisniz.
 
  • Bu Konuda Son 10
  • Eklenen Son 10
  • Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor