Nesne Tabanlı Programlama 2

4 - Kapsülleme, Soyutlama ve Erişim Belirleyiciler

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2026

Motivasyon: Neyi Çözmeye Çalışıyoruz?

Bu hafta aslında tek bir probleme odaklanıyoruz:

Bir nesnenin içindeki veriler nasıl “geçerli” kalır ve dışarıdan yanlış kullanımı nasıl azaltırız?

Örnek bir kural (invariant):

  • Bir banka hesabında bakiye negatif olmamalı.

Bu tür kurallar, nesneyi “doğru” durumda tutar.

Motivasyon: Kısa Bir Problem Örneği

Aşağıdaki tasarımda, dışarıdan doğrudan erişim yüzünden kural kolayca bozulur:

class BankaHesabi:
    def __init__(self, bakiye):
        self.bakiye = bakiye  # herkese açık

hesap = BankaHesabi(1000)
hesap.bakiye = -500  # kural bozuldu (bakiye negatif oldu)

Bu hafta, bu tür durumları tasarım düzeyinde nasıl önleyeceğimizi konuşacağız.

Bu Hafta Yol Haritası

Bu dersin sonunda şunları yapabiliyor olmanızı hedefliyoruz:

  • Bir sınıfta hangi nitelikler dışarıya açık olmalı, hangileri “iç detay” kalmalı karar vermek
  • @property ile kontrollü erişim tasarlamak (doğrulama / salt okunur nitelik)
  • Soyut sınıf (ABC) ile “bu sınıf ailesi şu metotları sağlamak zorunda” diye sözleşme koymak

Geçiş: Kapsülleme → Erişim Düzeyleri

Kapsülleme bir hedef: “Niteliklere rastgele dokunulmasın, kontrollü bir arayüz olsun.”

Bu hedefe yaklaşmak için iki araç kullanırız:

  • Erişim düzeyleri / konvansiyonlar: _ ve __ ile “iç detay” sinyali vermek (Python’da çoğu zaman kural değil, mesajdır.)
  • @property: nitelik gibi görünen bir erişimin arkasına kontrol koymak

Birazdan kapsüllemeyi anlattıktan sonra, bu iki aracı neden ve nasıl kullandığımız netleşecek.

Giriş

Bu hafta üç başlık birbirine bağlanarak ilerleyecek:

  • Kapsülleme: Nitelik + metodu sınıfta toplamak, dışarıya kontrollü bir arayüz sunmak
  • Erişim düzeyleri: “Bu nitelik/metoda kimler erişsin?” sorusunu yönetmek (Python’da çoğu zaman konvansiyon)
  • Soyutlama: “Ne yapıyor?” üzerinden sözleşme kurmak (ABC)

Bu kavramlar; kodun bakımını, genişletilmesini ve yanlış kullanıma karşı dayanıklılığını artırır.

Bu Hafta Ne Beklemeliyim?

Bu hafta şunu netleştireceğiz:

“Dışarıdan bir nesneye hangi yollarla dokunulmalı?”

Ders boyunca iki şeye odaklanacağız:

  1. Nitelikleri doğrudan değiştirmek yerine, kuralı koruyan metotlar / property tasarlamak
  2. “İç detay” ile “dış API” ayrımını yapmak: dışarıya açık kısım küçük ve anlaşılır olsun

Kapsülleme Nedir?

Kapsülleme; nitelikleri (durum/state) ile bu nitelikleri işleyen metotları (davranış/behavior) tek bir birimde (sınıfta) toplama yaklaşımıdır. Amaç, sınıfın dışından bakınca:

  • Nesnenin nasıl çalıştığı ayrıntılarını değil,
  • Nesneyle nasıl doğru etkileşeceğimizi göstermektir.

Bir başka ifadeyle, kapsülleme “gizlemek”ten çok kontrollü kullanım tasarlamaktır.

