EDA Playground'da Dene

Çok Biçimlilik (Polymorphism)

Gün 2: Nesne Yönelimli Programlama (OOP) | Virtual metodlar, upcasting, $cast ile downcasting

Bu derste çok biçimliliği (polymorphism) öğreneceğiz: Bir üst sınıf handle'ı üzerinden çağrılan metodun, çalışma zamanında nesnenin gerçek tipine göre seçilmesini sağlayan virtual metodları, upcasting'i ve $cast ile güvenli downcasting'i inceleyeceğiz.

Polimorfizm ve virtual Metodlar

Polimorfizm, "tek arayüz, çok davranış" demektir. Aynı metod çağrısının (speak()), çağrıldığı nesnenin tipine göre farklı sonuç vermesidir. Bunun anahtarı virtual anahtar kelimesidir:

  • Bir metod virtual olarak işaretlenirse, hangi sürümün çalışacağı derleme zamanında handle'ın tipine değil, çalışma zamanında nesnenin gerçek tipine göre belirlenir. Buna dinamik bağlama (dynamic dispatch) denir.
  • virtual olmayan bir metodda ise handle'ın bildirilen tipi belirleyicidir; bu polimorfik davranışı engeller.

Bu sayede bir üst sınıf dizisinde (Animal animals[4]) farklı alt sınıf nesneleri tutup hepsi üzerinde aynı display() çağrısını yaptığınızda, her nesne kendi özelleşmiş sürümünü çalıştırır.

Upcasting ve Downcasting ($cast)

  • Upcasting: Bir alt sınıf nesnesini üst sınıf handle'ına atamaktır (animals[1] = d;). Her zaman güvenlidir ve örtük (implicit) yapılır, çünkü bir Dog zaten bir Animal'dır.
  • Downcasting: Üst sınıf handle'ından alt sınıf handle'ına dönüştürmektir. Bu tehlikelidir çünkü handle gerçekte beklenen alt tipi işaret etmeyebilir. Bu yüzden $cast(hedef, kaynak) ile yapılır; dönüşüm başarılıysa 1, değilse 0 döndürür ve böylece güvenli kontrol sağlanır.

Kaynak Kod

// =============================================================================
// GUN 2 - Konu 6: Cok Bicimlilik (Polymorphism) ve Sanal (Virtual) Metodlar
// =============================================================================

class Animal;
  string name;
  int    age;

  function new(string name, int age);
    this.name = name;
    this.age  = age;
  endfunction

  // virtual: Alt siniflarda override edilebilir
  virtual function string speak();
    return "...";
  endfunction

  virtual function void display();
    $display("  [%s] Isim: %s, Yas: %0d, Ses: %s",
             get_type(), name, age, speak());
  endfunction

  virtual function string get_type();
    return "Animal";
  endfunction
endclass

class Dog extends Animal;
  string breed;
  
  function new(string name, int age, string breed = "Mixed");
    super.new(name, age);
    this.breed = breed;
  endfunction

  virtual function string speak();
    return "Hav hav!";
  endfunction

  virtual function string get_type();
    return "Dog";
  endfunction

  virtual function void display();
    super.display();
    $display("         Cins: %s", breed);
  endfunction
endclass

class Cat extends Animal;
  bit is_indoor;

  function new(string name, int age, bit indoor = 1);
    super.new(name, age);
    this.is_indoor = indoor;
  endfunction

  virtual function string speak();
    return "Miyav!";
  endfunction

  virtual function string get_type();
    return "Cat";
  endfunction

  virtual function void display();
    super.display();
    $display("         Ev kedisi: %s", is_indoor ? "Evet" : "Hayir");
  endfunction
endclass

class Parrot extends Animal;
  string vocabulary [$];

  function new(string name, int age);
    super.new(name, age);
  endfunction

  function void learn_word(string word);
    vocabulary.push_back(word);
  endfunction

  virtual function string speak();
    if (vocabulary.size() > 0) begin
      int idx = $urandom % vocabulary.size();
      return vocabulary[idx];
    end
    return "Cik cik!";
  endfunction

  virtual function string get_type();
    return "Parrot";
  endfunction
endclass

module polymorphism;
  initial begin
    // Ust sinif dizisi ile tum hayvanlari tutma (Polimorfizm)
    Animal animals [4];
    Dog    d;
    Cat    c;
    Parrot p;

    Animal a_ref;  // Dog nesnesi, Animal referansi
    Dog    d_ref;
    
    $display("=== Polimorfizm (Polymorphism) ===\n");

    // --- Nesneler olustur ---
    d = new("Karabas", 3, "Golden Retriever");
    c = new("Tekir", 2, 0);
    p = new("Papagan", 5);
    p.learn_word("Merhaba!");
    p.learn_word("Gunaydin!");
    p.learn_word("Nasilsin?");

    // --- Polimorfik dizi ---
    animals[0] = new("Bilinmeyen", 0);  // Base
    animals[1] = d;                      // Dog -> Animal (upcasting)
    animals[2] = c;                      // Cat -> Animal
    animals[3] = p;                      // Parrot -> Animal

    // --- virtual metod cagrilari ---
    $display("--- Polimorfik display() ---");
    foreach (animals[i]) begin
      $display("\n  Hayvan #%0d:", i);
      animals[i].display();  // Her nesne kendi override'ini calistirir
    end

    // --- $cast ile downcasting ---
    $display("\n--- $cast ile Downcasting ---");
    
    a_ref = d;  // Dog nesnesi, Animal referansi

    // Guvenli donusum
    if ($cast(d_ref, a_ref)) begin
      $display("  Cast basarili! Cins: %s", d_ref.breed);
    end else begin
      $display("  Cast basarisiz!");
    end

    // Yanlis cast denemesi
    a_ref = c;  // Cat nesnesi
    if (!$cast(d_ref, a_ref))
      $display("  Cat -> Dog cast basarisiz (beklenen)");
    

    // --- Polimorfik fonksiyon ---
    $display("\n--- Polimorfik Fonksiyon ---");
    begin
      foreach (animals[i]) begin
        make_speak(animals[i]);
      end
    end

    $display("\n=== Polimorfizm Sonu ===");
    $finish;
  end

  // Herhangi bir Animal alt sinifini kabul eder
  function void make_speak(Animal a);
    $display("  %s diyor: %s", a.name, a.speak());
  endfunction
endmodule

Kodun Açıklaması

  • Animal temel sınıfı: name, age alanları ve üç virtual metod (speak(), display(), get_type()) içerir. virtual olmaları, alt sınıfların bu metodları geçersiz kılabilmesini (override) ve polimorfik çağrıyı mümkün kılar.
  • Alt sınıflar: Dog, Cat ve Parrot, Animal'dan türer ve speak() ile get_type() metodlarını kendilerine göre yeniden tanımlar. Dog ve Cat ayrıca display()'i geçersiz kılarken içinde super.display() çağırarak temel çıktının üzerine ek bilgi yazar.
  • Parrot ve kuyruk: vocabulary bir kuyruktur (string [$]). learn_word() ile kelime eklenir; speak() kuyrukta kelime varsa $urandom ile rastgele birini döndürür.
  • Polimorfik dizi: Animal animals[4] dizisine farklı tipte nesneler atanır (upcasting). animals[1] = d; ile bir Dog, Animal handle'ına yerleştirilir.
  • Dinamik bağlama: foreach döngüsünde animals[i].display() çağrılır. Metod virtual olduğu için her eleman kendi gerçek tipinin display() ve speak() sürümünü çalıştırır; handle Animal tipinde olmasına rağmen.
  • $cast ile downcasting: a_ref = d; sonrası $cast(d_ref, a_ref) başarılı olur çünkü a_ref gerçekte bir Dog'u işaret eder. Ancak a_ref = c; (bir Cat) yapıldığında $cast(d_ref, a_ref) başarısız olur ve 0 döndürür; bu yüzden !$cast(...) koşulu beklenen şekilde çalışır.
  • Polimorfik fonksiyon: make_speak(Animal a) herhangi bir Animal alt sınıfını kabul eder ve a.speak() çağrısı yine gerçek tipe göre çözülür.

Önemli Noktalar

  • Polimorfik davranış için metodlar virtual olmalıdır. virtual olmazsa, handle'ın bildirilen tipine göre statik bağlama yapılır ve override edilen sürüm çağrılmaz.
  • virtual bir metod, kalıtım zinciri boyunca türetilen sınıflarda da virtual kalır; alt sınıflarda tekrar virtual yazmak iyi bir okunabilirlik pratiğidir.
  • Downcasting için her zaman $cast kullanın ve dönüş değerini kontrol edin; doğrudan atama derleme hatası verir veya güvensizdir. $cast başarısızsa 0 döner ve çalışma zamanı hatasından kaçınılır.
  • Upcasting örtüktür ve daima güvenlidir; downcasting ise yalnızca handle gerçekten hedef tipi işaret ediyorsa başarılı olur.
  • super.metot() çağrısı, override edilen bir metodun üst sınıf davranışını koruyup üzerine ekleme yapmak için kullanılır (örneğin Dog::display içindeki super.display()).
  • Polimorfizm, UVM gibi doğrulama metodolojilerinin temelidir: ortak bir temel transaction/bileşen arayüzü üzerinden farklı türler tek tip kodla işlenebilir.