Çokbiçimlilik (Polymorphism)

Lisans: Creative Commons 11.12.2020 tarihinde güncellendi
Bakabileceğiniz Etiketler: Eğitmen: Geleceği Yazanlar Ekibi

Çokbiçimlilik (Polymorphism) bir nesnenin farklı amaçlar için de kullanılabileceği anlamına gelir ve statik ve dinamik olmak üzere ikiye ayrılır.

  • Statik çokbiçimlilik; metot ve operatörlerin aşırı yüklenmesi (overload) olarak belirtilir.
  • Dinamik çokbiçimlilik; özet sınıflardan miras alma yoluyla işlemlerin gerçekleştirilmesi işlemine verilen isimdir.

Çokbiçimlilik konusunun daha iyi anlaşılması için, statik çokbiçimlilik türünden, metotların aşırı yüklenmesine yönelik bir çalışma yapacağız.

Öncelikle iki adet enum oluşturalım: Hareket ve CanliTip.

 

enumCanliTip
{
    Insan=0,
    Hayvan=1
}
 
enumHareket
{
    Havla=0,
    KuyrukSalla=1,
    PatiUzat=2
}

Ardından; üç adet static void metot oluşturalım.

static void Konus(CanliTip e,string ifade)
{
    Console.WriteLine("{0}der ki: '{1}'",e,ifade);
}
 
static void Konus(string ifade)
{
    Console.WriteLine(ifade);
}
 
static void Konus(CanliTip e , Hareket f)
{
    Console.WriteLine("{0}şunu yaptı: {1}",e,f);
}

Şimdi de, bu metotlarımızı projede kullanalım:

static void Main(string[] args)
{
    Konus(CanliTip.Insan,"ben bir insanım");
    Konus(CanliTip.Hayvan , Hareket.Pati);
    Konus("Hav");
    Console.ReadLine();
}

Uygulama çalıştırıldığında aşağıdaki gibi bir çıktı alınır:

  • İnsan der ki:'ben bir insanım'
  • Hayvan şunu yaptı: PatiUzat
  • Hav

Sonuç olarak, çokbiçimlilik bir nesnenin bir işlemi farklı şekillerde yapabileceğini gösteren bir kavramdır. Birbirine benzeyen nesneleri ortak özellikleriyle ele alabilmenizi ya da bir nesnenin aynı işi farklı şekillerde yapabilmesini sağlar. Çokbiçimlilik diğer konulara kıyasla daha soyut kaldığı için örnekleri artırmakta fayda var. Aşağıdaki örnekte farklı bir statik çokbiçimlilik yapısını göreceğiz. Bu örnekte yazıcıdan farklı değerlerin çıktısını almak için mantıklı olarak tek bir metot aracılığıyla işlem gerçekleştireceğiz.

// Yazıcı sınıfı ile çıktısını almak istediğimiz değerleri tek bir metot ile farklı formatlarda çıkarttırabiliriz.
// Nedenle içine Yazdir isimli iki metot tanımlıyor ve aldıkları parametreleri değiştiriyoruz.

        public class Yazici
        {
            public void Yazdir(int i)

            {
                Console.WriteLine("Sayısal değer yazdırılıyor - {0}", i);
            }

            public void Yazdir(string s)
            {
                Console.WriteLine("Metinsel değer yazdırılıyor - {0}", s);
            }
        }

static void Main(string[] args)
        {
            Yazici yazici = new Yazici();
            yazici.Yazdir("Turkcell Geleceği Yazanlar");
            yazici.Yazdir(1);
            Console.WriteLine("İşlem tamamlanmıştır");
            Console.ReadKey();
        }

Yukarıdaki örnek gibi birçok farklı örnek çıkartabiliriz. Örneğin, okuma metodu olan bir Okuyucu sınıfı. Klavyeden, veritabanından veya farklı cihazlardan gelen bilgileri farklı parametrelere sahip aynı isimli metot ile okuyabiliriz.

Uygulama çalıştığında aşağıdaki çıktıyı verecektir.

Metinsel değer yazdırılıyor - Turkcell Geleceği Yazanlar

Sayısal değer yazdırılıyor – 1

İşlem tamamlanmıştır

Dinamik çokbiçimlilik ise biraz daha karmaşık bir yapıda yukarıdaki örnekleri gerçekleştirebileceğimiz formattadır. Dinamik çokbiçimlilik ile ilgili örnek vermeden önce kullanacağımız bir özelliğin tanımını gerçekleştirelim. Dinamik çokbiçimlilik aynı zamanda runtime polymorphism (çalışma zamanı çokbiçimliliği) olarak bilinir. 

Abstract (soyut) sınıflar, aslında daha önceki konularda gördüğümüz arayüzlerle benzerlik göstermektedirler. Aynı arayüzlerde olduğu gibi kalıtım alındıkları sınıflara şablon getirmektedir. Şablonu oluşturmak için Abstract Sınıfların içerisine Abstract özellikler eklenmektedir (Örn: Metot, Değişken gibi). Buna ek olarak, normal özellikler ekleyip bunların içerisine kod yazmanız da mümkündür. Aslında Abstract Sınıfları Base Class (Ana Sınıf) olarak düşünebiliriz. 

