4 - Kapsülleme, Soyutlama ve Erişim Belirleyiciler
2025
Bu hafta, nesne tabanlı programlamanın temel prensiplerinden olan kapsülleme ve soyutlama kavramlarını inceleyeceğiz.
Ayrıca bu kavramlarla yakından ilişkili olan erişim belirleyicileri konusuna da değineceğiz.
Bu kavramlar, daha düzenli, güvenli ve sürdürülebilir yazılımlar geliştirmemize yardımcı olur.
Kapsülleme, veri ve bu veriyi işleyen metotları bir araya getirme ve bir birim (sınıf) içinde saklama prensibidir. Başka bir deyişle, sınıfımızın içindeki detayları dış dünyadan saklarız.
Bir kapsül gibi düşünün: İçinde değerli eşyalar var ve dış etkenlerden korunuyor. Sadece belirli yollarla (metotlarla) bu eşyalara erişebilir veya onları değiştirebilirsiniz.
Erişim belirleyiciler, sınıfımızın üyelerine (özellikler ve metotlar) nereden erişilebileceğini kontrol etmemizi sağlar. Temelde üç ana erişim belirleyici vardır:
Python’da diğer bazı dillerdeki gibi public
, protected
, private
anahtar kelimeleri yoktur. Ancak, isimlendirme kuralları ile bu kavramları simüle ederiz:
ortalama_not
, ogrenci_adi
, kaydet()
_
konulur. Örneğin: _sinif_listesi
, _hesapla()
__
konulur. Örneğin: __bakiye
, __parola_kriptolu
class Ogrenci:
def __init__(self, ad, soyad, okul_no):
self.ad = ad # public
self.soyad = soyad # public
self.okul_no = okul_no # public
def bilgileri_goster(self): # public metot
print(f"Ad: {self.ad}, Soyad: {self.soyad}, Okul No: {self.okul_no}")
ogrenci1 = Ogrenci("Ayşe", "Yılmaz", "12345")
print(ogrenci1.ad) # Public üyeye doğrudan erişim
ogrenci1.bilgileri_goster() # Public metoda erişim
class Ders:
def __init__(self, ders_adi, kredi):
self.ders_adi = ders_adi # public
self._kredi = kredi # protected
def ders_bilgisi_goster(self): # public metot
print(f"Ders Adı: {self.ders_adi}, Kredi: {self._kredi}")
class MuhendislikDersi(Ders): # Alt sınıf
def __init__(self, ders_adi, kredi, zorluk_seviyesi):
super().__init__(ders_adi, kredi)
self.zorluk_seviyesi = zorluk_seviyesi
def detayli_bilgi_goster(self):
print(f"Ders Adı: {self.ders_adi}, Kredi: {self._kredi}, Zorluk: {self.zorluk_seviyesi}") # Protected üyeye alt sınıftan erişim
ders1 = Ders("Matematik", 4)
print(ders1.ders_adi) # Public üyeye erişim
print(ders1._kredi) # Protected üyeye sınıf dışından erişim (UYARI: Python engellemez ama iyi bir pratik değil!)
muh_ders1 = MuhendislikDersi("Fizik", 3, "Orta")
muh_ders1.detayli_bilgi_goster() # Alt sınıftan protected üyeye erişim
class BankaHesabi:
def __init__(self, hesap_no, ad_soyad, bakiye):
self.hesap_no = hesap_no # public
self.ad_soyad = ad_soyad # public
self.__bakiye = bakiye # private
def para_yatir(self, miktar): # public metot
if miktar > 0:
self.__bakiye += miktar
print(f"{miktar} TL yatırıldı. Yeni bakiye: {self.__bakiye} TL")
else:
print("Geçersiz miktar.")
def para_cek(self, miktar): # public metot
if miktar > 0 and miktar <= self.__bakiye:
self.__bakiye -= miktar
print(f"{miktar} TL çekildi. Yeni bakiye: {self.__bakiye} TL")
else:
print("Geçersiz miktar veya yetersiz bakiye.")
def bakiye_goruntule(self): # public metot
print(f"Hesap Bakiyesi: {self.__bakiye} TL")
hesap1 = BankaHesabi("TR123", "Ahmet Demir", 1000)
hesap1.para_yatir(500)
hesap1.para_cek(200)
hesap1.bakiye_goruntule()
# print(hesap1.__bakiye) # Hata! Private üyeye sınıf dışından doğrudan erişim engellenir.
print(hesap1._BankaHesabi__bakiye) # İsim karmaşası (Name mangling) ile private üyeye dolaylı erişim (Genellikle kaçınılması gereken bir durum)
Özet: Python’da erişim belirleyiciler isimlendirme kuralları ile sağlanır. _
ve __
kullanarak verilerimizi kapsülleyebilir ve daha kontrollü erişim sağlayabiliriz.
Doğrudan özelliklere erişimi kısıtladığımızda (özellikle private veya protected yaptığımızda), verilere erişmek ve onları değiştirmek için kontrollü yollar oluşturmamız gerekir. İşte burada getter (erişici) ve setter (değiştirici) metotları devreye girer.
get_ozellik_adi()
veya sadece ozellik_adi()
şeklinde isimlendirilir.set_ozellik_adi(yeni_deger)
veya ozellik_adi = yeni_deger
(property ile) şeklinde isimlendirilir.class Sicaklik:
def __init__(self, celcius):
self.__celcius = celcius # private
def get_celcius(self): # Getter metodu
return self.__celcius
def set_celcius(self, celcius): # Setter metodu
if celcius < -273.15: # Mutlak sıfır kontrolü (veri doğrulama)
print("Geçersiz sıcaklık değeri! Mutlak sıfırın altında olamaz.")
else:
self.__celcius = celcius
def get_fahrenheit(self): # Getter metodu (dönüşümlü veri)
return (self.__celcius * 9/5) + 32
sicaklik1 = Sicaklik(25)
print(f"Celcius: {sicaklik1.get_celcius()}") # Getter ile okuma
print(f"Fahrenheit: {sicaklik1.get_fahrenheit()}") # Getter ile dönüştürülmüş veri okuma
sicaklik1.set_celcius(30) # Setter ile değiştirme
print(f"Yeni Celcius: {sicaklik1.get_celcius()}")
sicaklik1.set_celcius(-300) # Geçersiz değer kontrolü
print(f"Celcius (geçersiz denemeden sonra): {sicaklik1.get_celcius()}") # Değer değişmedi
@property
Dekoratörü ile Getter ve SetterPython’da getter ve setter metotlarını daha şık ve Pythonik bir şekilde tanımlamak için @property
dekoratörünü kullanabiliriz. Bu dekoratör, metotları özellik gibi kullanmamızı sağlar.
class SicaklikProperty:
def __init__(self, celcius):
self.__celcius = celcius
@property
def celcius(self): # Getter metodu (özellik gibi erişilir)
return self.__celcius
@celcius.setter
def celcius(self, celcius): # Setter metodu (özellik gibi atanır)
if celcius < -273.15:
print("Geçersiz sıcaklık değeri! Mutlak sıfırın altında olamaz.")
else:
self.__celcius = celcius
@property
def fahrenheit(self): # Sadece getter (salt okunur özellik)
return (self.__celcius * 9/5) + 32
sicaklik2 = SicaklikProperty(25)
print(f"Celcius: {sicaklik2.celcius}") # Özellik gibi erişim (getter)
print(f"Fahrenheit: {sicaklik2.fahrenheit}") # Salt okunur özellik erişimi
sicaklik2.celcius = 30 # Özellik gibi atama (setter)
print(f"Yeni Celcius: {sicaklik2.celcius}")
sicaklik2.celcius = -300 # Geçersiz değer kontrolü
print(f"Celcius (geçersiz denemeden sonra): {sicaklik2.celcius}")
# sicaklik2.fahrenheit = 100 # Hata! Salt okunur özellik, setter'ı yok.
Özet: Getter ve setter metotları (veya @property
dekoratörü) ile verilerimize kontrollü erişim sağlayarak kapsüllemeyi daha da güçlendirebiliriz.
Soyutlama, karmaşık sistemleri basitleştirme ve önemli detaylara odaklanma prensibidir. Yazılımda, soyutlama ile nesnelerin nasıl çalıştığıyla değil, ne yaptığıyla ilgileniriz. İç detayları saklar, sadece dış arayüzü gösteririz.
Bir araba kullanmak gibi düşünün: Arabayı nasıl çalıştırdığınızı (motorun iç mekanizması, yakıt sistemi vb.) bilmenize gerek yoktur. Sadece direksiyon, gaz, fren gibi arayüzleri kullanarak arabayı kullanabilirsiniz.
abc
Modülü (Abstract Base Classes)Python’da soyut sınıfları ve metotları tanımlamak için abc
(Abstract Base Classes) modülünü kullanırız.
abc.ABC
: Soyut sınıf oluşturmak için kullanılan temel sınıf. Sınıfımızı ABC
’den türeterek soyut sınıf yaparız.@abc.abstractmethod
: Metodu soyut yapmak için kullanılan dekoratör. Soyut metotların gövdesi olmaz (pass ifadesi kullanılır).class Dikdortgen(Sekil): # Dikdortgen somut sınıfı, Sekil'den türedi
def __init__(self, uzunluk, genislik):
self.uzunluk = uzunluk
self.genislik = genislik
def alan_hesapla(self): # Soyut metodu uygula (override)
return self.uzunluk * self.genislik
def cevre_hesapla(self): # Soyut metodu uygula (override)
return 2 * (self.uzunluk + self.genislik)
# sekil1 = Sekil() # Hata! Soyut sınıf örneklenemez.
dikdortgen1 = Dikdortgen(5, 10)
print(f"Dikdörtgen Alan: {dikdortgen1.alan_hesapla()}")
print(f"Dikdörtgen Çevre: {dikdortgen1.cevre_hesapla()}")
daire1 = Daire(3)
print(f"Daire Alan: {daire1.alan_hesapla()}")
print(f"Daire Çevre: {daire1.cevre_hesapla()}")
# class Ucgen(Sekil): # Ucgen sınıfı Sekil'den türedi ama soyut metotları uygulamadı
# pass # Hata! Soyut metotlar uygulanmalı.
Özet: Soyut sınıflar ve metotlar ile ortak arayüzler tanımlayabilir, kod tekrarını azaltabilir ve daha iyi bir sınıf hiyerarşisi oluşturabiliriz.
Banka hesap yönetim sistemi düşünelim. Hesap bilgilerini (bakiye, hesap numarası vb.) kapsülleyerek yetkisiz erişimi engelleriz. Hesap işlemleri (para yatırma, çekme) için ise soyut metotlar tanımlayarak farklı hesap türlerinin (vadeli, vadesiz vb.) aynı arayüzü kullanmasını sağlarız.
para_yatir()
ve para_cek()
gibi kontrollü metotlar ile bakiye güncellenebilir.Hesap
soyut sınıfı tanımlayarak, tüm hesap türlerinin (VadeliHesap
, VadesizHesap
) bakiye_goruntule()
, para_yatir()
, para_cek()
gibi temel işlemleri gerçekleştirmesini garanti ederiz.Hesap
soyut sınıfından türetip soyut metotları uygulamamız yeterli olur. Sistem genelinde tutarlılık sağlanır.Farklı medya oynatıcıları (video oynatıcı, müzik oynatıcı) düşünelim. Oynatma, durdurma, ses ayarı gibi ortak işlemleri soyutlayarak, farklı oynatıcı türlerinin aynı temel arayüzü kullanmasını sağlarız.
MedyaOynatici
soyut sınıfı tanımlayarak, tüm oynatıcı türlerinin (VideoOynatici
, MuzikOynatici
) oynat()
, durdur()
, ses_ayarla()
gibi temel işlemleri gerçekleştirmesini garanti ederiz.MedyaOynatici
arayüzü üzerinden işlemleri gerçekleştirebiliriz. Kod daha esnek ve modüler olur.Sonuç: Kapsülleme ve soyutlama, nesne tabanlı programlamanın temel taşlarıdır. Bu prensipleri doğru kullanarak, daha kaliteli, sürdürülebilir ve güvenilir yazılımlar geliştirebiliriz.