Part 7 Çok biçimlilik (Polimorfizm)

Polimorfizm Nedir?

Polimorfizm, esasında kalıtım gibi biyolojik bir terimdir. Yazılımda polimorfizm, iki veya daha fazla farklı nesnenin aynı türden sınıflar tarafından referans alması anlamına gelir.

OOP’de (Nesne Yönelimli Programlama) polimorfizm, iki ya da daha fazla nesnenin aynı tür sınıf tarafından karşılanabilmesi veya referans edilebilmesi anlamına gelir. Bir başka deyişle, bir nesnenin birden fazla farklı türdeki referans tarafından işaretlenebilmesi polimorfizmdir.

Polimorfizm, OOP tasarımlarında geliştirilen koda daha esnek ve manevra kabiliyeti yüksek nitelikler kazandıran bir özelliktir. Bu sayede, farklı türdeki nesnelerle aynı türde işlem yapabilmek mümkün hale gelir ve kod yeniden kullanılabilirlik ile genişletilebilirlik açısından daha güçlü hale gelir.

Bir Nesnenin Birden Fazla Referansla İşaretlenmesi Neye Yarar?

Bir nesnenin birden fazla referansla işaretlenmesi, o nesnenin birden fazla türün davranışını sergilemesini sağlar. Bu durum, farklı türdeki referanslarla aynı nesne üzerinde çeşitli işlemler yapılabilmesine imkan tanır ve esneklik kazandırır.

Avantajları:

  1. Esneklik: Nesneler, farklı türlerdeki referanslarla işaretlenerek çeşitli şekillerde kullanılabilirler.
  2. Kodun Yeniden Kullanılabilirliği: Aynı nesne, farklı referanslar aracılığıyla farklı bağlamlarda kullanılabilir, bu da kodun yeniden kullanılabilirliğini artırır.
  3. Genişletilebilirlik: Yeni türler eklemek ve bu türlerin mevcut sistemde nasıl kullanılacağını tanımlamak daha kolay hale gelir.
  4. Çok Biçimlilik (Polimorfizm): Aynı işlem, farklı türlerdeki nesneler üzerinde çalıştırılabilir, bu da kodun daha genel ve esnek olmasını sağlar.

Polimorfizm Felsefesi – 1

Kuş deyince aklınıza ne geliyor?

Farkındaysanız, kuş deyince belki de aklınıza herhangi belirli bir tür gelmeyebilir, ancak özünde hepsi bir kuş olduğunu görebiliriz. İşte bu hayvanların kendi türlerinin dışında ortak olarak “kuş” diye nitelendirilmeleri polimorfizmdir. Yani, bir olguyu çoklu (poli) form (morfizmos) olarak tanımlayabilmekteyiz.

“Kuş” cinsinden olan tüm hayvanlar, kendi türlerinin dışında bir yandan “kuş” olarak nitelendirilirler. Bu durumda “kuş” kavramı, farklı türlerdeki hayvanları ortak bir şekilde tanımlamaktadır.

Ortak atadan gelen, kalıtımsal olarak “kuş”tan türeyen tüm hayvanlar kendi türleri ya da “Kuş” türü ile referans edilebilirler.

Buradan da şunu anlıyoruz ki, yazılımsal açıdan çok biçimlilik söz konusu olabilmesi için teknik olarak “Kalıtım” olması gerekmektedir. Bunun daha detaylı açıklamasını dersin devamında göreceğiz.

Polimorfizim felsefesi – 2

Araçlar ve Polimorfizm

Araç deyince aklınıza ne geliyor? Otomobil, kamyon, motosiklet veya bisiklet gibi farklı araç türleri olabilir. Farkındaysanız, araç deyince bu taşıtlardan herhangi biri aklınıza gelmeyebilir, ancak özünde bunların hepsi birer araçtır. İşte bu taşıtların kendi türlerinin dışında ortak olarak “araç” diye nitelendirilmeleri polimorfizmdir. Yani, bir olguyu çoklu (poli) form (morfizmos) olarak tanımlayabilmekteyiz.

artık yavaştan programlamada geçelim.

Programlamada Polimorfizm Nerede Kullanılır?

Programlamada polimorfizm, aslında en başından itibaren kullanılmaktadır. Örneğin, elimizdeki herhangi bir byte türündeki veriyi ister byte istersek byte‘dan büyük olan herhangi bir türde tutmak çok biçimliliktir.

Ancak, çok biçimlilik denildiğinde, temel programlamadaki bu durumlardan ziyade nesne tabanlı programlamadaki getirileri önemlidir.

Polimorfizmin Kullanım Alanları

Nesne tabanlı programlamada polimorfizmi uygulamak için türler arasında kalıtımsal bir ilişki olması gerekmektedir. Yani, bir nesnenin başka bir nesne ile işaretlenebilmesi veya referans edilmesi için arada kalıtımsal bir bağ olmalıdır.

Başka bir deyişle, nesne tabanlı programlamada polimorfizm uygulamak istiyorsanız, türler arasında kalıtım uygulanmış olmalıdır. Aksi durumda polimorfizm uygulanması mümkün değildir.

Örnek: Polimorfizmin Kullanımı

Aşağıdaki örnek, polimorfizmin nesne tabanlı programlamada nasıl kullanıldığını göstermektedir:

C#
public class Hayvan
{
    public virtual void SesCikar()
    {
        Console.WriteLine("Hayvan sesi çıkarıyor.");
    }
}

public class Kopek : Hayvan
{
    public override void SesCikar()
    {
        Console.WriteLine("Hav Hav");
    }
}

public class Kedi : Hayvan
{
    public override void SesCikar()
    {
        Console.WriteLine("Miyav Miyav");
    }
}

public class Program
{
    public static void Main()
    {
        Hayvan hayvan1 = new Kopek();
        Hayvan hayvan2 = new Kedi();

        hayvan1.SesCikar(); // Hav Hav
        hayvan2.SesCikar(); // Miyav Miyav
    }
}

Bu örnekte, Hayvan sınıfı temel bir sınıf olarak tanımlanmış ve SesCikar metodu sanal (virtual) olarak işaretlenmiştir. Kopek ve Kedi sınıfları, Hayvan sınıfından türemiş ve SesCikar metodunu kendi türlerine özgü bir şekilde geçersiz kılmışlardır (override).

Programın Main metodunda, Hayvan türünde referanslarla Kopek ve Kedi nesneleri işaretlenmiştir. Bu, polimorfizmin bir örneğidir. Çünkü farklı türdeki nesneler (Kopek, Kedi), aynı temel tür (Hayvan) ile referans edilerek aynı metodun (SesCikar) farklı biçimlerde çalıştırılmasını sağlamaktadır.

Polimorfizm kalıtım ilişkisi

Bir nesneyi, kendi türünün dışındaki bir tür ile işaretleyebilmek veya referans edebilmek için, ilgili nesnenin o türden türetilmiş olması gerekmektedir. Bu, kalıtım ilişkisinin temelini oluşturur ve polimorfizmin uygulanabilmesini sağlar.

C#
namespace polimorfizm
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new B(); // A türünden bir referansla B nesnesi
            B b = new B(); // B türünden bir referansla B nesnesi
            C c = new B(); // C türünden bir referansla B nesnesi
        }
    }

    class A : C
    {
    }

    class B : A
    {
    }

    class C
    {
    }
}

Açıklama:

Açıklama

  • C sınıfı temel bir sınıftır.
  • A sınıfı, C sınıfından türemiştir.
  • B sınıfı ise A sınıfından türemiştir.

Bu yapıda, B sınıfı, A ve C sınıflarından türetilmiştir, dolayısıyla B nesnesi, A ve C türündeki referanslarla işaretlenebilir.

Kodda Polimorfizm

  • A a = new B(); ifadesi, A türündeki bir referansla B nesnesini işaretler. Bu, B nesnesinin A türünden bir nesne gibi davranabileceğini gösterir.
  • B b = new B(); ifadesi, B türündeki bir referansla B nesnesini işaretler.
  • C c = new B(); ifadesi, C türündeki bir referansla B nesnesini işaretler. Bu, B nesnesinin C türünden bir nesne gibi davranabileceğini gösterir.

Örnek

işte erkek nesnesinin insan referansıyla işaretlenebilmesi çok biçimlilik(Polimorfizm) dir.

Peki, Polimorfizm bir nesneye yönetiminde neye yarar?

Bir nesnenin birden fazla referansla işaretlenmesi, o nesnenin farklı türlerin davranışlarını sergilemesini sağlar. Bu durum, polimorfizmin temel prensiplerinden biridir ve nesne tabanlı programlamada önemli bir esneklik ve genişletilebilirlik sağlar.

Polimorfizm Türleri

1. static Polimorfizm(Compile-Time Polymorphism):

  • Metot overloading ile gerçekleştirilir.
  • Aynı isimde fakat farklı parametre listelerine sahip birden fazla metot tanımlanır.
  • Hangi metotun çağrılacağı derleme zamanında belirlenir.
  • Derleme zamanında karar verilmesi nedeniyle performans açısından daha hızlıdır.
C#
using System;

class Calculator
{
    // İki integer parametre alan metot
    public static int Add(int a, int b)
    {
        return a + b;
    }

    // İki double parametre alan metot
    public static double Add(double a, double b)
    {
        return a + b;
    }
}

class Program
{
    static void Main(string[] args)
    {
        int sum1 = Calculator.Add(5, 3);        // int tipindeki metot çağrılır
        double sum2 = Calculator.Add(2.5, 3.7); // double tipindeki metot çağrılır

        Console.WriteLine("Sum1: " + sum1);    // Çıktı: Sum1: 8
        Console.WriteLine("Sum2: " + sum2);    // Çıktı: Sum2: 6.2
    }
}

Bu örnekte, Calculator sınıfında iki farklı metot tanımlanmıştır. Her ikisi de Add ismini taşımaktadır, ancak farklı parametre listelerine sahiptirler (birisi int, diğeri double parametre alır). Hangi metotun çağrılacağı, argümanların türlerine göre derleme zamanında belirlenir.

2. Dinamik Polimorfizm (Run-Time Polymorphism):

  • Metot override ile gerçekleştirilir.
  • Temel sınıfta virtual olarak işaretlenmiş metotlar, alt sınıflarda override edilerek yeniden yazılır.
  • Hangi metotun çalışacağı çalışma zamanında belirlenir.
  • Çalışma zamanında karar verilmesi nedeniyle daha esnek ve genişletilebilirdir.
C#
using System;

class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound");
    }
}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Cat meows");
    }
}

class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Animal animal1 = new Cat(); // Cat sınıfından türetilmiş nesne, Animal referansıyla işaretlenir
        Animal animal2 = new Dog(); // Dog sınıfından türetilmiş nesne, Animal referansıyla işaretlenir

        animal1.MakeSound(); // Çıktı: Cat meows
        animal2.MakeSound(); // Çıktı: Dog barks
    }
}

Bu örnekte, Animal sınıfında MakeSound adında bir sanal metot tanımlanmıştır. Bu metot, alt sınıflar tarafından (Cat ve Dog) override edilerek yeniden yazılmıştır. Hangi metotun çağrılacağı, çalışma zamanında referansın gösterdiği nesnenin türüne bağlı olarak belirlenir. Bu nedenle, MakeSound metodu çalışma zamanında dinamik olarak seçilir.


Bu türlerin her biri, farklı durumlarda kullanılarak kodunuzun esnekliğini ve genişletilebilirliğini artırabilir. Statik polimorfizm, derleme zamanında performans avantajları sağlarken, dinamik polimorfizm ise çalışma zamanında esneklik ve genişletilebilirlik sağlar. Her ikisi de nesne tabanlı programlamanın güçlü araçlarıdır ve doğru senaryolarda kullanıldığında kodunuzu daha okunaklı, anlaşılır ve bakımı kolay hale getirebilir.

Polimorfizm durumlarında tür dönüşümleri

tür dönüşümleri polimorfizm durumlarında oldukça önemlidir. Bu dönüşümler, bir nesnenin kalıtımsal olarak ataları olan referanslar tarafından işaretlenmesini ve bu nesnenin farklı türlere dönüştürülmesini sağlar.

Örneğin, elimizdeki C sınıfı B sınıfından kalıtım alır ve B sınıfı da A sınıfından kalıtım alır.

C#
A a = new C();

Bu durumda, a referansı bir C türünden nesneyi işaretler, ancak A türünden bir referansla tutulur. İhtiyaç durumunda, bu referans A’nın ataları olan diğer referans türlerine veya kendi türüne dönüştürülebilir:

C#
C c = (C)a;

Bu dönüşümde, a referansındaki C türünden nesne, kendi türünde bir referansla işaretlenir. Bu tür dönüşümü sağlamak için cast operatörü kullanılır. Aynı şekilde, aşağıdaki gibi de bir dönüşüm yapılabilir:

C#
C c = new C();
A a = c; // veya A a = (A)c;

Bu durumda, c nesnesi doğrudan A türünden bir referansla işaretlenir. Burada da cast operatörü kullanılabilir, ancak çoğu durumda zaten alt sınıftan üst sınıfa otomatik bir tür dönüşümü gerçekleşir.

