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 üyepublickabul 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ı
BankAccountsınıfı erişim düzeyleri:ownerpublic'tir (dışarıdan erişilebilir).balance,account_novenext_accountlocal'dir (sadece sınıf içinden).account_typevetransaction_countiseprotected'tir (sınıf ve alt sınıflardan).local static next_account: Hemlocalhemstaticolduğu için sınıfa özel ortak bir sayaçtır. Her constructor'daaccount_no = next_account++ile benzersiz, ardışık hesap numaraları üretilir.- Getter metodları:
get_balance(),get_account_no()veget_transaction_count(),local/protectedalanları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ıp0döndürür, bakiyeyi bozmaz. Bu, kapsüllemenin asıl değeridir:balance'a yalnızca kuralları geçen değerler işlenebilir. PremiumAccountalt sınıfı:BankAccount'tan türer vesuper.new(...)ile üst sınıf constructor'ını çağırır.show_details()metoduaccount_typevetransaction_count(protected) alanlarına erişebilir; ancak üst sınıfınlocalbalancealanına erişemez.- Erişim hatası örneği: Koddaki yorum satırı
acc1.balance = 999999;etkin olsaydı,balancelocalolduğ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çalocalveyaprotectedyazmalısınız. localileprotectedarası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 staticbir sayaç kalıbı (next_account++) çok kullanışlıdır; sayaç dış müdahaleye kapalı kalır. localveprotectedmetodlara da uygulanabilir. Yalnızca sınıf içinden çağrılması gereken bir yardımcı fonksiyonu (local function void log_txn();) gizlemek, sınıfın dış arayüzünü (API) küçük ve net tutar.- Erişim belirleyici nesneye değil tipe bağlıdır:
BankAccountiçindeki bir metot, başka birBankAccountnesnesininlocal balancealanına erişebilir (other.balance). Bu,compare(other)gibi metodların yazılabilmesini sağlar.
Erişim Düzeyleri Özet Tablosu
| Belirleyici | Sınıfın kendi içinden | Alt sınıftan (extends) |
Sınıf dışından |
|---|---|---|---|
(yok) = public |
✅ | ✅ | ✅ |
protected |
✅ | ✅ | ❌ |
local |
✅ | ❌ | ❌ |
Benzetme: Bir bankayı düşünün. Public: lobide herkesin kullanabildiği ATM. Protected: çalışanlar ve şube yöneticilerinin (alt sınıflar) girebildiği arka ofis. Local: yalnızca kasa sorumlusunun açabildiği kasa. Kasaya para "ATM" üzerinden (
deposit/withdraw) girer; kimse kasaya elini uzatıp bakiyeyi doğrudan değiştiremez.
Doğrulamada Kapsülleme Nerede İşe Yarar?
- Transaction'ın hesaplanan alanları:
crc,paritygibi alanlarılocalyapıp yalnızcacalc_crc()ile güncelletmek, kullanıcının yanlışlıkla tutarsız CRC yazmasını engeller. - Driver'ın iç durumu: Sürülen işlem sayısı gibi sayaçlar
protectedtutulur; dışarı yalnızcaget_count()ile açılır. - Kütüphane (VIP) geliştirmek: Başkalarının kullanacağı bir doğrulama bileşeni yazıyorsanız, iç mekanizmayı gizlemek sonradan yapılan değişikliklerin kullanıcıları kırmamasını sağlar.
- UVM'de: UVM temel sınıflarında
m_ile başlayan üyelerin çoğulocal/protected'tır; bu yüzdenuvm_component'in iç alanlarına doğrudan erişemez,get_name(),get_parent()gibi getter'ları kullanırsınız.
Sık Yapılan Hatalar
- Her şeyi
localyapıp alt sınıfı kilitlemek: Türetilmiş sınıfın erişmesi gereken alanlarlocalise alt sınıf çalışmaz. Kalıtım planlıyorsanızprotectedseçin. - Getter'ın handle döndürmesi:
function Payload get_payload(); return payload;yazarsanız dışarıya iç nesnenin handle'ını verirsiniz; kapsülleme fiilen delinir. Gerekirse kopya döndürün. - Setter'da doğrulama yapmamak: Doğrulama içermeyen bir setter, alanı
publicyapmaktan farksızdır. Setter'ın var olma nedeni kural koymaktır. localüyeye alt sınıftan "süper" aracılığıyla erişmeyi denemek:super.balancede çalışmaz;localkesin olarak yalnızca tanımlandığı sınıfa aittir.
Kendinizi Deneyin
acc1.balance = 0;satırını yorumdan çıkarıp derleyin; hata mesajını not alın. Ardındanbalance'ıprotectedyapıp tekrar deneyin: sınıf dışından erişim hâlâ hata veriyor mu?PremiumAccountiçine, kredi limiti dahilinde eksiye düşmeye izin verenoverdraft_withdraw(real amount)metodu ekleyin.balancelocalolduğu için doğrudan yazamayacaksınız; çözümü üst sınıfaprotected function void adjust_balance(real delta);ekleyerek bulun.BankAccount'afunction bit same_owner(BankAccount other); return (owner == other.owner); endfunctionekleyin veother.account_no'ya erişmenin neden mümkün olduğunu açıklayın.next_accountsayacınılocal staticyerinepublic staticyapın ve dışarıdanBankAccount::next_account = 5;ile bozun. Kapsülleme olmadan ne tür hatalar doğabilir?
Hızlı Kontrol: Alt sınıfın erişebilmesini ama dış dünyanın erişememesini istediğiniz bir üye hangi belirleyiciyle tanımlanır?
protected. local alt sınıfa bile kapalıdır; public (varsayılan) ise herkese açıktır.