Neden Kapsülleme?

  • Tutarlılık (invariant) koruma: Nesne geçersiz bir duruma düşmesin diye kontrol noktaları oluşturur. (Invariant: nesne için her zaman doğru kalması gereken kural; örn. “bakiye negatif olamaz”.)
  • Bakım kolaylığı: İç tasarım değişse bile dışarıya sunduğunuz arayüz sabitse, dış kodlar daha az etkilenir.
  • Okunabilirlik: “Bu nitelik nerede değişiyor?” sorusu daha net cevaplanır.
  • Yanlış kullanımı azaltma: Niteliklere doğrudan yazmak yerine, kurallı bir yol sunarsınız.

Kısa örnek mantığı

“Yaş negatif olamaz” kuralı varsa, bunu her yerde değil tek yerde (setter / property) denetlemek isteriz.

Kapsüllemeyi Uygulamak İçin İki Pratik Araç

Kapsülleme fikrini Python’da genelde şu iki yolla somutlaştırırız:

  1. Erişim konvansiyonları (_ / __) İç detayları “dışarıdan kullanma” mesajı verir.

  2. @property Nitelik gibi görünen bir kullanımın arkasına kontrol koyar (doğrulama, salt okunur, vb.).

Şimdi önce erişim konvansiyonlarını netleştirelim.

Erişim Düzeyleri: Public, Protected, Private

Erişim Belirleyici Nedir?

Erişim belirleyiciler (access modifiers), bir sınıfın niteliklerine ve metotlarına nerelerden erişilebileceğini belirleyen kurallardır. Amaç, sınıfın “iç detaylarını” rastgele kullanımı azaltmak ve dışarıya kontrollü bir arayüz sunmaktır.

Kısaca: “Bu nitelik/metoda kimler erişebilir?” sorusunun cevabıdır.

Genel Fikir

C++/Java/C# gibi dillerde public/protected/private, dilin zorunlu kıldığı erişim kurallarıdır.

Python’da ise:

  • public/protected çoğunlukla konvansiyon (isimlendirme) düzeyindedir.
  • __ (çift alt çizgi) “tam gizlilik” sağlamaz; ad dönüştürme (name mangling) yapar.

Kritik nokta

Python’daki bu mekanizmalar güvenlik (security) hedefiyle tasarlanmamıştır. Amaç daha çok “yanlışlıkla kullanımın” azalması ve tasarımın daha okunaklı olmasıdır.

Python’da Pratik Karşılıklar

  • Public: Normal isimler

    Örn: ad, kaydet()

  • Protected (konvansiyon): Tek alt çizgi _

    Örn: _kredi, _hesapla() Python erişimi engellemez; “bu üye iç kullanım içindir” mesajı verir.

  • Private benzeri (name mangling): Çift alt çizgi __

    Örn: __bakiye Ad dönüştürme nedeniyle dışarıdan aynı adla erişim zorlaşır.

Public Üye Örneği

Aşağıdaki örnekte nitelikler ve metotlar doğrudan dışarıya açıktır.

class Ogrenci:
    def __init__(self, ad, soyad, okul_no):
        self.ad = ad
        self.soyad = soyad
        self.okul_no = okul_no

    def bilgileri_goster(self):
        print(f"Ad: {self.ad}, Soyad: {self.soyad}, Okul No: {self.okul_no}")

ogrenci1 = Ogrenci("Ayşe", "Yılmaz", "12345")
print(ogrenci1.ad)
ogrenci1.bilgileri_goster()

Ne gördük?

  • Public üyeler “her yerden erişilebilir”tir.
  • Küçük örneklerde pratik görünür; büyüdükçe kontrol ihtiyacı doğar.

Protected Üye Örneği

_kredi niteliği, dışarıdan erişilebilir; fakat “bu üye API garantisi değildir” mesajı taşır.

class Ders:
    def __init__(self, ders_adi, kredi):
        self.ders_adi = ders_adi   # public
        self._kredi = kredi        # protected (konvansiyon)

    def ders_bilgisi_goster(self):
        print(f"{self.ders_adi} ({self._kredi} kredi)")

class MuhendislikDersi(Ders):
    def __init__(self, ders_adi, kredi, zorluk):
        super().__init__(ders_adi, kredi)
        self.zorluk = zorluk

    def detayli_bilgi_goster(self):
        print(f"{self.ders_adi} ({self._kredi} kredi) - Zorluk: {self.zorluk}")

ders1 = Ders("Matematik", 4)
print(ders1._kredi)  # mümkün ama dış kod için kırılgan bir bağımlılık

muh_ders1 = MuhendislikDersi("Fizik", 3, "Orta")
muh_ders1.detayli_bilgi_goster()

Neden “dışarıdan kullanma” diyoruz?

Bugün _kredi var; yarın sınıf tasarımında alan adı değişebilir veya tamamen kaldırılabilir. Dış kod _krediye bağlandıysa, küçük bir iç değişiklik dışarıyı da kırar.

Ne gördük?

  • _ erişimi engellemez; “iç detay” sinyalidir.
  • Alt sınıflar bu tür üyeleri kullanabilir; yine de tasarım sorumluluğu sınıf tasarımcısındadır.

Private Benzeri: Name Mangling

Çift alt çizgi ile başlayan bazı isimler ad dönüştürmeye uğrar.

Örnek: BankaHesabi içinde __bakiye niteliği tanımlanırsa, Python bunu şu forma dönüştürür:

  • __bakiye_BankaHesabi__bakiye

Bu yüzden hesap.__bakiye yazdığınızda genellikle AttributeError görürsünüz: Nitelik aslında vardır, ama adı dönüştürülmüştür.

Not: Name mangling hangi isimlerde olur?

Genel kural: Başında en az iki alt çizgi olup sonunda __ ile bitmeyen isimlerde (örn. __bakiye) devreye girer. __init__, __str__ gibi özel metot adları bu amaçla kullanılmaz.

Private Üye Örneği

class BankaHesabi:
    def __init__(self, hesap_no, ad_soyad, bakiye):
        self.hesap_no = hesap_no
        self.ad_soyad = ad_soyad
        self.__bakiye = bakiye  # name mangling

    def para_yatir(self, miktar):
        if miktar <= 0:
            raise ValueError("Miktar pozitif olmalı.")
        self.__bakiye += miktar

    def para_cek(self, miktar):
        if miktar <= 0:
            raise ValueError("Miktar pozitif olmalı.")
        if miktar > self.__bakiye:
            raise ValueError("Yetersiz bakiye.")
        self.__bakiye -= miktar

    def bakiye_goruntule(self):
        return self.__bakiye

hesap1 = BankaHesabi("TR123", "Ahmet Demir", 1000)
hesap1.para_yatir(500)
hesap1.para_cek(200)
print(hesap1.bakiye_goruntule())

# Mekanizmayı göstermek içindir; normal tasarımda kullanılmaz:
# print(hesap1._BankaHesabi__bakiye)

Ne zaman __ kullanılır?

  • Alt sınıflarda isim çakışması riskini azaltmak istediğinizde
  • “Yanlışlıkla erişim” olasılığını düşürmek istediğinizde
  • Tam gizlilik hedefiyle kullanılmaz.

Getter ve Setter Mantığı

Doğrudan bir niteliğin değerine erişim kolaydır; fakat şu ihtiyaçlar doğar:

  • Değer geçerli mi? (doğrulama)
  • Değer değişince başka bir işlem yapılacak mı? (log, yeniden hesaplama)
  • Sadece okunabilir bir nitelik mi istiyoruz? (read-only)

Bu ihtiyaçlar, getter/setter veya @property ile temiz biçimde çözülebilir.

Manuel Getter/Setter Örneği

class Sicaklik:
    def __init__(self, celsius):
        self._celsius = None
        self.set_celsius(celsius)

    def get_celsius(self):
        return self._celsius

    def set_celsius(self, celsius):
        if celsius < -273.15:
            raise ValueError("Mutlak sıfırın altında sıcaklık olamaz.")
        self._celsius = celsius

    def get_fahrenheit(self):
        return (self._celsius * 9/5) + 32

s1 = Sicaklik(25)
print(s1.get_celsius())
print(s1.get_fahrenheit())

s1.set_celsius(30)
print(s1.get_celsius())

Ne gördük?

  • Değer yazma/okuma “kontrollü noktalar” üzerinden gidiyor.
  • Ancak kullanım tarafı daha uzuyor: get_..., set_...

@property ile Pythonik Yaklaşım

@property ile bir metodu nitelik gibi kullanırsınız.

Yani dışarıdan obj.celsius diye okunur / obj.celsius = 30 diye atanır; ama okuma/atama sırasında sizin yazdığınız getter/setter kodu çalışır (doğrulama, dönüşüm, log gibi).

Note

Setter yazmazsanız property salt okunur olur (dışarıdan değiştirilemez).

Sık Yapılan Hata: __init__ İçinde Doğrulamayı Atlama

Bazı öğrenciler __init__ içinde doğrudan iç niteliğe yazar:

  • self._celsius = celsius

Bu durumda setter’daki doğrulama devreye girmez.

Doğru yaklaşım

__init__ içinde de setter’dan geçmesini istiyorsanız: self.celsius = celsius yazmalısınız.

@property Örneği

class SicaklikProperty:
    def __init__(self, celsius):
        self._celsius = None
        self.celsius = celsius  # setter çalışsın

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Mutlak sıfırın altında sıcaklık olamaz.")
        self._celsius = value

    @property
    def fahrenheit(self):
        return (self._celsius * 9/5) + 32

s2 = SicaklikProperty(25)
print(s2.celsius)
print(s2.fahrenheit)

s2.celsius = 30
print(s2.celsius)

# SicaklikProperty(-500)  # ValueError

Ne gördük?

  • Dışarıdan obj.celsius = ... nitelik ataması gibi görünüyor.
  • İçeride doğrulama var; geçersiz durumda nesne daha baştan oluşmuyor.

Soyutlama Nedir?

Soyutlama; karmaşık ayrıntıları geri plana itip, “nesne ne yapar?” sorusuna odaklanır.

Örnek: Bir aracı kullanmak için motor ayrıntılarını bilmeniz gerekmez; sizin için önemli olan “ilerle/dur/sağa dön” gibi işlevlerdir.

Yazılımda da benzer biçimde; sınıfların iç detaylarını geri plana itip “sözleşme” tanımlamak isteriz.

Soyut Sınıflar ve Soyut Metotlar

  • Soyut sınıf (Abstract Class): Doğrudan örneklenmez; alt sınıflar için ortak bir “sözleşme” sağlar.
  • Soyut metot (Abstract Method): Alt sınıfların sağlaması beklenen metot.

Pratik hedef

Bir soyut sınıf, “alt sınıflar şu metotları mutlaka sağlayacak” garantisini vermek için kullanılır.

abc Modülü ile Abstract Base Class

from abc import ABC, abstractmethod

class Sekil(ABC):
    @abstractmethod
    def alan_hesapla(self):
        pass

    @abstractmethod
    def cevre_hesapla(self):
        pass

Ne gördük?

  • Sekil, “şekil gibi davranan” tüm sınıflar için bir sözleşme oldu.
  • Alt sınıflar bu metotları yazmazsa örneklenemez.

Somut Sınıflar: Dikdörtgen ve Daire

import math

class Dikdortgen(Sekil):
    def __init__(self, uzunluk, genislik):
        self.uzunluk = uzunluk
        self.genislik = genislik

    def alan_hesapla(self):
        return self.uzunluk * self.genislik

    def cevre_hesapla(self):
        return 2 * (self.uzunluk + self.genislik)

class Daire(Sekil):
    def __init__(self, yaricap):
        self.yaricap = yaricap

    def alan_hesapla(self):
        return math.pi * (self.yaricap ** 2)

    def cevre_hesapla(self):
        return 2 * math.pi * self.yaricap

Ne gördük?

  • math.pi kullanımı “sabitleri elle yazma” alışkanlığını azaltır.
  • Her iki sınıf da Sekil sözleşmesini sağlıyor.

Polimorfik Kullanım (Kısa Örnek)

Aynı sözleşmeyi sağlayan farklı nesneler, tek bir listede ortak biçimde kullanılabilir.

sekiller = [Dikdortgen(5, 10), Daire(3), Dikdortgen(2, 7)]

for s in sekiller:
    print(type(s).__name__, "Alan:", s.alan_hesapla(), "Çevre:", s.cevre_hesapla())

Bu fikri haftaya Çok Biçimlilik (Polimorfizm) başlığında daha sistematik işleyeceğiz.

Kapsülleme – Soyutlama

Bu iki kavram birbiriyle ilişkilidir ama aynı şey değildir:

  • Kapsülleme: Nitelik + metodu aynı sınıfta toplar; dışarıya kontrollü API sunar.
  • Soyutlama: Ayrıntıları geri plana iter; gerekli davranışları “sözleşme” gibi sunar.

Pratik ayrım: Kapsülleme nasıl sakladığınızdır, soyutlama neyi gösterdiğinizdir.

Alıştırmalar

Alıştırma 1: Banka Hesap Yönetimi (Property ile)

Bu alıştırmada iki hedef var:

  1. Kapsülleme: Hesabın kritik verileri (özellikle bakiye) dışarıdan rastgele değiştirilemesin.
  2. Soyutlama: Tüm hesap türleri aynı “temel sözleşmeyi” sağlasın (para_yatir / para_cek).

Bu alıştırmada get_... / set_... yerine, Pythonik biçimde @property kullanacağız.

Alıştırma 1: Mesaj Standartları

Bu alıştırmada değerlendirme kolaylığı için mesajları aşağıdaki standartta üretin:

  • miktar <= 0 ise: "Geçersiz miktar"
  • Çekimde yetersiz bakiye ise: "Yetersiz bakiye"
  • Vadeli hesapta ceza dahil yetersiz bakiye ise: "Yetersiz bakiye (ceza dahil)"

Format notu

Ceza/ faiz gibi ondalıklı değerleri iki basamak ile yazdırın: :.2f Örn: 10.00 TL, 123.45 TL

Alıştırma 1: İstenen Sınıflar ve Kurallar

1) Soyut Sınıf (Sözleşme)

BankaHesabi(ABC) sınıfını tanımlayın:

  • Private nitelikler:

    • __hesap_no, __isim, __bakiye
  • Salt okunur property’ler:

    • hesap_no, isim, bakiye
  • Korumalı (iç kullanım) güncelleme metotları:

    • _bakiye_ekle(miktar)
    • _bakiye_cikar(miktar)
  • Soyut metotlar:

    • para_yatir(miktar) → işlem sonucu bilgisini string olarak döndürsün
    • para_cek(miktar) → işlem sonucu bilgisini string olarak döndürsün
  • Ortak metot:

    • hesap_bilgisi_goster() → hesap no, isim, bakiye bilgisi döndürsün

Neden bakiye salt okunur?

Dışarıdan hesap.bakiye = ... gibi bir atama yapılmasın istiyoruz. Bakiye güncellemesi sadece para_yatir/para_cek gibi kurallı metotlarla olmalı.

Alıştırma 1: Alt Sınıflar ve Davranış

2) VadesizHesap(BankaHesabi)

  • para_yatir(miktar):

    • miktar > 0 ise bakiyeye ekle; "500 TL yatırıldı. Güncel bakiye: 1500 TL" gibi bir mesaj döndür.
    • Aksi halde "Geçersiz miktar"
  • para_cek(miktar):

    • miktar > 0 ve miktar <= bakiye ise bakiyeden düş; "200 TL çekildi. Güncel bakiye: 1300 TL" gibi bir mesaj döndür.
    • Bakiye yetmiyorsa "Yetersiz bakiye"
    • Aksi halde "Geçersiz miktar"

Alıştırma 1: Alt Sınıflar ve Davranış

3) VadeliHesap(BankaHesabi)

Ek private nitelikler: * __vade_suresi, __faiz_orani

Ek salt okunur property’ler: * vade_suresi, faiz_orani

Ek metot: * faiz_hesapla() → yıllık faiz tutarını hesaplayıp mesaj döndürsün (iki basamak)