Polimorfizm durumlarında üst bir referansla işaretlenen bir nesne, kendi türünden bir referansla işaretlenmek için cast operatörü kullanılarak tür dönüşümü gerçekleştirilir. Bu durum, object türünde gerçekleştirilen Unboxing’e benzer bir mekanizma izler.

Object türünde gerçekleşen Unboxing işlemi, bir object türünden değer tip bir türe dönüşümüdür. Benzer şekilde, polimorfizm durumlarında yapılan tür dönüşümleri de bir üst sınıf referansından alt sınıf referansına dönüşümü ifade eder. Bu dönüşümler, nesne tabanlı programlamadaki genişletilebilirliği ve esnekliği destekler.

Dolayısıyla, object türünde gerçekleştirilen Unboxing işlemi, polimorfizmin bir sonucudur ve bir nesnenin farklı türlere dönüştürülmesini sağlayarak programın daha dinamik hale gelmesine katkıda bulunur.

Boxing ve Unboxing, değer türleri ile referans türleri arasında dönüşüm yapmayı sağlayan işlemlerdir. Bu işlemler genellikle C# gibi dillerde kullanılır. Detaylı anlatımı aşşağıda anlatıyorum.

Boxing (Kutulama):

  • Boxing işlemi, bir değer tipini (int, double, vb.) bir nesne türüne (object, interface, vb.) dönüştürme işlemidir.
  • Değer tipinin değeri, bellekte heap’te yeni bir alan oluşturularak saklanır ve referans tipi bir değişkene atanır.
  • Bu işlem genellikle polymorphism (çok biçimlilik) durumlarında, yani farklı türlerin aynı arayüzü paylaştığı durumlarda kullanılır.
    C#
    int i = 10;
    object obj = i; // Boxing işlemi

    Unboxing (Kutudan Çıkarma):

    • Unboxing işlemi, bir nesne türünü (object, interface, vb.) bir değer türüne (int, double, vb.) dönüştürme işlemidir.
    • Kutulanmış (boxed) bir nesnenin değeri, doğrudan bir değer türüne atanarak alınır.
    • Bu işlem, bir önceki kutulama işlemiyle elde edilen değerin orijinal değer tipine geri dönüştürülmesini sağlar.
      C#
      object obj = 10; // Boxing işlemi
      int i = (int)obj; // Unboxing işlemi

      Polimorfizm Durumlarında Tür Dönüşümleri

      polimorfizm durumlarında tür dönüşümleri gerçekleştirmek için cast veya as operatörleri kullanılabilir. Bu operatörler, bir nesneyi farklı türlere dönüştürmek için kullanılır ve her birinin kendine özgü kullanımı ve avantajları vardır.

      Cast operatörü

      Cast operatörü, üst türden alt türe kalıtımsal ilişkide dönüşüm sağlar. Bu işlem, bir nesnenin türetilmiş bir türden olduğunu belirtir ve bu türle ilgili işlemleri yapmanıza olanak tanır.

      C#
      class A
      {
          public void MethodA() => Console.WriteLine("Method in A");
      }
      
      class B : A
      {
          public void MethodB() => Console.WriteLine("Method in B");
      }
      
      // Nesne oluşturma ve dönüşüm
      A a = new B();
      B b = (B)a;
      b.MethodB(); // Output: Method in B
      

      Cast Operatörünün Kullanım Kuralları

      Kalıtımsal İlişki Olması Gerekliliği:

      • Eğer bir nesne, kalıtımsal ilişkisi olmayan bir türe dönüştürülmeye çalışılırsa derleyici hatası alırsınız.
      • Örneğin, aşağıdaki kod derleme hatası verecektir çünkü A ve C arasında kalıtımsal bir ilişki yoktur:
      C#
      class A { }
      class C { }
      A a = new A();
      C c = (C)a; // Derleyici hatası
      

      Fiziksel Nesnenin Hiyerarşik Altında Olma Durumu:

      • Eğer bir nesne, kalıtımsal ilişkide olup fiziksel olarak hiyerarşik altında olmayan bir türe dönüştürülmeye çalışılırsa, run time hatası (InvalidCastException) alınır.
      • Örneğin, aşağıdaki kod run time hatası verecektir çünkü a referansı aslında A türündendir ve B türüne dönüştürülmeye çalışılmaktadır:
      C#
      class A { }
      class B : A { }
      A a = new A();
      B b = (B)a; // Run time hatası: InvalidCastException
      

      as Operatörü

      as operatörü, kalıtımsal ilişki olan türler arasında referans dönüşümü yapmamızı sağlayan bir operatördür. as operatörü, cast operatörüne benzer şekilde çalışır ancak başarısız dönüşümler durumunda run time hatası yerine null döner.

      C#
      class A
      {
          public void MethodA() => Console.WriteLine("Method in A");
      }
      
      class B : A
      {
          public void MethodB() => Console.WriteLine("Method in B");
      }
      
      // Nesne oluşturma ve dönüşüm
      A a = new B();
      B b = a as B;
      
      if (b != null)
      {
          b.MethodB(); // Output: Method in B
      }
      else
      {
          Console.WriteLine("Dönüşüm başarısız.");
      }
      

      cast ve as Operatörlerinin Farkları

      • cast operatörü: Başarısız dönüşümler durumunda InvalidCastException fırlatır.
      • as operatörü: Başarısız dönüşümler durumunda null döner.

      Bu farklar, dönüşüm işlemlerinde hata kontrolünü ve hata yönetimini farklı şekillerde ele almamızı sağlar. as operatörü, dönüşümün başarısız olma ihtimaline karşı daha güvenli bir yaklaşım sunar, çünkü run time hatası yerine null döner. Bu, özellikle dönüşümün başarısız olma olasılığının yüksek olduğu durumlarda tercih edilebilir.

      is Operatörü

      is operatörü, kalıtsal ilişkiye sahip nesnelerin polimorfizm özelliğine nazaran fiziksel olarak hangi türde olduğunu belirlemeye yarayan bir operatördür. is operatörü, bir nesnenin belirtilen türe uygun olup olmadığını kontrol eder ve sonucunda true veya false değerini döndürür.

      • True Döner: Eğer nesne belirtilen türe uygun ise.
      • False Döner: Eğer nesne belirtilen türe uygun değilse veya kalıtsal bir ilişki yoksa.

      Bu operatör sayesinde, polimorfizm uygulanmış bir nesnenin, ihtiyaç doğrultusunda (uygun olan) farklı bir türe dönüştürülmesi için önce is kontrolü yapılabilir. Ardından, tür dönüşümü için Cast veya as operatörleri kullanılabilir. Bu yaklaşım, tür dönüşüm işlemlerinin güvenli ve hatasız bir şekilde yapılmasını sağlar.

      C#
      class A { }
      
      class B : A { }
      
      class Program
      {
          static void Main()
          {
              A a = new A();
              B b = new B();
              A aAsB = new B();
      
              Console.WriteLine(a is A);      // True
              Console.WriteLine(a is B);      // False
              Console.WriteLine(b is A);      // True (B, A'dan türediği için)
              Console.WriteLine(b is B);      // True
              Console.WriteLine(aAsB is A);   // True
              Console.WriteLine(aAsB is B);   // True (aAsB'nin fiziksel türü B'dir)
              Console.WriteLine(a is string); // False (A, string ile kalıtımsal ilişkiye sahip değil)
          }
      }
      

      is Operatörünün Özellikleri

      Tür Kontrolü:

      is operatörü, bir nesnenin belirli bir türe ait olup olmadığını kontrol etmek için kullanılır.Bu, özellikle bir nesnenin belirli bir türe ait olup olmadığını bilmeniz gerektiğinde yararlıdır.

      Güvenli Tür Dönüşümü:

      is operatörü ile bir tür kontrolü yaparak, daha sonra güvenli bir şekilde tür dönüşümü gerçekleştirebilirsiniz.

      C#
      A a = new B();
      if (a is B b)
      {
          b.MethodB(); // Güvenli tür dönüşümü ile MethodB'yi çağırabilirsiniz
      }
      else
      {
          Console.WriteLine("Dönüşüm başarısız.");
      }
      

      Bu kullanım, hem tür kontrolü hem de tür dönüşümünü aynı anda gerçekleştiren bir yapıdır ve is operatörünün sağladığı güvenlikten yararlanır.

      haliyle çok biçimlilik uygulanmış bir nesnenin ihtiyaç doğrultusunda ( uygun olan) farklı bir türe dönüştürülmesi için işi garantiye alabilmek adına önce is kontrolu ardından Cast yada as operasyonu sağlanması kafidir.

      Yorum Yap

      Bir yanıt yazın

      E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir