Yaşadığım bu kodlama dünyasında bazen öyle durumlar oluyor ki, insanın aklına gelmiyor değil, “Acaba bunu daha iyi yapamaz mıyız?” diye. İşte tam da bu noktada, nesne yönelimli programlamanın (OOP) o sihirli dünyasına dalıyoruz. Polymorphism, yani çok biçimlilik, tam da böyle bir şey. Hani bir şeyi farklı farklı şekillerde kullanabilmek gibi düşünün. Mesela bir kumanda düşünün, aynı kumandayla televizyonun sesini açabiliyorsunuz, kanalını değiştirebiliyorsunuz, menüsüne girebiliyorsunuz. İşte bu da bir nevi çok biçimlilik. Programlama dünyasında da benzer mantıkla ilerliyor her şey. 🙂
Polymorphism’i iki ana başlık altında topluyoruz aslında: Override ve Overload. Bunlar, aynı isimli ama farklı işlevlere sahip metotları (fonksiyonları) kontrol altına almamızı sağlayan süper kahramanlarımız gibi. Biri kalıtım yoluyla gelirken, diğeri aynı sınıf içinde farklılık gösteriyor. Şimdi bu iki kahramanımızı biraz daha yakından tanıyalım, ne dersiniz?
Öncelikle Override’a bakalım. Bu, kalıtım alan bir sınıfın (çocuk sınıf), kalıtım veren sınıftan (ebeveyn sınıf) devraldığı bir metodu kendi ihtiyacına göre yeniden tanımlaması demek. Yani, ebeveynin bir sesi var ama çocuk diyor ki, “Ben bu sesi biraz değiştirip kendi tarzımda söyleyeceğim.” İşte bu “yeniden tanımlama” olayına biz Override diyoruz. Bu sayede, aynı isimli metot farklı sınıflarda farklı davranışlar sergileyebiliyor. Düşünsenize, bir ‘Hayvan’ sınıfı var ve içinde ‘sesCikar()’ metodu var. Köpek sınıfı bunu ‘Hav hav!’ diye, kedi sınıfı da ‘Miyav!’ diye override edebilir. Ne kadar güzel değil mi?
Bu arada, Override yaparken dikkat etmemiz gereken bazı şeyler var tabii. Metodun adı, parametreleri ve dönüş tipi aynı olmak zorunda. Hani ebeveynin imzasını birebir almak gibi düşünün, sadece içini kendi dolduruyorsunuz. Eğer bu kurallara uymazsanız, o zaman metot yeni bir metot olur, Override yapmış olmazsınız. Tabi bu küçük bir detay ama önemli.
Şimdi gelelim Overload’a. Bu biraz daha farklı bir durum. Overload, aynı sınıf içinde, aynı isimde ama farklı parametre listelerine sahip birden fazla metot oluşturmamızı sağlıyor. Parametreler farklı olunca, compiler hangi metodu çağıracağımıza karar verebiliyor. Mesela, bir ‘Hesapla()’ metodu var. Bir tanesi iki sayıyı toplar, diğeri üç sayıyı toplar, bir diğeri de bir sayıyla bir string’i birleştirir. Hepsinin adı ‘Hesapla!’ ama aldıkları malzemeye göre farklı işler yapıyorlar. Bu da ne kadar esnek bir yapı sağladığını gösteriyor.
Overload kullanırken, metotların isimleri aynı olabilir ama parametre sayıları, tipleri veya sıralamaları farklı olmalı. Dönüş tiplerinin farklı olması Overload için yeterli değil, bunu unutmamak lazım. Yani, ‘int Topla(int a, int b)’ ve ‘string Topla(int a, int b)’ gibi bir durum söz konusu olamaz. Compiler burada kafayı yerdi herhalde. 🙂 Ama ‘int Topla(int a, int b)’ ve ‘int Topla(int a, int b, int c)’ olabilir. Ya da ‘void Yaz(string mesaj)’ ve ‘void Yaz(int sayi)’ olabilir.
Neticede, bu iki özellik sayesinde kodumuz hem daha okunaklı hem de daha esnek hale geliyor. Düşünsenize, her seferinde farklı isimlerde metotlar yazmak yerine, aynı işlevi yerine getiren ama farklı senaryolara uyum sağlayan metotlara sahip olmak ne kadar kolaylaştırıyor işleri.
Şimdi gelelim işin en can alıcı kısmına: kod örnekleri! Bu teorik bilgileri biraz ete kemiğe büründürelim.
Override Örneği
Önce basit bir ‘Hayvan’ sınıfı oluşturalım. Sonra da ‘Kopek’ ve ‘Kedi’ sınıflarını bu ‘Hayvan’ sınıfından türetip, ‘sesCikar’ metodunu override edelim.
// Ebeveyn Sınıf public class Hayvan { public virtual void SesCikar() // 'virtual' keyword'ü override'a izin verir { Console.WriteLine("Hayvan genel bir ses çıkarıyor."); } }// Türetilmiş Sınıf 1 public class Kopek : Hayvan { public override void SesCikar() // 'override' keyword'ü ile metodu yeniden tanımlıyoruz { Console.WriteLine("Hav Hav!"); } }
// Türetilmiş Sınıf 2 public class Kedi : Hayvan { public override void SesCikar() { Console.WriteLine("Miyav!"); } }
Şimdi bu kodu çalıştırdığımızda ne görüyoruz? Bir ‘Hayvan’ nesnesi oluşturup ‘SesCikar()’ dediğimizde genel sesi duyuyoruz. Ama bir ‘Kopek’ nesnesi oluşturup aynı metodu çağırdığımızda ‘Hav Hav!’ diyor. Bir ‘Kedi’ nesnesi için de aynı şekilde ‘Miyav!’ sesini duyuyoruz. İşte bu, Override’ın gücü! Aynı metod çağrısı, nesnenin türüne göre farklı sonuçlar veriyor.
Overload Örneği
Şimdi de Overload’a bakalım. Basit bir ‘Hesaplayici’ sınıfı yapalım ve içine farklı parametrelerle ‘Topla’ metotları ekleyelim.
public class Hesaplayici { // İki integer'ı toplama public int Topla(int a, int b) { return a + b; } // Üç integer'ı toplama public int Topla(int a, int b, int c) { return a + b + c; }
// İki double'ı toplama public double Topla(double a, double b) { return a + b; }
// Bir string ve bir integer'ı birleştirme public string Topla(string str, int sayi) { return str + sayi.ToString(); } }
Burada da ‘Hesaplayici’ sınıfından bir nesne oluşturup, farklı sayıda veya tipte parametrelerle ‘Topla’ metodunu çağırdığımızda, C# compiler doğru metodu seçerek işlemi gerçekleştiriyor. Mesela ‘hesaplayici.Topla(5, 10)’ dediğinizde ilk metot çağrılırken, ‘hesaplayici.Topla(5, 10, 15)’ dediğinizde ikinci metot çalışır. Bu da Overload sayesinde oluyor.
Bana göre, bu özellikler yazılım geliştirirken bize inanılmaz bir esneklik ve düzen sağlıyor. Hani kodunuzu okurken “Aa, bu tam da benim istediğim gibi çalışıyor!” dediğiniz anlar olur ya, işte o anların çoğu Polymorphism sayesinde oluyor.
Gerçi bu işlerin pratiği, teorisinden biraz daha farklı olabiliyor bazen. Hani kod yazarken bir bakıyorsunuz hata üstüne hata. Ama işte deneye deneye, yanılta yanılta öğreniyoruz bunları da. Kendi programım sınıfta kaldı diyerek espri yapıyorum ama aslında her hata bize bir şeyler öğretiyor. 🙂
Neyse efendim, gelelim benim kişisel bir anıma. Geçenlerde bir arkadaşım, C# ile basit bir oyun yapmaya çalışıyordu. Karakterin hareketini kontrol etmek için bir metot yazmıştı. Oyunda karakteri sağa, sola, ileri, geri hareket ettiriyordu. Ama her yön için ayrı ayrı metot yazmıştı: ‘GitSag’, ‘GitSol’, ‘GitIleri’, ‘GitGeri’ gibi. Ben de dedim ki, “Ya bak, sen bunu Polymorphism ile çok daha basit hale getirebilirsin. Bir ‘HareketEt(Yön yon)’ metodu tanımlayıp, sonra da farklı yönler için enum kullanarak bunu override edebilirsin.” Arkadaşım başta anlamadı ama sonra örneği gösterince olayın ne kadar basit ve etkili olduğunu anladı. İşte bu tür durumlarda Polymorphism’in ne kadar hayat kurtarıcı olabildiğini görüyorsunuz.
Sonuç olarak, bu iki kavram, yani Override ve Overload, yazılım geliştiricinin elindeki güçlü araçlardır. Kodunuzu daha anlaşılır, daha yönetilebilir ve daha esnek hale getirirler. Bu yüzden, nesne yönelimli programlamanın bu temel taşlarını iyice öğrenmek ve kodlarınızda etkin bir şekilde kullanmak size büyük fayda sağlayacaktır.
Bu arada, bu konuyla ilgili daha fazla bilgi edinmek isterseniz, Microsoft’un kendi dokümantasyonlarına göz atabilirsiniz. Genelde bu tür temel konuları en iyi onlar açıklar. Mesela Google’da C# Polymorphism MSDN diye aratırsanız, bolca kaynak bulabilirsiniz. Ya da YouTube’da da görsel anlatımlar mevcut. Oradan da bakabilirsiniz.
Bir de şey var tabi, bazen çok karmaşık hale getirebiliyor insan işleri. Hani her şeyi en ince detayına kadar düşünmeye çalışırken, işin içinden çıkamaz hale geliyor. Ama neticede, bu iki konsepti doğru anlamak ve doğru yerlerde kullanmak, kod kalitesini inanılmaz artırıyor. Ben de kendi projelerimde bunları kullanmaya özen gösteriyorum, hem daha hızlı geliştirme yapıyorum hem de kodum daha temiz oluyor. Siz de deneyin derim.
Ne güzel değil mi? Kendi kendini çağıran metodlar, farklı davranışlar sergileyen nesneler… Programlama bu yüzden eğlenceli işte.
Bu arada, benim programım bir keresinde bir hata yüzünden tamamen çökmüştü sanırım. Bir kamp gezisindeydik ve gece laptop’umun şarjı bitti, bütün gece yazdığım kodlar gitti. O an gerçekten çok sinirlenmiştim ama sonra düşündüm ki, “Neyse, ne yapalım, ders oldu.” O günden sonra her zaman yedek almayı öğrendim. 🙂