Daha önceki konularımızda işlediğimiz kalıtım alma işlemini hatırlayalım. Yazmış olduğumuz bir Abstract Sınıfı alt sınıflara kalıtım aldırarak düzen oluşturmuş, aynı zamanda çatıda yani abstract sınıfta yazdığımız kodlar ile ortak kullanım alanları oluşturabiliriz. Son olarak Abstract sınıfların örneği alınamaz bilgisini verdikten sonra örneğimize doğru devam ediyoruz.

Dinamik çokbiçimlilikteki örneğimiz için öncelikle Abstract sınıfımızı oluşturalım.

public abstract class Yazici
    {
        //aşağıdaki gibi aynı tipte olan değişkenleri tek satırda tanımlayabiliyoruz.
        protected int Genislik, Yukseklik;
        //daha önceki derslerimizde öğrendiğimiz constructor (yapıcı) metodu tanımlıyoruz.
        //bu sayede sınıf örneği alınırken genislik ve yükseklik değerlerini vermek zorunlu bırakılıyor.

        public Yazici(int genislik, int yukseklik)
        {
            //yapıcı metoda gönderilen değerler Yazici sınıfının içerisindeki Genislik ve Yukseklik
            //özelliklerine atanıyor. Bu atama sırasında this anahtar kelimesi sayesinde sınıfın kendi
            //öz kaynaklarına erişebiliyoruz.
            this.Genislik = genislik;
            this.Yukseklik = yukseklik;
        }
        //Yazici sınıfını kalıtım alacak bütün sınıflar Yazdir metodunu kullanacaklardır.
        public abstract void Yazdir();
   }

Sırada Yazici sınıfını kalıtım alacak, ResimYazicisi ve MetinYazicisi sınıflarımızı oluşturmak var.

public class ResimYazicisi : Yazici
    {
        //Image tipi resimler ile ilgili işlemler yapmak istediğimiz zaman kullandığımız .Net Framework sınıfıdır.
        //Kullanabilmek için System.Drawing referansını projemize eklememiz gerekir.
     //Temsilen eklenmiştir. Image işlemleri yapmayacağımızı için kullanılmayacaktır.
        protected Image Resim;

        //Kalıtım alınan sınıfın (Yazici sınıfı) constructor'ında parametre bekleniyor ise base anahtar kelimesi ile
        //kalıtım alan sınıftan bu parametrelerin içeriye gönderilmesi gerekmektedir.
        public ResimYazicisi(int genislik, int yukseklik):
            base(genislik, yukseklik)
        { }

     //Başarılı şekilde yazdırıldığını bildirmek için geriye True/False değeri döndürüyoruz.
        public override bool Yazdir()
        {
            Console.WriteLine("Resim yazdırılıyor. Boyutları : Genişlik {0} Uzunluk {1}", Genislik, Yukseklik);
         return true;
        }
    }

    public class MetinYazici : Yazici
    {
        protected string Metin;
        public MetinYazici(int genislik, int yukseklik)
            :base(genislik, yukseklik)
        { }
        
        public override bool Yazdir()
        {
            Console.WriteLine("Metin yazdırılıyor. Boyutları : Genişlik {0} Uzunluk {1}", Genislik, Yukseklik);
         return false;
        }
    }

Örneğimizin önemli bileşenlerinden bir tanesi Cagirici sınıfıdır. Bu sınıf ile ortak Yazdir metodunu çalıştırıp sonuçlarını tek bir yerden yönetebileceğiz.

public class Cagirici
    {
        //YaziciCagir metodu ile ResimYazicisi ve MetinYazicisi sınıfları içerisinde Yazdir metodunun kullanımı hedeflenmektedir.
        //Her iki sınıfta aynı üst sınıfı kalıtım aldığı için parametre olarak içeriye Yazici sınıfı gönderilebiliyor.
        //Aslında içeriye gönderilen Yazici sınıfı gibi görünen ResimYazicisi veya MetinYazicisi sınıflarından biridir.

        public void YaziciCagir(Yazici yazici)
        {
            bool yazdirildi;
            yazdirildi = yazici.Yazdir();
            if(yazdirildi)
                Console.WriteLine("Yazdırma işlemi başarılı");
            else
                Console.WriteLine("Yazdırma işlemi başarısızdır");
        }
    }

Örneğimizin son parçası, MetinYazici ve ResimYazici sınıflarını kullandığımız kod bloğu aşağıdaki gibidir:

static void Main(string[] args)

        {
            Cagirici cagirici = new Cagirici();
            ResimYazicisi r = new ResimYazicisi(200, 100);
            MetinYazici m = new MetinYazici(35, 50);
            cagirici.YaziciCagir(r);
            cagirici.YaziciCagir(m);
            Console.ReadKey();
        }

Program çıktısı aşağıdaki gibi olacak:

Resim yazdırılıyor. Boyutları : Genişlik 200 Uzunluk 100

Yazdırma işlemi başarılı

Metin yazdırılıyor. Boyutları : Genişlik 35 Uzunluk 50

Yazdırma işlemi başarısızdır

Dinamik çokbiçimlilik örneğimizi bir de diyagramla yorumlayalım: