📌 Hızlı Özet & Temel Çıkarım:
Tasarım Kalıpları (Design Patterns), yazılım mühendislerinin nesiller boyunca karşılaştığı tekrar eden mimari problemlere verilen test edilmiş ve kanıtlanmış ortak çözüm şablonlarıdır. Factory (Fabrika) ile nesne üretim karmaşasını izole etmek, Strategy (Strateji) ile çalışma anında algoritmaları dinamik olarak değiştirmek ve Observer (Gözlemci) ile olay odaklı gevşek bağlılık (loose coupling) kurmak; projelerin büyüdükçe spagetti koda dönüşmesini engeller.


1. Tasarım Kalıpları Neden Ezberlenmemelidir?

Tasarım kalıplarını kitaptaki şekilleriyle ezberleyip her probleme uyarlamaya çalışmak (Golden Hammer anti-pattern) yazılım dünyasının en büyük hatalarındandır.

Kalıplar bir amaç değil, belirli tasarım takaslarını (trade-offs) yönetmek için birer araçtır:

  1. Yaratımsal (Creational): Nesnelerin nasıl oluşturulacağını soyutlar (Factory, Singleton, Builder).
  2. Yapısal (Structural): Sınıfların ve nesnelerin nasıl bir araya geleceğini düzenler (Adapter, Decorator, Facade).
  3. Davranışsal (Behavioral): Nesneler arasındaki sorumluluk ve algoritma akışını yönetir (Strategy, Observer, Command).

2. Strategy Kalıbı: Çalışma Zamanında Dinamik Karar Verme

Örnek Senaryo: Bir navigasyon sistemi yol koşullarına göre rotayı hesaplarken farklı algoritmalar (En Hızlı Yol, En Az Yakıt Tüketen Yol veya Manzaralı Yol) kullanabilir. Dev bir if-else veya switch-case bloğu yazmak yerine Strategy Deseni uygulanır:

from abc import ABC, abstractmethod

# Strateji Arayuzu
class RoutingStrategy(ABC):
    @abstractmethod
    def calculate_path(self, start: str, end: str) -> list:
        pass

# Somut Strateji 1
class FastestRouteStrategy(RoutingStrategy):
    def calculate_path(self, start: str, end: str) -> list:
        print(f"{start} -> {end} arasinda otoyol odakli en hizli rota hesaplandi.")
        return ["Otoyol-1", "Viyaduk-3"]

# Somut Strateji 2
class EcoRouteStrategy(RoutingStrategy):
    def calculate_path(self, start: str, end: str) -> list:
        print(f"{start} -> {end} arasinda yakit tasarruflu rota hesaplandi.")
        return ["Koy Yolu-4", "Sahil Yolu-2"]

# Baglam (Context) Sinifi
class NavigatorContext:
    def __init__(self, strategy: RoutingStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: RoutingStrategy):
        self._strategy = strategy

    def build_navigation(self, start: str, end: str):
        return self._strategy.calculate_path(start, end)

# Kullanim
nav = NavigatorContext(FastestRouteStrategy())
nav.build_navigation("Liman A", "Liman B")

# Calisma zamaninda batarya azaldi, stratejiyi tek satirda degistir:
nav.set_strategy(EcoRouteStrategy())
nav.build_navigation("Liman A", "Liman B")

Bu yapı sayesinde yarın sisteme “COLREGs Uyumlu Deniz Rotası” adında yeni bir strateji eklendiğinde mevcut tek bir satır kod değiştirilmez; sadece yeni bir sınıf yazılır (Open/Closed Prensibi).


3. Observer Kalıbı: Sensör Olaylarını Dağıtmak

Bir deniz aracında GPS verisi güncellendiğinde; Rota Planlayıcı, Veri Kaydedici (Blackbox Logger) ve Yer İstasyonu Telemetrisi aynı anda haberdar edilmelidir.

