EDA Playground'da Dene

Kapsülleme (Encapsulation)

Gün 2: Nesne Yönelimli Programlama (OOP) | Erişim belirleyicileri: local, protected, public

Bu derste nesne yönelimli programlamanın temel ilkelerinden biri olan kapsüllemeyi (encapsulation) öğreneceğiz. Bir sınıfın iç verilerini dış dünyadan nasıl gizleyeceğimizi ve bu verilere yalnızca kontrollü metodlar (getter/setter) üzerinden erişim sağlamayı local, protected ve public belirleyicileriyle inceleyeceğiz.

Kapsülleme Nedir?

Kapsülleme, bir sınıfın verilerini ve bu veriler üzerinde çalışan metodları bir arada tutarak iç ayrıntıları dışarıdan gizleme ilkesidir. Amaç, nesnenin durumunun ancak izin verilen ve doğrulanan yollarla değiştirilebilmesini sağlamaktır. Bu, hatalı veya tutarsız durumların oluşmasını engeller.

SystemVerilog üç erişim belirleyicisi sunar:

  • public (varsayılan): Üyeye her yerden erişilebilir. Belirteç yazılmazsa üye public kabul edilir.
  • local: Üyeye yalnızca tanımlandığı sınıfın içinden erişilebilir. Alt sınıflar bile erişemez. En katı gizlilik düzeyidir.
  • protected: Üyeye tanımlandığı sınıf ve ondan türeyen alt sınıflardan erişilebilir, ancak sınıf dışından erişilemez.

Getter ve Setter Kalıbı

Gizli (local/protected) verilere dışarıdan erişim gerektiğinde, doğrudan erişim yerine public metodlar kullanılır:

  • Getter: Veriyi okumak için (get_balance() gibi).
  • Setter: Veriyi yazmak için, ama önce kontrol/doğrulama yaparak (deposit(), withdraw() gibi). Böylece geçersiz değerler engellenir.

Kaynak Kod

// =============================================================================
// GUN 2 - Konu 4: Kapsulleme (Encapsulation): local, protected, public
// =============================================================================

class BankAccount;
  // public: Disaridan erisilebilir (varsayilan)
  string owner;

  // local: Sadece bu sinif icinden erisilebilir
  local real balance;
  local int  account_no;
  local static int next_account = 1000;

  // protected: Bu sinif ve alt siniflardan erisilebilir
  protected string account_type;
  protected int    transaction_count;

  function new(string owner, real initial_balance = 0, string acc_type = "Standard");
    this.owner       = owner;
    this.balance     = initial_balance;
    this.account_type = acc_type;
    this.account_no  = next_account++;
    this.transaction_count = 0;
  endfunction

  // Public getter (okuma erisimi saglar)
  function real get_balance();
    return balance;
  endfunction

  function int get_account_no();
    return account_no;
  endfunction

  function int get_transaction_count();
    return transaction_count;
  endfunction

  // Public setter (kontrollu yazma)
  function bit deposit(real amount);
    if (amount <= 0) begin
      $display("  [HATA] Gecersiz miktar: %.2f", amount);
      return 0;
    end
    balance += amount;
    transaction_count++;
    $display("  [+] %.2f yatirildi. Yeni bakiye: %.2f", amount, balance);
    return 1;
  endfunction

  function bit withdraw(real amount);
    if (amount <= 0 || amount > balance) begin
      $display("  [HATA] Gecersiz cekim: %.2f (Bakiye: %.2f)", amount, balance);
      return 0;
    end
    balance -= amount;
    transaction_count++;
    $display("  [-] %.2f cekildi. Yeni bakiye: %.2f", amount, balance);
    return 1;
  endfunction

  function void display();
    $display("  Hesap #%0d | Sahip: %s | Tip: %s | Bakiye: %.2f | Islem: %0d",
             account_no, owner, account_type, balance, transaction_count);
  endfunction
endclass

// Alt sinif: protected'a erisebilir, local'a erisemez
class PremiumAccount extends BankAccount;
  local real credit_limit;

  function new(string owner, real initial_balance, real credit_limit = 5000);
    super.new(owner, initial_balance, "Premium");
    this.credit_limit = credit_limit;
  endfunction

  // protected uyelere erisim (OK)
  function void show_details();
    $display("  [Premium] Tip: %s, Islem sayisi: %0d, Kredi limiti: %.2f",
             account_type, transaction_count, credit_limit);
  endfunction
endclass

module kapsulleme;
  initial begin
    BankAccount    acc1;
    PremiumAccount acc2;

    $display("=== Kapsulleme (Encapsulation) ===\n");

    // --- Public erisim ---
    $display("--- Hesap Olusturma ---");
    acc1 = new("Ali", 1000.0);
    acc2 = new("Ayse", 5000.0, 10000.0);
    acc1.display();
    acc2.display();

    // --- Kontrollu erisim (getter/setter) ---
    $display("\n--- Islemler (Kontrollu Erisim) ---");
    void'(acc1.deposit(500));
    void'(acc1.withdraw(200));
    void'(acc1.withdraw(99999));   // Hata: yetersiz bakiye
    void'(acc1.deposit(-100));     // Hata: negatif miktar
    
    $display("\n  Bakiye (getter ile): %.2f", acc1.get_balance());
    $display("  Islem sayisi: %0d", acc1.get_transaction_count());

    // --- local erisim denemesi (derleme hatasi verir) ---
    // acc1.balance = 999999;       // HATA: 'balance' local
    // acc1.account_no = 0;         // HATA: 'account_no' local

    // --- protected erisim (alt siniftan) ---
    $display("\n--- Protected Erisim (Alt Sinif) ---");
    void'(acc2.deposit(2000));
    acc2.show_details();

    // --- Sonuc ---
    $display("\n--- Sonuc ---");
    acc1.display();
    acc2.display();

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

Kodun Açıklaması

  • BankAccount sınıfı erişim düzeyleri: owner public'tir (dışarıdan erişilebilir). balance, account_no ve next_account local'dir (sadece sınıf içinden). account_type ve transaction_count ise protected'tir (sınıf ve alt sınıflardan).
  • local static next_account: Hem local hem static olduğu için sınıfa özel ortak bir sayaçtır. Her constructor'da account_no = next_account++ ile benzersiz, ardışık hesap numaraları üretilir.
  • Getter metodları: get_balance(), get_account_no() ve get_transaction_count(), local/protected alanların değerlerini dışarıya güvenli biçimde okuma erişimi sağlar.
  • Kontrollü setter'lar: deposit() miktarın pozitif olduğunu, withdraw() ise miktarın geçerli ve bakiyeden küçük olduğunu doğrular. Geçersiz durumda hata yazıp 0 döndürür, bakiyeyi bozmaz. Bu, kapsüllemenin asıl değeridir: balance'a yalnızca kuralları geçen değerler işlenebilir.
  • PremiumAccount alt sınıfı: BankAccount'tan türer ve super.new(...) ile üst sınıf constructor'ını çağırır. show_details() metodu account_type ve transaction_count (protected) alanlarına erişebilir; ancak üst sınıfın local balance alanına erişemez.
  • Erişim hatası örneği: Koddaki yorum satırı acc1.balance = 999999; etkin olsaydı, balance local olduğu için derleme hatası verirdi. Bu, kapsüllemenin derleyici düzeyinde nasıl zorlandığını gösterir.

Önemli Noktalar

  • Bir üye için belirteç yazılmazsa varsayılan public'tir; gizlilik istiyorsanız açıkça local veya protected yazmalısınız.
  • local ile protected arasındaki fark kalıtımdadır: protected üyeler alt sınıflardan erişilebilir, local üyeler erişilemez.
  • Veri alanlarını gizleyip erişimi getter/setter üzerinden vermek, doğrulama (validation) eklemenize ve nesnenin tutarlılığını korumanıza imkân tanır.
  • Erişim ihlalleri derleme zamanında yakalanır; bu da hataları simülasyon çalışmadan önce ortaya çıkarır.
  • Benzersiz kimlik üretimi için local static bir sayaç kalıbı (next_account++) çok kullanışlıdır; sayaç dış müdahaleye kapalı kalır.