Para çekme kuralı (erken çekim cezası):

  • Çekilecek miktara %2 ceza eklenir.
  • Toplam düşülecek miktar: miktar + ceza
  • Mesajda ceza miktarı da gösterilsin: "500 TL çekildi (10.00 TL ceza uygulandı). Güncel bakiye: 4490 TL"

Yetersizlik durumları:

  • miktar <= 0 ise "Geçersiz miktar"
  • miktar + ceza bakiyeyi aşıyorsa "Yetersiz bakiye (ceza dahil)"

Alıştırma 1: İskelet Kod (Soyut Sınıf)

from abc import ABC, abstractmethod

class BankaHesabi(ABC):
    def __init__(self, hesap_no, isim, bakiye=0):
        self.__hesap_no = hesap_no
        self.__isim = isim
        self.__bakiye = bakiye
    
    @property
    def hesap_no(self):
        return self.__hesap_no

    @property
    def isim(self):
        return self.__isim

    @property
    def bakiye(self):
        return self.__bakiye  # salt okunur

Devam

    # Alt sınıflar için kontrollü güncelleme
    def _bakiye_ekle(self, miktar):
        self.__bakiye += miktar

    def _bakiye_cikar(self, miktar):
        self.__bakiye -= miktar

    @abstractmethod
    def para_yatir(self, miktar):
        pass  # str döndürmeli

    @abstractmethod
    def para_cek(self, miktar):
        pass  # str döndürmeli

    def hesap_bilgisi_goster(self):
        return f"Hesap No: {self.hesap_no}, İsim: {self.isim}, Bakiye: {self.bakiye} TL"

Alıştırma 1: İskelet Kod (Alt Sınıflar)

class VadesizHesap(BankaHesabi):
    def para_yatir(self, miktar):
        # TODO: miktar kontrolü + _bakiye_ekle + mesaj döndür
        pass
    def para_cek(self, miktar):
        # TODO: miktar kontrolü + yeterlilik kontrolü + _bakiye_cikar + mesaj döndür
        pass

Devam

class VadeliHesap(BankaHesabi):
    def __init__(self, hesap_no, isim, bakiye=0, vade_suresi=12, faiz_orani=0.05):
        super().__init__(hesap_no, isim, bakiye)
        self.__vade_suresi = vade_suresi
        self.__faiz_orani = faiz_orani

    @property
    def vade_suresi(self):
        return self.__vade_suresi

    @property
    def faiz_orani(self):
        return self.__faiz_orani

    def para_yatir(self, miktar):
        # TODO: miktar kontrolü + _bakiye_ekle + mesaj döndür
        pass

    def para_cek(self, miktar):
        # TODO: miktar kontrolü + %2 ceza + ceza dahil yeterlilik kontrolü + _bakiye_cikar + mesaj döndür
        pass

    def faiz_hesapla(self):
        # TODO: yıllık faiz tutarı hesapla, :.2f formatıyla mesaj döndür
        pass

Alıştırma 1: Basit Test Senaryosu

Aşağıdaki kod çalıştığında, metotların doğru davrandığını görebilmelisiniz.

if __name__ == "__main__":
    vadesiz = VadesizHesap("123456", "Ahmet Yılmaz", 1000)
    vadeli = VadeliHesap("789012", "Mehmet Kaya", 5000, 24, 0.08)

    print(vadesiz.hesap_bilgisi_goster())
    print(vadesiz.para_yatir(500))   # "500 TL yatırıldı. Güncel bakiye: 1500 TL"
    print(vadesiz.para_cek(200))     # "200 TL çekildi. Güncel bakiye: 1300 TL"
    print(vadesiz.para_cek(50000))   # "Yetersiz bakiye"
    print(vadesiz.para_yatir(-5))    # "Geçersiz miktar"

    print("\n" + vadeli.hesap_bilgisi_goster())
    print(vadeli.para_yatir(1000))   # "1000 TL yatırıldı. Güncel bakiye: 6000 TL"
    print(vadeli.para_cek(500))      # "500 TL çekildi (10.00 TL ceza uygulandı). Güncel bakiye: 5490 TL"
    print(vadeli.para_cek(999999))   # "Yetersiz bakiye (ceza dahil)"
    print(vadeli.faiz_hesapla())

Ne ölçüyoruz?

  • bakiye dışarıdan set edilebiliyor mu? (set edilememeli)
  • Bakiye güncellemesi sadece kurallı metotlardan mı geçiyor? (kapsülleme)
  • Her hesap türü aynı metotları sağlıyor mu? (soyutlama/sözleşme)
  • Vadeli hesapta ceza/faiz kuralları doğru mu?

Alıştırma 2: Medya Oynatıcıları

Bu örnekte amaç, farklı oynatıcıların aynı sözleşmeyi sağlayabilmesidir.

Senaryo: Uygulama bir “oynatıcı” ile çalışıyor. Oynatıcı ister video ister ses oynatsın, uygulama şu iki işi aynı şekilde çağırabilmeli:

  • play()
  • pause()

Buradaki fikir: uygulama “hangi tür oynatıcı” olduğunu bilmeden, sözleşmeye güvenerek çalışır.

Alıştırma 2: İstenen Sınıflar ve Kurallar

  • Player(ABC) soyut sınıfı:

    • play() ve pause() metotları soyut olsun.
  • VideoPlayer(Player):

    • play()"Video oynatılıyor."
    • pause()"Video duraklatıldı."
  • AudioPlayer(Player):

    • play()"Ses oynatılıyor."
    • pause()"Ses duraklatıldı."

Alıştırma 2: İskelet Kod

from abc import ABC, abstractmethod

class Player(ABC):
    @abstractmethod
    def play(self):
        pass
    @abstractmethod
    def pause(self):
        pass

class VideoPlayer(Player):
    def play(self):
        # TODO
        pass
    def pause(self):
        # TODO
        pass

class AudioPlayer(Player):
    def play(self):
        # TODO
        pass
    def pause(self):
        # TODO
        pass

Alıştırma 2: Test Senaryosu

music_player = AudioPlayer()
video_player = VideoPlayer()

music_player.play()
music_player.pause()

video_player.play()
video_player.pause()

Neyi gösteriyor?

Aynı metot adlarıyla farklı nesnelerin çalışması, haftaya işleyeceğimiz Çok Biçimlilik (Polimorfizm) fikrine doğrudan zemin hazırlar.

Özet

  • Kapsülleme: Nitelik + metodu bir sınıfta toplamak; kontrollü API sunmak.

  • Python’da erişim:

    • Public → normal isim
    • Protected → _ (konvansiyon, engellemez)
    • Private benzeri → __ (name mangling, tam gizlilik değil)
  • @property: Nitelik gibi kullanım + içeride kontrol (doğrulama/dönüşüm/log)

  • Sık hata: __init__ içinde doğrudan iç niteliğe atayıp setter doğrulamasını atlamak

  • Soyut sınıf (ABC): Alt sınıflar için sözleşme; metotlar sağlanmadan örneklenemez

  • Polimorfizm köprüsü: Aynı sözleşmeye sahip farklı nesneler ortak biçimde kullanılabilir

Mini kontrol soruları

  1. __bakiye niteliğine obj.__bakiye ile neden erişemezsiniz? (Name mangling)
  2. _kredi dışarıdan okunabiliyorsa neden “dışarıdan kullanma” denir? (API garantisi değil)
  3. @property ile manuel getter/setter farkı nedir? (kullanım biçimi + kontrolün saklanması)

Kapanış

Bu hafta, “sınıfın içi” ile “sınıfın dışarıya sunduğu arayüz” ayrımını netleştirdik:

  • Kapsülleme → doğru kullanım için kontrol noktaları
  • Python’da erişim düzeyleri → çoğunlukla konvansiyon + name mangling
  • Soyutlama → sözleşme (ABC) ile ortak davranış garantisi

Haftaya

Çok Biçimlilik (Polimorfizm) ile bu sözleşme fikrini daha sistematik biçimde kullanacağız.

Haftaya Görüşmek Üzere!