Observer kalıbı ile veri kaynağı (Subject) dinleyicileri (Observers) bir liste olarak tutar ve notify() ile hepsine yayın yapar. Bu durum ROS 2’deki Topic Publish-Subscribe mekanizmasının saf nesne yönelimli karşılığıdır.


4. İlgili Konular ve İç Bağlantılar


5. Sıkça Sorulan Sorular

Singleton Kalıbı neden sıklıkla bir “anti-pattern” olarak görülür?

Singleton, sistem genelinde tek bir nesne oluşturulmasını ve global erişimi garanti eder (örneğin veritabanı bağlantı havuzu). Ancak global durum (global state) yarattığı için birim testlerinin birbirinden izole çalışmasını engeller ve çok iş parçacıklı sistemlerde gizli kilitlenmelere (race condition) sebep olabilir. Bunun yerine Dependency Injection tercih edilmelidir.

Modern programlama dillerinde Factory kalıbına hala ihtiyaç var mıdır?

Python veya JavaScript gibi dillerde fonksiyonlar birinci sınıf vatandaş (first-class citizen) olduğu ve nesne üretimini doğrudan fonksiyon referanslarıyla yapabildiğimiz için hantal Factory sınıfları yerine basit fabrika fonksiyonları (factory functions) kullanılır.

6. Modern Dillerde Kalıpların Evrimi ve Sadeleşmesi

Tasarım kalıpları ilk ortaya çıktığında (1994 GoF kitabı), C++ ve Java gibi diller fonksiyonel programlama yeteneklerinden yoksundu. Bu nedenle en basit davranış değişiklikleri için bile devasa sınıf hiyerarşileri kuruluyordu.

Günümüzde ise:

  • Command Deseni: Ayrı bir sınıf yerine basit bir Lambda / Callable fonksiyonu ile çözülür.
  • Strategy Deseni: Strateji arayüzü yerine bir üst seviye fonksiyona parametre olarak fonksiyon göndermek (fn: Callable) yeterlidir.
  • Decorator Deseni: Python’ın yerel @decorator sözdizimi ile 5 satırda uygulanır.

Mühendislikte amaç kalıbın ismini kodda yaşatmak değil; kalıbın sunduğu esneklik ve gevşek bağlılık (loose coupling) felsefesini modern dilin en zarif araçlarıyla hayata geçirmektir.

7. Dependency Injection (DI) Konteynerleri ile Bağımlılık Yönetimi

Büyük ölçekli kurumsal uygulamalarda sınıfların birbirine elle enjekte edilmesi kurucu parametrelerini şişirir. Bu karmaşayı çözmek için modern dillerde IoC (Inversion of Control) konteynerleri kullanılır.

Konteyner, hangi sınıfın hangi arayüzü uyguladığını tek bir merkezde kayıt altına alır ve çalışma anında nesneleri otomatik olarak birleştirir. Bu yaklaşım, bir projede test ortamı ile prodüksiyon ortamı arasındaki veritabanı sürücüsü veya mesaj broker’ı değişimini tek bir yapılandırma satırına indirir.

8. Tasarım Kalıpları ile Refactoring Döngüsü: Ne Zaman Başlamalı?

Tasarım kalıplarını boş bir projede sıfırdan uygulamaya çalışmak çoğu zaman erken soyutlama (premature abstraction) hatasına yol açar. En sağlıklı mühendislik yaklaşımı TDD ve Refactoring döngüsünü işletmektir:

  1. Önce Basitçe Çalıştır: Kodu en basit, doğrudan ve anlaşılır şekilde yazın.
  2. İkinci Kez Tekrar Ettiğinde Not Et: Benzer bir mantık başka bir yerde ortaya çıktığında acele etmeyin.
  3. Üçüncü Kez Tekrar Ettiğinde Soyutla (Rule of Three): Aynı problem üçüncü kez karşınıza çıktığında Strategy, Factory veya Observer kalıbına dönüştürün. Bu pragmatik disiplin, projenizi hem aşırı mühendislik felaketinden korur hem de kod tabanınızın ihtiyaç duyduğu anda doğru esnekliğe kavuşmasını sağlar.