Soyut Sınıflar (ABCs) vs. Arabirimler (Interfaces): Bir Karşılaştırma
Nesne Yönelimli Programlama (OOP) dünyasında, kodun modülerliğini, esnekliğini ve sürdürülebilirliğini artırmak için çeşitli tasarım prensipleri ve mekanizmalar kullanılır. Bu mekanizmalardan ikisi, genellikle benzer amaçlar için kullanılsa da aralarında önemli kavramsal ve pratik farklar bulunan Soyut Temel Sınıflar (Abstract Base Classes — ABCs) ve Arabirimler (Interfaces)’dir. Her iki yapı da temel olarak bir kontrat tanımlama görevini üstlenir. Yani, belirli bir işlevselliği sağlaması gereken sınıfların hangi metotlara sahip olması gerektiğini belirtirler. Bu sayede, farklı sınıfların ortak bir arayüze uymasını sağlayarak polimorfizmi destekler ve kodun farklı bileşenleri arasındaki bağımlılıkları (coupling) azaltırlar. Ancak, bu iki kavramın nasıl uygulandığı, hangi yeteneklere sahip olduğu ve hangi durumlarda tercih edildiği programlama dilleri arasında farklılık gösterir. Özellikle Python gibi dinamik tipli ve doğrudan interface anahtar kelimesine sahip olmayan bir dilde, bu kavramların nasıl ele alındığını anlamak önemlidir. Python, arabirim benzeri kontratları uygulamak için öncelikle Soyut Temel Sınıfları (abc modülü aracılığıyla) ve daha modern yaklaşımlarda Protokolleri (typing.Protocol) kullanır. Bu rehberde, Soyut Sınıflar ve Arabirimler kavramlarını genel OOP perspektifinden ve özellikle Python’daki uygulamaları üzerinden karşılaştıracağız. Temel tanımlarını, amaçlarını, benzerliklerini, en önemlisi de aralarındaki temel farkları (implementasyon içerme, kalıtım modeli, durum saklama vb.) detaylıca ele alacak, Python’daki ilgili mekanizmaları (ABCs, Protokoller, Duck Typing) konumlandıracak ve hangi durumda hangi yaklaşımın daha uygun olabileceğine dair bir çerçeve sunacağız. Bölüm 1: Kavramsal Tanımlar Karşılaştırmaya başlamadan önce her iki kavramı da net bir şekilde tanımlayalım: 1.1. Arabirim (Interface) — Genel OOP Kavramı Geleneksel olarak (özellikle Java, C# gibi dillerde), bir Arabirim (Interface): Sadece soyut metotların imzalarını (isim, parametreler, bazen dönüş tipi) ve (genellikle) sabit (constant) değerleri içeren bir yapıdır. Metotların implementasyonunu (gövdesini) içermez. Doğrudan örneklenemez (instantiate edilemez). Bir sınıfın sahip olması gereken yetenekleri (capabilities) veya uyması gereken bir kontratı tanımlar. Bir sınıf, bir veya (çoğu dilde) birden fazla arabirimi “implemente edebilir” (uygulayabilir). Implemente eden sınıf, arabirimdeki tüm metotları sağlamakla yükümlüdür. Genellikle “Can-Do” (yapabilir) ilişkisini modeller: “Bu sınıf X yeteneğine sahiptir.” Amaç: Farklı sınıfların ortak bir davranışı (metot setini) garanti etmesini sağlamak, sınıflar arası kontrat oluşturmak, polimorfizmi sağlamak ve gevşek bağlantıyı (loose coupling) teşvik etmek. Örnek: Bir Uçabilir arabirimi, uc() metodunu tanımlayabilir. Hem Kuş sınıfı hem de Uçak sınıfı bu arabirimi implemente ederek kendi uc() metotlarını sağlamak zorunda kalır. 1.2. Soyut Temel Sınıf (Abstract Base Class — ABC) — Python Kavramı Python’da abc modülü ile tanımlanan bir Soyut Temel Sınıf (ABC): Doğrudan örneklenemeyen bir sınıftır. Hem soyut metotlar (@abstractmethod ile işaretlenmiş, implementasyonu olmayan veya alt sınıflarca override edilmesi zorunlu olan) hem de somut metotlar (tam implementasyona sahip) içerebilir. Nitelikler (instance/class variables) ve bir init metodu içerebilir. Alt sınıfların belirli metotları implemente etmesini zorunlu kılar (eğer alt sınıf da soyut kalmak istemiyorsa). Kalıtım (Inheritance) mekanizmasına dayanır. Alt sınıflar ABC’den türemelidir. Genellikle “Is-A” (bir türüdür) ilişkisini modellemekle birlikte, bir arayüz/kontrat tanımlama amacı da güder. Amaç: Alt sınıflar için ortak bir temel yapı ve/veya implementasyon sağlamak, belirli bir arayüze uyulmasını zorunlu kılmak, kod tekrarını azaltmak ve polimorfizmi desteklemek. Örnek: Bir Sekil ABC'si, tüm şekillerin sahip olması gereken soyut alan() ve cevre() metotlarını tanımlayabilirken, tüm şekiller için ortak olan somut bir renk_getir() metodu da içerebilir. Daire ve Kare sınıfları bu ABC'den türeyip soyut metotları implemente etmek zorundadır. Bölüm 2: Temel Farklılıklar: Soyut Sınıflar vs. Arabirimler Bu iki kavram arasındaki temel farklar şunlardır: ÖzellikArabirim (Interface — Geleneksel OOP)Soyut Temel Sınıf (ABC — Python)Metot ImplementasyonuHayır (Geleneksel olarak sadece imzalar). Modern versiyonlarda ‘default methods’ olabilir.Evet (Hem soyut hem de somut metotlar içerebilir).Nitelikler (Instance Variables)Hayır (Genellikle sadece sabitlere izin verilir).Evet (Hem sınıf hem de örnek nitelikleri tanımlanabilir).Constructor (__init__)Hayır.Evet (Kendi __init__ metoduna sahip olabilir ve alt sınıflar super().__init__ ile çağırabilir).Kalıtım ModeliÇoklu Implementasyon: Bir sınıf birden fazla arabirimi implemente edebilir (“Can-Do” yetenekler).Genellikle Tekli Kalıtım Odaklı: Python çoklu kalıtımı desteklese de, ABC’ler genellikle birincil “Is-A” hiyerarşisi için kullanılır. Çoklu ABC’den kalıtım mümkündür a

Nesne Yönelimli Programlama (OOP) dünyasında, kodun modülerliğini, esnekliğini ve sürdürülebilirliğini artırmak için çeşitli tasarım prensipleri ve mekanizmalar kullanılır. Bu mekanizmalardan ikisi, genellikle benzer amaçlar için kullanılsa da aralarında önemli kavramsal ve pratik farklar bulunan Soyut Temel Sınıflar (Abstract Base Classes — ABCs) ve Arabirimler (Interfaces)’dir.
Her iki yapı da temel olarak bir kontrat tanımlama görevini üstlenir. Yani, belirli bir işlevselliği sağlaması gereken sınıfların hangi metotlara sahip olması gerektiğini belirtirler. Bu sayede, farklı sınıfların ortak bir arayüze uymasını sağlayarak polimorfizmi destekler ve kodun farklı bileşenleri arasındaki bağımlılıkları (coupling) azaltırlar.
Ancak, bu iki kavramın nasıl uygulandığı, hangi yeteneklere sahip olduğu ve hangi durumlarda tercih edildiği programlama dilleri arasında farklılık gösterir. Özellikle Python gibi dinamik tipli ve doğrudan interface anahtar kelimesine sahip olmayan bir dilde, bu kavramların nasıl ele alındığını anlamak önemlidir. Python, arabirim benzeri kontratları uygulamak için öncelikle Soyut Temel Sınıfları (abc modülü aracılığıyla) ve daha modern yaklaşımlarda Protokolleri (typing.Protocol) kullanır.
Bu rehberde, Soyut Sınıflar ve Arabirimler kavramlarını genel OOP perspektifinden ve özellikle Python’daki uygulamaları üzerinden karşılaştıracağız. Temel tanımlarını, amaçlarını, benzerliklerini, en önemlisi de aralarındaki temel farkları (implementasyon içerme, kalıtım modeli, durum saklama vb.) detaylıca ele alacak, Python’daki ilgili mekanizmaları (ABCs, Protokoller, Duck Typing) konumlandıracak ve hangi durumda hangi yaklaşımın daha uygun olabileceğine dair bir çerçeve sunacağız.
Bölüm 1: Kavramsal Tanımlar
Karşılaştırmaya başlamadan önce her iki kavramı da net bir şekilde tanımlayalım:
1.1. Arabirim (Interface) — Genel OOP Kavramı
Geleneksel olarak (özellikle Java, C# gibi dillerde), bir Arabirim (Interface):
Sadece soyut metotların imzalarını (isim, parametreler, bazen dönüş tipi) ve (genellikle) sabit (constant) değerleri içeren bir yapıdır.
Metotların implementasyonunu (gövdesini) içermez.
Doğrudan örneklenemez (instantiate edilemez).
Bir sınıfın sahip olması gereken yetenekleri (capabilities) veya uyması gereken bir kontratı tanımlar.
Bir sınıf, bir veya (çoğu dilde) birden fazla arabirimi “implemente edebilir” (uygulayabilir). Implemente eden sınıf, arabirimdeki tüm metotları sağlamakla yükümlüdür.
Genellikle “Can-Do” (yapabilir) ilişkisini modeller: “Bu sınıf X yeteneğine sahiptir.”
Amaç: Farklı sınıfların ortak bir davranışı (metot setini) garanti etmesini sağlamak, sınıflar arası kontrat oluşturmak, polimorfizmi sağlamak ve gevşek bağlantıyı (loose coupling) teşvik etmek.
Örnek: Bir Uçabilir arabirimi, uc() metodunu tanımlayabilir. Hem Kuş sınıfı hem de Uçak sınıfı bu arabirimi implemente ederek kendi uc() metotlarını sağlamak zorunda kalır.
1.2. Soyut Temel Sınıf (Abstract Base Class — ABC) — Python Kavramı
Python’da abc modülü ile tanımlanan bir Soyut Temel Sınıf (ABC):
Doğrudan örneklenemeyen bir sınıftır.
Hem soyut metotlar (@abstractmethod ile işaretlenmiş, implementasyonu olmayan veya alt sınıflarca override edilmesi zorunlu olan) hem de somut metotlar (tam implementasyona sahip) içerebilir.
Nitelikler (instance/class variables) ve bir init metodu içerebilir.
Alt sınıfların belirli metotları implemente etmesini zorunlu kılar (eğer alt sınıf da soyut kalmak istemiyorsa).
Kalıtım (Inheritance) mekanizmasına dayanır. Alt sınıflar ABC’den türemelidir.
Genellikle “Is-A” (bir türüdür) ilişkisini modellemekle birlikte, bir arayüz/kontrat tanımlama amacı da güder.
Amaç: Alt sınıflar için ortak bir temel yapı ve/veya implementasyon sağlamak, belirli bir arayüze uyulmasını zorunlu kılmak, kod tekrarını azaltmak ve polimorfizmi desteklemek.
Örnek: Bir Sekil ABC'si, tüm şekillerin sahip olması gereken soyut alan() ve cevre() metotlarını tanımlayabilirken, tüm şekiller için ortak olan somut bir renk_getir() metodu da içerebilir. Daire ve Kare sınıfları bu ABC'den türeyip soyut metotları implemente etmek zorundadır.
Bölüm 2: Temel Farklılıklar: Soyut Sınıflar vs. Arabirimler
Bu iki kavram arasındaki temel farklar şunlardır:
ÖzellikArabirim (Interface — Geleneksel OOP)Soyut Temel Sınıf (ABC — Python)Metot ImplementasyonuHayır (Geleneksel olarak sadece imzalar). Modern versiyonlarda ‘default methods’ olabilir.Evet (Hem soyut hem de somut metotlar içerebilir).Nitelikler (Instance Variables)Hayır (Genellikle sadece sabitlere izin verilir).Evet (Hem sınıf hem de örnek nitelikleri tanımlanabilir).Constructor (__init__
)Hayır.Evet (Kendi __init__
metoduna sahip olabilir ve alt sınıflar super().__init__
ile çağırabilir).Kalıtım ModeliÇoklu Implementasyon: Bir sınıf birden fazla arabirimi implemente edebilir (“Can-Do” yetenekler).Genellikle Tekli Kalıtım Odaklı: Python çoklu kalıtımı desteklese de, ABC’ler genellikle birincil “Is-A” hiyerarşisi için kullanılır. Çoklu ABC’den kalıtım mümkündür ama karmaşıklaşabilir.Temel AmaçSaf kontrat/arayüz tanımlama. Ne yapılacağını söyler, nasıl yapılacağını değil.Hem kontrat tanımlama hem de ortak temel implementasyon sağlama.Dil Desteği (Python)Doğrudan interface anahtar kelimesi yok. Benzer işlevsellik ABC'ler veya Protokoller ile sağlanır.abc modülü (ABC sınıfı, @abstractmethod dekoratörü) ile doğrudan desteklenir.İlişki TürüGenellikle "Can-Do" veya "Has-Capability".Genellikle "Is-A" (soyut bir türün alt tipi).
Anahtar Çıkarım: Geleneksel arabirimler tamamen soyut kontratlardır; implementasyon içermezler. Python’daki Soyut Temel Sınıflar (ABCs) ise hem soyut kontratlar tanımlayabilir hem de alt sınıfların paylaşabileceği somut kodlar (metotlar, nitelikler, __init__
) içerebilirler. Bu nedenle, ABC’ler hem arabirimlerin hem de geleneksel (somut) üst sınıfların bazı özelliklerini birleştirir.
Bölüm 3: Python’un Perspektifi: Neden interface
Yok?
Python’un tasarımcıları, dile Java veya C# gibi dillerde bulunan katı bir interface
mekanizması eklememeyi tercih etmişlerdir. Bunun temel nedenleri şunlardır:
Duck Typing Felsefesi: Python’un temel polimorfizm yaklaşımı Duck Typing’e dayanır. Eğer bir nesne beklenen metotlara sahipse, onun belirli bir arayüzü resmi olarak implemente edip etmediği genellikle önemli değildir. Bu, doğal bir esneklik sağlar ve katı arayüz tanımlarına olan ihtiyacı azaltır.
Esneklik: Katı arayüzler bazen gereksiz kısıtlamalar getirebilir. Duck Typing, sınıfların belirli bir arayüzden haberdar olmadan bile, eğer gerekli metotları sağlarlarsa, polimorfik olarak kullanılabilmelerine olanak tanır.
Çoklu Kalıtım Desteği: Python zaten çoklu kalıtımı destekler. Arabirimlerin temel motivasyonlarından biri (Java’da olduğu gibi) çoklu kalıtımın getirdiği bazı sorunları (özellikle Elmas Problemi’ni durum içeren sınıflarda) aşmaktır. Python, MRO (Metot Çözümleme Sırası) ile çoklu kalıtımı yönetir, bu da ayrı bir arabirim mekanizmasına olan ihtiyacı kısmen azaltır.
Basitlik: Python’un temel tasarım hedeflerinden biri basitliktir. Dile yeni bir anahtar kelime ve onunla ilişkili kuralları eklemek yerine, mevcut mekanizmaların (sınıflar, kalıtım, abc
modülü) bu ihtiyacı karşılayabileceği düşünülmüştür.
Ancak bu, Python’da arayüz tanımlama ihtiyacının olmadığı anlamına gelmez. Duck Typing’in belirsiz olabileceği veya kontratların zorunlu kılınması gereken durumlar için ABC’ler ve Protokoller geliştirilmiştir.
Bölüm 4: Python’da Arabirim Benzeri Yapılar Oluşturma
Python’da arabirimlerin sağladığı faydaları (kontrat, polimorfizm, gevşek bağlantı) elde etmek için kullanılan yöntemler şunlardır:
4.1. Soyut Temel Sınıflar (ABCs)
Daha önce detaylıca açıklandığı gibi, ABC’ler Python’da resmi bir arayüz tanımlamanın ve bu arayüze uyulmasını zorunlu kılmanın standart yoludur. Hem soyut metotlarla kontratı belirler hem de istenirse ortak implementasyon sunabilirler.
Ne zaman ABC? Bir grup sınıfın hem ortak bir arayüze uymasını hem de potansiyel olarak ortak bir temel implementasyonu veya durumu (state) paylaşmasını istediğinizde ABC’ler idealdir. Kalıtım hiyerarşisi mantıklı olduğunda (“Is-A”) kullanılır.
4.2. Protokoller (typing.Protocol
) (Python 3.8+)
Protokoller, yapısal alt tipleme (structural subtyping) konseptini Python’a getirir. Bir sınıfın bir protokolü uygulayıp uygulamadığı, o sınıftan miras alıp almadığına değil, protokelde tanımlanan metot ve niteliklere (doğru imzalarla) sahip olup olmadığına bakılarak belirlenir. Bu, geleneksel arabirim fikrine daha yakındır çünkü kalıtım zorunluluğu yoktur.
from typing import Protocol
class DosyaBenzeri(Protocol):
# Bu protokol, 'read' ve 'write' metotlarına sahip olmayı gerektirir
def read(self) -> bytes: ...
def write(self, data: bytes) -> int: ...
# Sadece imzalar tanımlanır, '...' veya 'pass' kullanılır
class BellekAkisi: # DosyaBenzeri'nden miras ALMIYOR!
def init(self):
self.buffer = b""
self._pos = 0
def read(self) -> bytes:
data = self._buffer[self._pos:]
self._pos = len(self._buffer)
print("BellekAkisi.read çağrıldı")
return data
def write(self, data: bytes) -> int:
self._buffer += data
print(f"BellekAkisi.write çağrıldı, {len(data)} byte yazıldı")
return len(data)
class AgAkisi: # DosyaBenzeri'nden miras ALMIYOR!
def read(self) -> bytes:
print("AgAkisi.read çağrıldı")
return b"Ag verisi"
def write(self, data: bytes) -> int:
print(f"AgAkisi.write çağrıldı, {len(data)} byte yazıldı")
return len(data)
def veriyi_kopyala(kaynak: DosyaBenzeri, hedef: DosyaBenzeri): # Tip ipucu olarak protokol
"""DosyaBenzeri protokolüne uyan kaynakdan hedefe veri kopyalar."""
print(f"\n{type(kaynak).name} -> {type(hedef).name_} kopyalanıyor...")
try:
while chunk := kaynak.read(1024): # Örnek bir chunk boyutu
if not chunk: break # Gerçek implementasyonda okuma şekli değişir
hedef.write(chunk)
# Bu basit örnekte read() tüm veriyi döndürdüğü için döngü bir kez çalışır
hedef.write(kaynak.read()) # Kalanı (veya tamamını) yaz
except AttributeError:
print("Hata: Kaynak veya hedef DosyaBenzeri protokolüne uymuyor.")
bellek = BellekAkisi()
ag = AgAkisi()
Statik tip denetleyiciler (MyPy) bu çağrıları onaylar, çünkü
BellekAkisi ve AgAkisi yapısal olarak DosyaBenzeri'ne uyar.
veriyi_kopyala(ag, bellek)
veriyi_kopyala(bellek, ag)
Ne zaman Protokol? Kalıtım ilişkisi kurmak istemediğinizde, farklı hiyerarşilerdeki sınıfların aynı yapısal arayüze uymasını sağlamak istediğinizde ve özellikle statik tip denetleyicileriyle çalışırken kontratları doğrulamak istediğinizde Protokoller güçlü bir seçenektir. Duck Typing’e benzer ama tip güvenliği ekler.
4.3. Duck Typing (Zımni Arabirim)
En esnek yaklaşımdır. Resmi bir kontrat tanımı yoktur. Kod, nesnenin gerekli metotlara sahip olduğunu varsayar ve doğrudan çağırır. Kontrol çalışma zamanında yapılır.
Ne zaman Duck Typing? Esnekliğin ve basitliğin öncelikli olduğu, beklenen arayüzün küçük ve iyi anlaşıldığı veya statik tip kontrolünün kritik olmadığı durumlarda yeterlidir. Küçük scriptler ve prototipleme için idealdir.
Bölüm 5: Karşılaştırmalı Özet: ABCs vs. Interfaces (Kavramsal) vs. Protocols
ÖzellikInterface (Kavramsal)ABC (Python abc)Protocol (Python typing)Implementasyon İçermeHayır (Genellikle)Evet (Somut metotlar olabilir)Hayır (Sadece imzalar)Durum (Nitelikler)Hayır (Genellikle sadece sabitler)EvetEvet (Nitelik imzaları tanımlanabilir)Constructor (__init__
)HayırEvetHayır (Protokolün kendisi için değil)İlişki TürüImplementasyon ("Can-Do")Kalıtım ("Is-A", ama kontrat odaklı)Yapısal Uyumluluk ("Behaves-Like")ZorunlulukSınıf açıkça implemente etmeli (Statik dillerde)Sınıf açıkça miras almalıSınıfın açıkça belirtmesine gerek yok (Yapısal kontrol)Çoklu İlişkiÇoklu implementasyon yaygınÇoklu kalıtım mümkün ama dikkatli kullanılmalıBir sınıf birden fazla protokole uyabilirTip Kontrolü (Python)- (Doğrudan yok)isinstance()/issubclass() ile Runtime kontrolüStatik tip denetleyicileri (MyPy) ile kontrol, isinstance() ile Runtime (@runtime_checkable ile)
Bölüm 6: Pratik Örnek: Bir Ödeme Sistemi Tasarımı
Farklı ödeme yöntemlerini (Kredi Kartı, PayPal, Banka Havalesi) destekleyen bir sistem tasarladığımızı düşünelim. Arabirim/Soyutlama nasıl yardımcı olur?
Yaklaşım 1: ABC Kullanımı
import abc
class IOdemeYontemi(abc.ABC):
@abc.abstractmethod
def odeme_yap(self, tutar: float) -> bool:
"""Belirtilen tutarda ödeme yapar, başarı durumunu döndürür."""
pass
@abc.abstractmethod
def geri_odeme(self, tutar: float) -> bool:
"""Belirtilen tutarda geri ödeme yapar."""
pass
class KrediKartiOdeme(IOdemeYontemi):
def init(self, kart_no, son_kullanma, cvv):
self.kart_no = kart_no # vb.
print("Kredi Kartı yöntemi hazırlandı.")
def odeme_yap(self, tutar: float) -> bool:
print(f"[Kredi Kartı] {tutar:.2f} TL ödeme deneniyor...")
# ... API çağrısı, doğrulama vb. ...
print("[Kredi Kartı] Ödeme başarılı.")
return True
def geri_odeme(self, tutar: float) -> bool:
print(f"[Kredi Kartı] {tutar:.2f} TL geri ödeme deneniyor...")
# ... Geri ödeme API çağrısı ...
print("[Kredi Kartı] Geri ödeme başarılı.")
return True
class PayPalOdeme(IOdemeYontemi):
def init(self, email):
self.email = email
print("PayPal yöntemi hazırlandı.")
def odeme_yap(self, tutar: float) -> bool:
print(f"[PayPal] {self.email} üzerinden {tutar:.2f} TL ödeme deneniyor...")
# ... PayPal API çağrısı ...
print("[PayPal] Ödeme başarılı.")
return True
def geri_odeme(self, tutar: float) -> bool:
print(f"[PayPal] {tutar:.2f} TL geri ödeme deneniyor...")
# ... Geri ödeme API çağrısı ...
print("[PayPal] Geri ödeme başarılı.")
return True
Kullanım
def siparis_ode(odeme_yontemi: IOdemeYontemi, siparis_tutari: float):
print(f"\n{siparis_tutari:.2f} TL tutarındaki sipariş ödeniyor...")
if odeme_yontemi.odeme_yap(siparis_tutari):
print("Sipariş başarıyla ödendi!")
else:
print("Ödeme başarısız oldu!")
kart = KrediKartiOdeme("1234...", "12/25", "123")
paypal = PayPalOdeme("test@example.com")
siparis_ode(kart, 150.75)
siparis_ode(paypal, 85.50)
Bu yaklaşımda, tüm ödeme yöntemlerinin IOdemeYontemi'nden türemesi ve gerekli metotları implemente etmesi zorunludur. Bu, sisteme yeni bir ödeme yöntemi eklenirken kontratın takip edilmesini garanti eder.
Yaklaşım 2: Protokol Kullanımı (Python 3.8+)
Eğer sınıfların ortak bir üst sınıftan gelmesini istemiyorsak veya mevcut sınıfları değiştiremiyorsak, protokol kullanabiliriz.
from typing import Protocol, runtime_checkable
@runtime_checkable # isinstance ile kontrol edebilmek için (isteğe bağlı)
class OdemeProtokolu(Protocol):
def odeme_yap(self, tutar: float) -> bool: ...
def geri_odeme(self, tutar: float) -> bool: ...
Sınıflar Protokol'den miras ALMIYOR
class KrediKartiOdemePro:
# ... (init metodu) ...
def odeme_yap(self, tutar: float) -> bool:
print(f"[Kredi Kartı Pro] {tutar:.2f} TL ödeme...")
return True
def geri_odeme(self, tutar: float) -> bool:
print(f"[Kredi Kartı Pro] {tutar:.2f} TL geri ödeme...")
return True
class PayPalOdemePro:
# ... (init metodu) ...
def odeme_yap(self, tutar: float) -> bool:
print(f"[PayPal Pro] {tutar:.2f} TL ödeme...")
return True
def geri_odeme(self, tutar: float) -> bool:
print(f"[PayPal Pro] {tutar:.2f} TL geri ödeme...")
return True
class BankaHavalesi: # Sadece odeme_yap var, geri_odeme yok!
def odeme_yap(self, tutar: float) -> bool:
print(f"[Banka Havalesi] {tutar:.2f} TL ödeme...")
return True
Kullanım (siparis_ode fonksiyonu aynı kalabilir, tip ipucu OdemeProtokolu olur)
def siparis_ode_proto(odeme_yontemi: OdemeProtokolu, siparis_tutari: float):
print(f"\n{siparis_tutari:.2f} TL tutarındaki sipariş (Proto) ödeniyor...")
# isinstance kontrolü @runtime_checkable sayesinde çalışır
if isinstance(odeme_yontemi, OdemeProtokolu):
if odeme_yontemi.odeme_yap(siparis_tutari):
print("Sipariş başarıyla ödendi! (Proto)")
else:
print("Ödeme başarısız oldu! (Proto)")
else:
print("Hata: Nesne OdemeProtokolu'ne uymuyor.")
kart_pro = KrediKartiOdemePro()
paypal_pro = PayPalOdemePro()
havale = BankaHavalesi()
siparis_ode_proto(kart_pro, 200.0)
siparis_ode_proto(paypal_pro, 50.0)
MyPy bu çağrıda hata verir (BankaHavalesi protokole uymaz).
Çalışma zamanında isinstance kontrolü de False döner.
siparis_ode_proto(havale, 100.0)
Protokol yaklaşımı daha esnektir, çünkü sınıfların belirli bir temel sınıftan miras almasını gerektirmez, sadece yapısal olarak uyumlu olmalarına bakar (statik tip denetleyicileri bunu doğrular).
Bölüm 7: Sonuç: Doğru Soyutlama Aracını Seçmek
Soyut Sınıflar ve Arabirimler (ve Python’daki karşılıkları olan ABC’ler ve Protokoller), Nesne Yönelimli Tasarımın temel araçlarıdır ve kodun esnekliğini, modülerliğini ve sürdürülebilirliğini artırmaya yardımcı olurlar. Her ikisi de sınıfların uyması gereken kontratları tanımlayarak polimorfizmi destekler ve gevşek bağlantıyı teşvik eder.
Temel fark, implementasyon içerme yetenekleri ve kalıtım modellerinde yatar:
Arabirimler (Kavramsal): Saf kontratlardır, implementasyon içermezler, genellikle çoklu implementasyona izin verirler (“Can-Do”).
Soyut Temel Sınıflar (Python ABC): Hem kontrat tanımlayabilir hem de ortak implementasyon sağlayabilirler, kalıtıma dayanırlar (“Is-A”).
Python, doğrudan bir interface
anahtar kelimesine sahip olmasa da, abc modülü ile güçlü bir Soyut Temel Sınıf mekanizması sunar. Bu, hem kontrat zorunluluğu hem de ortak kod paylaşımı gerektiğinde idealdir. Python 3.8 ile gelen typing.Protocol ise, kalıtıma ihtiyaç duymadan yapısal uyumluluğa dayalı, arabirimlere daha yakın bir kontrat tanımlama ve statik tip kontrolü imkanı sunar.
Hangi yaklaşımın seçileceği projenin gereksinimlerine bağlıdır. Ortak bir temel ve davranış seti paylaşan bir sınıf hiyerarşisi için ABC’ler; farklı hiyerarşilerden sınıfların ortak bir yapıya uymasını sağlamak ve statik tip kontrolünden yararlanmak için Protokoller; basit ve esnek durumlar için ise genellikle Duck Typing yeterli olabilir. Bu araçları ve aralarındaki farkları anlamak, Python’da daha bilinçli ve etkili OOP tasarımları yapmanızı sağlayacaktır.
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin