5 - Kompozisyon ve Polimorfizm
2026
Sınıflar arasında ilişki kurarken en sık yapılan hatalardan biri, her benzerliği kalıtım sanmaktır.
Oysa iki temel ilişki vardır:
Bu ayrımı doğru yapmak, daha düzenli ve daha anlaşılır kod yazmamızı sağlar.
Sınıflar arasında ilişki kurarken önce şu ayrımı yaparız:
Örnek:
Araba bir Tasit türüdür → is-aSiparis, SiparisKalemi nesnelerine sahiptir → has-aÖnce bu ayrımı doğru yapmak gerekir.
Bu dersin sonunda şunları ayırt edebiliyor olmanız beklenir:
is-a ve has-a ilişkileri arasındaki farkı,Kalıtım, bir sınıfın başka bir sınıftan özellik ve davranış devralmasıdır.
Burada temel düşünce şudur:
Alt sınıf, üst sınıfın bir türüdür.
Örnekler:
Araba bir Tasit türüdürKedi bir Hayvan türüdürBu nedenle kalıtımda sormamız gereken soru şudur:
“Bu sınıf gerçekten ötekinin bir türü mü?”
Kompozisyon, bir nesnenin görevini yerine getirmek için başka nesneleri yapısının parçası olarak kullanmasıdır.
Burada temel düşünce şudur:
Bir nesne, başka nesnelerden oluşabilir.
Örnek:
Siparis, birden fazla SiparisKalemi içerirKitap, birden fazla Bolum içerirBurada tür ilişkisi değil, parça-bütün ilişkisi vardır.
Bir ilişki kurarken şu iki soruyu sorun:
Pratik kural:
Sırf kod tekrarını azaltmak için hemen kalıtıma gitmeyin. Önce ilişkinin türünü düşünün.
Aşağıdaki ilişkiye bakalım:
Burada doğru model şudur:
Siparis, SiparisKalemi değildirSiparis, SiparisKalemi nesnelerine sahiptirYani burada uygun yaklaşım kompozisyondur.
class SiparisKalemi:
def __init__(self, urun_adi, birim_fiyat, adet):
self.urun_adi = urun_adi
self.birim_fiyat = birim_fiyat
self.adet = adet
def tutar(self):
return self.birim_fiyat * self.adet
class Siparis:
def __init__(self, siparis_no):
self.siparis_no = siparis_no
self.kalemler = []
def kalem_ekle(self, kalem):
self.kalemler.append(kalem)
def toplam_tutar(self):
return sum(kalem.tutar() for kalem in self.kalemler)siparis = Siparis("SP-1001")
siparis.kalem_ekle(SiparisKalemi("Klavye", 750, 1))
siparis.kalem_ekle(SiparisKalemi("Mouse", 350, 2))
print(siparis.toplam_tutar())
# Çıktı:
# 1450Burada:
Siparis, SiparisKalemi nesnelerine sahiptirSiparis içinde yapılırÇünkü sorumluluklar ayrılmıştır:
SiparisKalemiSiparisEğer tüm mantık tek sınıfta toplansaydı:
İkinci büyük kavramımız polimorfizmdir.
En sade haliyle:
Aynı metot çağrısı, farklı nesnelerde farklı sonuç üretebilir.
Burada önemli olan şudur:
Kısaca:
Aynı mesaj, farklı cevap.
Aşağıdaki yaklaşım ilk bakışta çalışır; ama büyümeye uygun değildir:
def odeme_al(tur, tutar):
if tur == "kart":
return f"{tutar} TL kredi kartı ile ödendi"
elif tur == "havale":
return f"{tutar} TL havale ile ödendi"
elif tur == "cuzdan":
return f"{tutar} TL dijital cüzdan ile ödendi"Sorunlar:
if/elif zinciri büyüyecek,class OdemeYontemi:
def ode(self, tutar):
raise NotImplementedError(
"Bu metot alt sınıfta tanımlanmalıdır"
)
class KrediKarti(OdemeYontemi):
def ode(self, tutar):
return f"{tutar} TL kredi kartı ile ödendi"
class Havale(OdemeYontemi):
def ode(self, tutar):
return f"{tutar} TL havale ile ödendi"
class DijitalCuzdan(OdemeYontemi):
def ode(self, tutar):
return f"{tutar} TL dijital cüzdan ile ödendi"Burada ortak beklenti açıktır:
Her ödeme yöntemi ode() metodunu sağlamalıdır.
Döngü şunu sormuyor:
KrediKarti mı?”Havale mi?”Döngü sadece şunu varsayıyor:
ode() metodunu çağırabilirim.
Polimorfizmin gücü buradadır:
Python’da bazen ortak davranış için ortak bir üst sınıf yazmak zorunda olmayız.
Bu yaklaşımın yaygın adı duck typing’dir.
Adı şu düşünceden gelir:
Eğer bir şey ördek gibi yürüyorsa ve ördek gibi vaklıyorsa, ona ördek gibi davranırım.
Buradaki temel fikir şudur:
Yani ortak üst sınıf olmasa da, gerekli metot varsa nesne kullanılabilir.
rapor_uret(PDFRapor())
rapor_uret(CSVRapor())
rapor_uret(HTMLRapor())
# Çıktı:
# PDF raporu oluşturuldu
# CSV raporu oluşturuldu
# HTML raporu oluşturulduBurada kalıtım yoktur; ama ortak davranış vardır:
olustur()
Duck typing, yalnızca aynı isimli metot olması demek değildir.
Şunlar da uyumlu olmalıdır:
Örneğin:
Eğer rapor_uret() fonksiyonu parametresiz olustur() bekliyorsa, burada sorun çıkar.
Yani yalnızca metot adı değil, kullanım biçimi de önemlidir.
Gerçek projelerde bu iki kavram çoğu zaman birlikte kullanılır.
Örnek fikir:
Sepet sınıfı bir ödeme yöntemi nesnesine sahiptir → kompozisyonode() çağrılır → polimorfizmclass Sepet:
def __init__(self, odeme_yontemi):
self.odeme_yontemi = odeme_yontemi
def satin_al(self, tutar):
print(self.odeme_yontemi.ode(tutar))
sepet1 = Sepet(KrediKarti())
sepet2 = Sepet(Havale())
sepet1.satin_al(500)
sepet2.satin_al(500)
# Çıktı:
# 500 TL kredi kartı ile ödendi
# 500 TL havale ile ödendiÇünkü Sepet şunları bilmek zorunda değildir:
Sepet için önemli olan tek şey şudur:
Elimde ode() metodu olan bir nesne var mı?
Bu yaklaşım sınıflar arasındaki bağımlılığı azaltır.
has-a ilişkisinde yanlışlıkla kalıtım kullanmakBu derste akılda tutulması gereken temel soru şudur:
Bu ilişki is-a mı, yoksa has-a mı?
İyi tasarımda amaç, sınıfları gereğinden fazla birbirine bağlamadan birlikte çalıştırabilmektir.
Bolum sınıfı ve bir Kitap sınıfı tasarlayın.Bolum sınıfında bölüm adı ve sayfa sayısı olsun.Kitap sınıfında kitabın adını, bölümlerini ve toplam sayfa sayısını gösteren bir metot yazın.Tasit adında bir temel sınıf oluşturun.Araba, Bisiklet ve Ucak sınıflarını yazın.hareket_et() metodu farklı çıktı üretsin.Bu alıştırmayı dersten sonra kısa pratik olarak çözün.
PDFRapor ve HTMLRapor adında iki sınıf oluşturun.olustur() adında bir metot olsun.olustur() metodunu çağırsın.Bu alıştırmayı dersten sonra kısa pratik olarak çözün.
Karakter adında bir temel sınıf oluşturun.Savasci, Buyucu ve Okcu sınıfları bu sınıftan türesin.saldir() metodu farklı bir çıktı versin.Oyun sınıfı, karakter nesnelerini bir listede tutsun.Oyun sınıfı, listedeki tüm karakterlerin saldırmasını sağlasın.Aşağıdaki yapıyı inceleyin:
UniversiteFakulteBolumOgretimElemaniOgrenciBu kavramlar arasındaki ilişkilerden hangileri:
daha yakın görünüyor?
Her kararınızı kısa bir gerekçe ile açıklayın.
İpucu
Sadece “birlikte bulunuyorlar mı?” diye düşünmeyin.
Şu iki soruyu ayrı ayrı sorun: