OCP’yi Uygulamak İçin Stratejiler: Soyutlama ve Strateji Deseni
SOLID prensiplerinden Açık/Kapalı Prensibi (Open/Closed Principle — OCP), yazılım varlıklarının (sınıflar, modüller vb.) genişletmeye açık ama değiştirmeye kapalı olması gerektiğini savunur. Bu prensip, yazılımın zamanla değişen gereksinimlere mevcut kodu bozmadan uyum sağlamasını, daha kararlı, esnek ve bakımı kolay olmasını hedefler. Peki, bu teorik prensibi pratikte nasıl uygulayabiliriz? Kodu hem yeni davranışlara açık tutarken hem de mevcut yapısını korumayı nasıl başarırız? Cevap, büyük ölçüde soyutlama (abstraction) ve bu soyutlamayı kullanan tasarım desenlerinde yatar. OCP’yi uygulamak için en yaygın ve etkili iki strateji şunlardır: Soyutlama ve Kalıtım/Polimorfizm: Ortak bir arayüz veya soyut temel sınıf tanımlayarak ve yeni davranışları bu soyutlamadan türeyen yeni alt sınıflar olarak ekleyerek. Strateji Tasarım Deseni (Strategy Pattern) ve Kompozisyon: Farklı algoritmaları veya davranışları ayrı nesnelerde (stratejiler) kapsülleyerek ve ana nesnenin (context) bu stratejilerden birini kullanmasını sağlayarak. Bu rehberde, OCP’yi hayata geçirmek için bu iki temel stratejiyi Python özelinde detaylı bir şekilde inceleyeceğiz. Her stratejinin nasıl çalıştığını, hangi mekanizmaları kullandığını, avantajlarını, dezavantajlarını ve hangi senaryolarda daha uygun olduğunu somut örneklerle açıklayacağız. Bölüm 1: OCP İhlali — Tekrar Hatırlayalım Stratejilere geçmeden önce, OCP’nin neden gerekli olduğunu gösteren tipik bir ihlal senaryosunu hatırlamak faydalı olacaktır. Genellikle bu ihlaller, yeni bir tip veya durum eklendiğinde mevcut kodda değişiklik yapmayı gerektiren if/elif/else bloklarında kendini gösterir. Örneğin, farklı dosya türlerini işleyen bir fonksiyon: OCP İhlali Örneği def dosya_isle(dosya_adi): if dosya_adi.endswith(".txt"): print(f"'{dosya_adi}' metin dosyası işleniyor...") # Metin işleme kodu... elif dosya_adi.endswith(".csv"): print(f"'{dosya_adi}' CSV dosyası işleniyor...") # CSV işleme kodu... elif dosya_adi.endswith(".json"): print(f"'{dosya_adi}' JSON dosyası işleniyor...") # JSON işleme kodu... # YENİ BİR DOSYA TÜRÜ (örn: .xml) DESTEĞİ EKLEMEK İÇİN # BU FONKSİYONU AÇIP YENİ BİR 'elif' EKLEMEK GEREKİR! else: print(f"Desteklenmeyen dosya türü: {dosya_adi}") Kullanım dosya_isle("rapor.txt") dosya_isle("veriler.csv") dosya_isle("ayarlar.json") dosya_isle("semalar.xml") # Bu çağrı desteklenmeyen tür mesajı verir. Bu fonksiyon, yeni dosya türleri için genişletmeye açık değildir, çünkü her yeni tür için kodunun değiştirilmesi gerekir. Bu durum hem riskli (mevcut işleyişi bozabilir) hem de sürdürülemezdir. Bölüm 2: Strateji 1 — Soyutlama ve Kalıtım/Polimorfizm Yoluyla OCP Bu strateji, OCP’yi sağlamak için OOP’nin temel mekanizmaları olan kalıtım ve polimorfizmden yararlanır. Temel Fikir Soyutlama Tanımlama: Değişkenlik gösterecek veya genişletilecek davranış için ortak bir arayüz veya Soyut Temel Sınıf (Abstract Base Class — ABC) tanımlanır. Bu soyutlama, tüm ilgili nesnelerin sahip olması gereken ortak metotları (genellikle soyut metotlar olarak) belirler. Somut Uygulamalar Oluşturma: Her farklı davranış türü (örneğin, her dosya tipi için işleme mantığı) için, soyutlamadan miras alan ve gerekli soyut metotları implemente eden somut (concrete) alt sınıflar oluşturulur. İstemci Kodunu Soyutlamaya Bağlama: Ana iş mantığını yürüten kod (istemci kod), belirli somut sınıflara değil, tanımlanan soyutlamaya (arayüze/ABC’ye) bağımlı hale getirilir. Polimorfizm Kullanımı: İstemci kod, soyutlama türünden bir referans üzerinden metotları çağırdığında, Python çalışma zamanında nesnenin gerçek tipine göre doğru alt sınıfın implementasyonunu (override edilmiş metodu) çalıştırır. Python Uygulaması (ABC ile) Önceki dosya işleme örneğini bu stratejiyle OCP’ye uygun hale getirelim: OCP Çözümü (Soyutlama ve Kalıtım) import abc import os # Dosya uzantısını almak için 1. Soyutlamayı Tanımla (ABC) class IDosyaIsleyici(abc.ABC): """Dosya işleme için soyut temel sınıf (arayüz).""" @abc.abstractmethod def isle(self, dosya_yolu: str): """Verilen dosyayı işler.""" pass @abc.abstractmethod def desteklenen_uzanti(self) -> str: """Bu işleyicinin desteklediği dosya uzantısını döndürür.""" pass 2. Somut Implementasyonları Oluştur class MetinIsleyici(IDosyaIsleyici): def isle(self, dosya_yolu: str): print(f"[Metin İşleyici] '{dosya_yolu}' işleniyor...") # ... Metin dosyası okuma/işleme mantığı ... def desteklenen_uzanti(self) -> str: return ".txt" class CsvIsleyici(IDosyaIsleyici): def isle(self, dosya_yolu: str): print(f"[CSV İşleyici] '{dosya_yolu}' işleniyor...") # ... CSV dosyası okuma/işleme mantığı (örn: pandas ile) ... def desteklenen_uzanti(self) -> str: return ".csv" class JsonIsleyici(IDosyaIsleyici): def isle(self, dosya_yolu: str): print(f"[JSON İşl

SOLID prensiplerinden Açık/Kapalı Prensibi (Open/Closed Principle — OCP), yazılım varlıklarının (sınıflar, modüller vb.) genişletmeye açık ama değiştirmeye kapalı olması gerektiğini savunur. Bu prensip, yazılımın zamanla değişen gereksinimlere mevcut kodu bozmadan uyum sağlamasını, daha kararlı, esnek ve bakımı kolay olmasını hedefler. Peki, bu teorik prensibi pratikte nasıl uygulayabiliriz? Kodu hem yeni davranışlara açık tutarken hem de mevcut yapısını korumayı nasıl başarırız?
Cevap, büyük ölçüde soyutlama (abstraction) ve bu soyutlamayı kullanan tasarım desenlerinde yatar. OCP’yi uygulamak için en yaygın ve etkili iki strateji şunlardır:
Soyutlama ve Kalıtım/Polimorfizm: Ortak bir arayüz veya soyut temel sınıf tanımlayarak ve yeni davranışları bu soyutlamadan türeyen yeni alt sınıflar olarak ekleyerek.
Strateji Tasarım Deseni (Strategy Pattern) ve Kompozisyon: Farklı algoritmaları veya davranışları ayrı nesnelerde (stratejiler) kapsülleyerek ve ana nesnenin (context) bu stratejilerden birini kullanmasını sağlayarak.
Bu rehberde, OCP’yi hayata geçirmek için bu iki temel stratejiyi Python özelinde detaylı bir şekilde inceleyeceğiz. Her stratejinin nasıl çalıştığını, hangi mekanizmaları kullandığını, avantajlarını, dezavantajlarını ve hangi senaryolarda daha uygun olduğunu somut örneklerle açıklayacağız.
Bölüm 1: OCP İhlali — Tekrar Hatırlayalım
Stratejilere geçmeden önce, OCP’nin neden gerekli olduğunu gösteren tipik bir ihlal senaryosunu hatırlamak faydalı olacaktır. Genellikle bu ihlaller, yeni bir tip veya durum eklendiğinde mevcut kodda değişiklik yapmayı gerektiren if/elif/else bloklarında kendini gösterir.
Örneğin, farklı dosya türlerini işleyen bir fonksiyon:
OCP İhlali Örneği
def dosya_isle(dosya_adi):
if dosya_adi.endswith(".txt"):
print(f"'{dosya_adi}' metin dosyası işleniyor...")
# Metin işleme kodu...
elif dosya_adi.endswith(".csv"):
print(f"'{dosya_adi}' CSV dosyası işleniyor...")
# CSV işleme kodu...
elif dosya_adi.endswith(".json"):
print(f"'{dosya_adi}' JSON dosyası işleniyor...")
# JSON işleme kodu...
# YENİ BİR DOSYA TÜRÜ (örn: .xml) DESTEĞİ EKLEMEK İÇİN
# BU FONKSİYONU AÇIP YENİ BİR 'elif' EKLEMEK GEREKİR!
else:
print(f"Desteklenmeyen dosya türü: {dosya_adi}")
Kullanım
dosya_isle("rapor.txt")
dosya_isle("veriler.csv")
dosya_isle("ayarlar.json")
dosya_isle("semalar.xml") # Bu çağrı desteklenmeyen tür mesajı verir.
Bu fonksiyon, yeni dosya türleri için genişletmeye açık değildir, çünkü her yeni tür için kodunun değiştirilmesi gerekir. Bu durum hem riskli (mevcut işleyişi bozabilir) hem de sürdürülemezdir.
Bölüm 2: Strateji 1 — Soyutlama ve Kalıtım/Polimorfizm Yoluyla OCP
Bu strateji, OCP’yi sağlamak için OOP’nin temel mekanizmaları olan kalıtım ve polimorfizmden yararlanır.
Temel Fikir
Soyutlama Tanımlama: Değişkenlik gösterecek veya genişletilecek davranış için ortak bir arayüz veya Soyut Temel Sınıf (Abstract Base Class — ABC) tanımlanır. Bu soyutlama, tüm ilgili nesnelerin sahip olması gereken ortak metotları (genellikle soyut metotlar olarak) belirler.
Somut Uygulamalar Oluşturma: Her farklı davranış türü (örneğin, her dosya tipi için işleme mantığı) için, soyutlamadan miras alan ve gerekli soyut metotları implemente eden somut (concrete) alt sınıflar oluşturulur.
İstemci Kodunu Soyutlamaya Bağlama: Ana iş mantığını yürüten kod (istemci kod), belirli somut sınıflara değil, tanımlanan soyutlamaya (arayüze/ABC’ye) bağımlı hale getirilir.
Polimorfizm Kullanımı: İstemci kod, soyutlama türünden bir referans üzerinden metotları çağırdığında, Python çalışma zamanında nesnenin gerçek tipine göre doğru alt sınıfın implementasyonunu (override edilmiş metodu) çalıştırır.
Python Uygulaması (ABC ile)
Önceki dosya işleme örneğini bu stratejiyle OCP’ye uygun hale getirelim:
OCP Çözümü (Soyutlama ve Kalıtım)
import abc
import os # Dosya uzantısını almak için
1. Soyutlamayı Tanımla (ABC)
class IDosyaIsleyici(abc.ABC):
"""Dosya işleme için soyut temel sınıf (arayüz)."""
@abc.abstractmethod
def isle(self, dosya_yolu: str):
"""Verilen dosyayı işler."""
pass
@abc.abstractmethod
def desteklenen_uzanti(self) -> str:
"""Bu işleyicinin desteklediği dosya uzantısını döndürür."""
pass
2. Somut Implementasyonları Oluştur
class MetinIsleyici(IDosyaIsleyici):
def isle(self, dosya_yolu: str):
print(f"[Metin İşleyici] '{dosya_yolu}' işleniyor...")
# ... Metin dosyası okuma/işleme mantığı ...
def desteklenen_uzanti(self) -> str:
return ".txt"
class CsvIsleyici(IDosyaIsleyici):
def isle(self, dosya_yolu: str):
print(f"[CSV İşleyici] '{dosya_yolu}' işleniyor...")
# ... CSV dosyası okuma/işleme mantığı (örn: pandas ile) ...
def desteklenen_uzanti(self) -> str:
return ".csv"
class JsonIsleyici(IDosyaIsleyici):
def isle(self, dosya_yolu: str):
print(f"[JSON İşleyici] '{dosya_yolu}' işleniyor...")
# ... JSON dosyası okuma/işleme mantığı (örn: json modülü ile) ...
def desteklenen_uzanti(self) -> str:
return ".json"
YENİ dosya türü için SADECE yeni bir sınıf eklenir:
class XmlIsleyici(IDosyaIsleyici):
def isle(self, dosya_yolu: str):
print(f"[XML İşleyici] '{dosya_yolu}' işleniyor...")
# ... XML dosyası okuma/işleme mantığı ...
def desteklenen_format(self) -> str: # Hata: Metot adı farklı! (LSP ihlali olabilir)
return ".xml"
# Düzeltme:
def desteklenen_uzanti(self) -> str:
return ".xml"
3. İstemci Kod Soyutlamaya Bağlansın (ve Polimorfizm Kullansın)
class DosyaYoneticisi:
def init(self):
# Mevcut tüm işleyicileri (veya dinamik olarak bulunabilecekleri) tutalım
# Bu kısım daha gelişmiş hale getirilebilir (örn: otomatik kayıt)
self.isleyiciler: dict[str, IDosyaIsleyici] = {}
self.isleyici_ekle(MetinIsleyici())
self.isleyici_ekle(CsvIsleyici())
self.isleyici_ekle(JsonIsleyici())
self.isleyici_ekle(XmlIsleyici()) # Yeni işleyici kolayca eklendi
def isleyici_ekle(self, isleyici: IDosyaIsleyici):
"""Listeye yeni bir işleyici ekler."""
if isinstance(isleyici, IDosyaIsleyici):
uzanti = isleyici.desteklenen_uzanti()
self.isleyiciler[uzanti] = isleyici
print(f"'{uzanti}' için işleyici eklendi: {type(isleyici).name}")
else:
print("Uyarı: Geçersiz işleyici türü eklenemedi.")
# BU FONKSİYON ARTIK YENİ DOSYA TİPLERİ İÇİN DEĞİŞTİRİLMEYECEK!
def dosyayi_isle(self, dosya_yolu: str):
"""Dosya uzantısına göre uygun işleyiciyi bulur ve çalıştırır."""
# Dosya uzantısını al (küçük harf)
uzanti = os.path.splitext(dosya_yolu)[1].lower()
# Uygun işleyiciyi bul
isleyici = self.isleyiciler.get(uzanti)
if isleyici:
# Polimorfizm: Hangi işleyicinin isle() metodunun çalışacağına
# Python karar verir. DosyaYoneticisi bunu bilmek zorunda değil.
try:
isleyici.isle(dosya_yolu)
except Exception as e:
print(f"'{dosya_yolu}' işlenirken hata: {e}")
else:
print(f"'{uzanti}' uzantısı için desteklenen işleyici bulunamadı.")
Kullanım
yonetici = DosyaYoneticisi()
print("-" * 30)
yonetici.dosyayi_isle("rapor.txt")
yonetici.dosyayi_isle("veriler.csv")
yonetici.dosyayi_isle("ayarlar.json")
yonetici.dosyayi_isle("semalar.xml") # Yeni eklenen tür sorunsuz çalışır
yonetici.dosyayi_isle("resim.jpg") # Desteklenmeyen tür
Bu Stratejinin Değerlendirmesi:
OCP Sağlandı mı? Evet. Yeni bir dosya türünü (XML) desteklemek için sadece yeni bir XmlIsleyici sınıfı ekledik. Mevcut DosyaYoneticisi sınıfını veya diğer işleyici sınıflarını değiştirmemiz gerekmedi. Sistem genişletmeye açık, değiştirmeye kapalı hale geldi.
Mekanizma: Soyutlama (IDosyaIsleyici ABC'si) ve Polimorfizm (isleyici.isle() çağrısının doğru implementasyonu çalıştırması).
Avantajlar: Yeni türler eklemek kolay ve güvenli. Kod daha modüler. İşleyici mantığı kendi sınıflarında izole edilmiş. Tasarım kontratı (ABC ile) net.
Dezavantajlar/Dikkat Edilmesi Gerekenler: Bir kalıtım hiyerarşisi oluşturur (sınıflar arasında “Is-A” ilişkisi mantıklı olmalı). Biraz daha fazla başlangıç kodu (ABC tanımı) gerektirebilir.
Bölüm 3: Strateji 2 — Strateji Tasarım Deseni ve Kompozisyon Yoluyla OCP
Bu strateji, değişebilecek algoritmaları veya davranışları (stratejileri) kendi başlarına nesneler olarak kapsüllemeye ve ana nesnenin (context) bu stratejilerden birini kullanmasına dayanır. Kalıtım yerine kompozisyonu (“Has-A” ilişkisi) temel alır.
Temel Fikir
Strateji Arayüzü Tanımlama: Tüm farklı algoritma veya davranışların uygulayacağı ortak bir arayüz (genellikle bir ABC veya Protokol) tanımlanır. Bu arayüz, algoritmanın çalıştırılacağı ana metodu (örn: execute(), calculate()) içerir.
Somut Stratejiler Oluşturma: Her farklı algoritma veya davranış, strateji arayüzünü implemente eden ayrı bir somut strateji sınıfı olarak oluşturulur.
Bağlam (Context) Sınıfı Oluşturma: Asıl işi yapan veya süreci yöneten bir bağlam (context) sınıfı tanımlanır. Bu sınıf, içinde bir strateji nesnesine referans tutar.
Bağlamın Stratejiyi Kullanması (Delegasyon): Bağlam sınıfı, değişkenlik gösteren işlemi doğrudan kendisi yapmak yerine, o an tuttuğu strateji nesnesinin ilgili metodunu çağırarak işi ona delege eder.
Stratejiyi Ayarlama/Değiştirme: Bağlam nesnesinin hangi stratejiyi kullanacağı genellikle dışarıdan belirlenir (constructor veya bir setter metodu aracılığıyla — Dependency Injection).
Python Uygulaması (Strateji Deseni ile)
Bu sefer farklı bir örnek üzerinden gidelim: Bir e-ticaret sitesinde farklı indirim stratejileri uygulama.
OCP Çözümü (Strateji Deseni)
import abc
from decimal import Decimal # Para işlemleri için Decimal kullanmak daha iyidir
1. Strateji Arayüzünü Tanımla
class IIndirimStratejisi(abc.ABC):
@abc.abstractmethod
def indirim_uygula(self, tutar: Decimal) -> Decimal:
"""Verilen tutara indirimi uygular ve yeni tutarı döndürür."""
pass
2. Somut Stratejileri Oluştur
class YuzdeIndirimi(IIndirimStratejisi):
def init(self, oran: float): # Oran 0 ile 1 arasında olmalı (örn: 0.10 for %10)
if not 0 <= oran <= 1:
raise ValueError("Oran 0 ile 1 arasında olmalıdır.")
self.oran = Decimal(str(oran)) # Decimal'e çevir
def indirim_uygula(self, tutar: Decimal) -> Decimal:
indirim_miktari = tutar * self.oran
print(f"Uygulanan %{self.oran*100:.0f} indirim: {indirim_miktari:.2f} TL")
return tutar - indirim_miktari
class SabitTutarIndirimi(IIndirimStratejisi):
def init(self, indirim_tutari: float):
if indirim_tutari < 0:
raise ValueError("İndirim tutarı negatif olamaz.")
self.indirim_tutari = Decimal(str(indirim_tutari))
def indirim_uygula(self, tutar: Decimal) -> Decimal:
indirim = min(tutar, self.indirim_tutari) # Tutar negatif olmamalı
print(f"Uygulanan sabit indirim: {indirim:.2f} TL")
return tutar - indirim
YENİ İNDİRİM TÜRÜ: Sezon Sonu İndirimi (örn: %30)
class SezonSonuIndirimi(IIndirimStratejisi):
def indirim_uygula(self, tutar: Decimal) -> Decimal:
indirim_miktari = tutar * Decimal("0.30")
print(f"Uygulanan Sezon Sonu (%30) indirim: {indirim_miktari:.2f} TL")
return tutar - indirim_miktari
3. Bağlam (Context) Sınıfını Oluştur
class Sepet:
def init(self, indirim_stratejisi: IIndirimStratejisi = None): # Başlangıçta strateji olmayabilir
self.urunler = []
self.indirim_stratejisi = indirim_stratejisi
def urun_ekle(self, ad: str, fiyat: float):
self.urunler.append({"ad": ad, "fiyat": Decimal(str(fiyat))})
print(f"'{ad}' sepete eklendi.")
def indirim_stratejisi_ata(self, strateji: IIndirimStratejisi):
print(f"\nİndirim stratejisi {type(strateji).name_} olarak ayarlandı.")
self._indirim_stratejisi = strateji
def toplam_tutar_hesapla(self) -> Decimal:
ham_tutar = sum(urun["fiyat"] for urun in self.urunler)
print(f"\nAra Toplam: {ham_tutar:.2f} TL")
if self._indirim_stratejisi:
# 4. İşlemi Stratejiye Delege Et
indirimli_tutar = self._indirim_stratejisi.indirim_uygula(ham_tutar)
print(f"İndirim Sonrası Toplam Tutar: {indirimli_tutar:.2f} TL")
return indirimli_tutar
else:
print("İndirim uygulanmadı.")
print(f"Toplam Tutar: {ham_tutar:.2f} TL")
return ham_tutar
Kullanım
sepet = Sepet()
sepet.urun_ekle("Kitap", 55.0)
sepet.urun_ekle("Kalem", 15.5)
sepet.urun_ekle("Defter", 29.5)
Başlangıçta indirim yok
tutar1 = sepet.toplam_tutar_hesapla()
%10 indirim stratejisi ata
yuzde_10_indirim = YuzdeIndirimi(0.10)
sepet.indirim_stratejisi_ata(yuzde_10_indirim)
tutar2 = sepet.toplam_tutar_hesapla()
20 TL sabit indirim stratejisi ata
sabit_20_indirim = SabitTutarIndirimi(20.0)
sepet.indirim_stratejisi_ata(sabit_20_indirim)
tutar3 = sepet.toplam_tutar_hesapla()
Yeni Sezon Sonu indirimini kullanmak için Sepet sınıfını DEĞİŞTİRMEYE GEREK YOK!
sezon_indirimi = SezonSonuIndirimi()
sepet.indirim_stratejisi_ata(sezon_indirimi)
tutar4 = sepet.toplam_tutar_hesapla()
Bu Stratejinin Değerlendirmesi:
OCP Sağlandı mı? Evet. Yeni bir indirim türü (SezonSonuIndirimi) eklemek için sadece yeni bir strateji sınıfı yazdık. Sepet sınıfı (Context) hiç değiştirilmedi. Sistem yeni indirim algoritmaları eklenmesine açık, ancak Sepet sınıfı bu değişikliklere kapalı.
Mekanizma: Soyutlama (IIndirimStratejisi ABC'si), Kompozisyon (Sepet bir IIndirimStratejisi nesnesi içeriyor) ve Delegasyon (Sepet, indirim hesaplamayı strateji nesnesine devrediyor).
Avantajlar: Algoritmalar (stratejiler) birbirinden ve bağlamdan bağımsızdır. Çalışma zamanında strateji değiştirmek kolaydır. Kalıtım hiyerarşisinin karmaşıklığından kaçınılır. Farklı bağlamlarda aynı stratejiler yeniden kullanılabilir.
Dezavantajlar/Dikkat Edilmesi Gerekenler: Sistemdeki sınıf sayısı artabilir (her strateji için bir sınıf). İstemci kodun (stratejiyi ayarlayan kodun) hangi stratejinin ne zaman uygun olduğunu bilmesi gerekebilir.
Bölüm 5: Hangi Stratejiyi Ne Zaman Kullanmalı?
Her iki strateji de OCP’yi uygulamak için geçerli yollardır, ancak farklı durumlarda daha uygun olabilirler:
Soyutlama ve Kalıtım/Polimorfizm Kullanımı:
Ne zaman? Sınıflar arasında net bir “Is-A” hiyerarşisi olduğunda.
Ne zaman? Alt sınıfların, üst sınıfın bazı ortak davranışlarını veya durumunu (niteliklerini) miras alması gerektiğinde.
Ne zaman? Genişletilecek davranış, sınıfın temel kimliğiyle yakından ilişkili olduğunda (örneğin, farklı Şekil
türlerinin alan
hesaplaması).
Örnekler: GUI widget hiyerarşileri, belge türleri (Text, PDF, Word), hayvan sınıflandırması.
Strateji Deseni ve Kompozisyon Kullanımı:
Ne zaman? Bir nesnenin davranışının veya algoritmasının çalışma zamanında değişmesi gerektiğinde.
Ne zaman? Birbirinin yerine geçebilen farklı algoritmalar veya iş kuralları ailesi olduğunda (sıralama, doğrulama, fiyatlandırma vb.).
Ne zaman? Kalıtım hiyerarşisini karmaşıklaştırmaktan veya “Is-A” ilişkisi zorlamaktan kaçınmak istediğinizde.
Ne zaman? Davranışın nesnenin durumundan ziyade bağımsız bir algoritma olarak modellenebildiği durumlarda.
Örnekler: Farklı sıralama algoritmaları, farklı veri sıkıştırma yöntemleri, farklı ödeme stratejileri, farklı loglama mekanizmaları.
Bazen bu iki strateji birlikte de kullanılabilir. Örneğin, bir strateji arayüzünü implemente eden sınıflar aynı zamanda ortak bir temel sınıftan da miras alabilirler.
Bölüm 6: Sonuç: Değişime Hazır Kodlar Yazmak
Açık/Kapalı Prensibi (OCP), yazılım mühendisliğinde kararlılık ve esneklik arasında bir denge kurmayı hedefler. Yeni özellikler eklemenin veya mevcut davranışları genişletmenin, çalışan ve test edilmiş kodu kırma riski olmadan yapılabilmesi gerektiğini savunur.
Bu prensibi Python’da uygulamanın temel yolu soyutlamadan geçer. Değişkenlik gösterebilecek noktaları belirleyip, bu noktalar için kararlı arayüzler (Soyut Temel Sınıflar veya Protokoller gibi) tanımlayarak ve ana iş mantığını bu soyutlamalara bağımlı hale getirerek OCP’ye ulaşabiliriz.
İki ana strateji öne çıkar:
Soyutlama ve Kalıtım/Polimorfizm: “Is-A” ilişkisi olan ve ortak bir temel üzerine inşa edilen hiyerarşiler için uygundur. Yeni türler, soyutlamadan türeyen yeni sınıflar olarak eklenir.
Strateji Deseni ve Kompozisyon: Birbirinin yerine geçebilen algoritmalar veya davranışlar için idealdir. Davranışlar ayrı nesnelerde kapsüllenir ve ana nesne bunları delege ederek kullanır. Yeni davranışlar, yeni strateji sınıfları olarak eklenir.
OCP’ye uygun tasarımlar yapmak, başlangıçta biraz daha fazla planlama ve soyutlama gerektirse de, uzun vadede büyük faydalar sağlar. Kodunuz:
Daha kararlı olur (mevcut kod daha az değişir).
Daha esnek olur (yeni özellikler daha kolay eklenir).
Daha kolay test edilebilir hale gelir.
Daha anlaşılır ve bakımı kolay olur.
OCP’yi bir tasarım hedefi olarak benimsemek, zamanla değişen ve gelişen, sağlam ve sürdürülebilir yazılım sistemleri oluşturmanın anahtarlarından biridir.
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin