← Kursa Dön
📄 Text · 18 min

Design Patterns: Singleton, Factory, Observer

Bir mimar düşün. Her bina farklıdır ama bazı yapısal çözümler hep tekrarlanır: kemer, kubbe, kolon... Bunlar yüzyılların deneyimiyle "kanıtlanmış" çözümlerdir. Biri "nasıl geniş bir alanı desteklerim?" diye sorduğunda, sıfırdan deney yapmazsın — kubbe kullanırsın.

Yazılımda da aynı. Design pattern'ler, yazılım geliştirmede tekrar eden sorunlara tekrar tekrar işe yaradığı kanıtlanmış çözümlerdir. Gang of Four (GoF) kitabı 1994'te 23 pattern tanımladı. Ama Python'da bunların çoğu gereksiz veya çok daha basit çözülebilir.

Bu derste Python'a en uygun pattern'leri, gerçek örneklerle ve Python'un güçlü yanlarını kullanarak öğreneceğiz.


Pattern'lere Neden İhtiyacımız Var?

Pattern öğrenmenin üç faydası:

  1. Ortak dil: "Bu bir Observer pattern" dediğinde, tüm geliştiriciler ne demek istediğini anlar

  2. Kanıtlanmış çözüm: Tekerleği yeniden icat etme, deneyimden yararlan

  3. Tasarım düşüncesi: Kodu nasıl yapılandıracağını düşünmeyi öğretir

Ama dikkat: pattern bilmek, her yere pattern koymak anlamına gelmez. Bunu dersin sonunda tekrar konuşacağız.


Singleton — Tek Instance

Problem

Bazı nesnelerden sadece bir tane olmasını istersin: veritabanı bağlantısı, konfigürasyon yöneticisi, logger...

Analoji

Bir ülkede tek bir merkez bankası vardır. İkinci bir tane açmazsın. Kim başvurursa başvursun, aynı merkez bankasına gider.

Python'da Modül = Singleton

Python'da en basit singleton, modülün kendisidir. Bir modül ilk import edildiğinde çalışır ve sonraki import'larda aynı nesne döner:

# config.py — Bu dosyanın kendisi bir singleton!
import json

_config = None

def _load_config():
    global _config
    if _config is None:
        with open("config.json") as f:
            _config = json.load(f)
    return _config

def get(key, default=None):
    config = _load_config()
    return config.get(key, default)

# Kullanım (başka dosyada)
# import config
# db_host = config.get("database_host", "localhost")

Her yerden import config yaptığında aynı nesneye erişirsin. Başka bir şeye gerek yok!

Sınıf ile Singleton

Eğer gerçekten bir sınıf istiyorsan:

class DatabaseConnection:
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self, host="localhost", port=5432):
        if self._initialized:
            return
        self.host = host
        self.port = port
        self._initialized = True
        print(f"Bağlantı oluşturuldu: {host}:{port}")

# Test
db1 = DatabaseConnection("localhost", 5432)
db2 = DatabaseConnection("remote", 3306)  # Bu çağrıda yeni instance oluşmaz

print(db1 is db2)      # True — aynı nesne!
print(db2.host)         # "localhost" — ilk init geçerli

💡 İpucu: Python'da singleton'a gerçekten ihtiyacın olduğu durumlar azdır. Çoğu zaman bir modül-seviye değişken veya basit bir fonksiyon yeterlidir. Singleton'ı test etmek zordur çünkü global state oluşturur.


Factory — Nesne Oluşturmayı Soyutlama

Problem

Farklı koşullara göre farklı nesneler oluşturman gerekiyor. if/elif zinciri yazmak istemiyorsun.

Analoji

Bir pizza restoranına giriyorsun. "Margarita" diyorsun, mutfak nasıl yapacağını biliyor. "Karışık" diyorsun, farklı bir tarif uygulanıyor. Sen sadece istediğini söylüyorsun, yapım detayı mutfağın sorumluluğu.

Basit Factory

class Dog:
    def speak(self):
        return "Hav hav!"

class Cat:
    def speak(self):
        return "Miyav!"

class Bird:
    def speak(self):
        return "Cik cik!"

def create_animal(animal_type):
    """Factory fonksiyonu — nesne oluşturmayı soyutlar"""
    animals = {
        "dog": Dog,
        "cat": Cat,
        "bird": Bird,
    }
    
    animal_class = animals.get(animal_type.lower())
    if animal_class is None:
        raise ValueError(f"Bilinmeyen hayvan: {animal_type}")
    
    return animal_class()

# Kullanım
animal = create_animal("dog")
print(animal.speak())  # Hav hav!

animal = create_animal("cat")
print(animal.speak())  # Miyav!

Dictionary'ye sınıf referanslarını koymak, Python'ın first-class özelliği sayesinde çok doğal.

Parametreli Factory

class Notification:
    def send(self, message):
        raise NotImplementedError

class EmailNotification(Notification):
    def __init__(self, recipient):
        self.recipient = recipient
    
    def send(self, message):
        print(f"📧 Email to {self.recipient}: {message}")

class SMSNotification(Notification):
    def __init__(self, phone):
        self.phone = phone
    
    def send(self, message):
        print(f"📱 SMS to {self.phone}: {message}")

class PushNotification(Notification):
    def __init__(self, device_id):
        self.device_id = device_id
    
    def send(self, message):
        print(f"🔔 Push to {self.device_id}: {message}")

class NotificationFactory:
    _creators = {
        "email": EmailNotification,
        "sms": SMSNotification,
        "push": PushNotification,
    }
    
    @classmethod
    def create(cls, channel, **kwargs):
        creator = cls._creators.get(channel)
        if not creator:
            raise ValueError(f"Desteklenmeyen kanal: {channel}")
        return creator(**kwargs)
    
    @classmethod
    def register(cls, channel, creator_class):
        """Yeni bildirim tipi ekle — genişlemeye açık!"""
        cls._creators[channel] = creator_class

# Kullanım
notif = NotificationFactory.create("email", recipient="ali@example.com")
notif.send("Siparişiniz hazır!")

notif = NotificationFactory.create("sms", phone="+905551234567")
notif.send("Kodunuz: 1234")

register metodu sayesinde, mevcut kodu değiştirmeden yeni bildirim türleri ekleyebilirsin (Open/Closed prensibi).


Observer — Olay Sistemi

Problem

Bir nesnede değişiklik olduğunda, diğer nesnelerin haberdar olmasını istiyorsun. Ama bu nesneler birbirini bilmemeli.

Analoji

YouTube aboneliği gibi. Bir kanala abone oluyorsun. Kanal yeni video yüklediğinde sana bildirim geliyor. Kanal senin kim olduğunu bilmek zorunda değil — sadece abonelerine bildiriyor.

Basit Observer (Callback Tabanlı)

class EventEmitter:
    def __init__(self):
        self._listeners = {}
    
    def on(self, event, callback):
        """Olaya abone ol"""
        if event not in self._listeners:
            self._listeners[event] = []
        self._listeners[event].append(callback)
    
    def off(self, event, callback):
        """Aboneliği iptal et"""
        if event in self._listeners:
            self._listeners[event].remove(callback)
    
    def emit(self, event, *args, **kwargs):
        """Olayı tetikle — tüm abonelere bildir"""
        for callback in self._listeners.get(event, []):
            callback(*args, **kwargs)

# Kullanım
emitter = EventEmitter()

# Abone ol
def on_user_login(user):
    print(f"📊 Analytics: {user} giriş yaptı")

def on_user_login_log(user):
    print(f"📝 Log: {user} giriş yaptı - {__import__('datetime').datetime.now()}")

def on_user_login_welcome(user):
    print(f"👋 Welcome back, {user}!")

emitter.on("user_login", on_user_login)
emitter.on("user_login", on_user_login_log)
emitter.on("user_login", on_user_login_welcome)

# Olay gerçekleştiğinde
emitter.emit("user_login", "Ahmet")

Çıktı:

📊 Analytics: Ahmet giriş yaptı
📝 Log: Ahmet giriş yaptı - 2024-01-15 10:30:00
👋 Welcome back, Ahmet!

Üç farklı modül, birbirini bilmeden, aynı olaya tepki veriyor. Gevşek bağlılık (loose coupling) böyle sağlanır.

Event Bus — Uygulama Çapında Olay Sistemi

class EventBus:
    """Uygulama çapında tek bir event bus (singleton gibi)"""
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._listeners = {}
        return cls._instance
    
    def subscribe(self, event, callback):
        if event not in self._listeners:
            self._listeners[event] = []
        self._listeners[event].append(callback)
        return lambda: self._listeners[event].remove(callback)
    
    def publish(self, event, data=None):
        for callback in self._listeners.get(event, []):
            try:
                callback(data)
            except Exception as e:
                print(f"Event handler error: {e}")

# Kullanım
bus = EventBus()

# Modül A: Sipariş servisi
def handle_order_created(order):
    print(f"📦 Stok güncelleniyor: {order['product']}")

# Modül B: Bildirim servisi
def notify_order_created(order):
    print(f"📧 Müşteriye email gönderiliyor: {order['customer']}")

bus.subscribe("order_created", handle_order_created)
bus.subscribe("order_created", notify_order_created)

# Sipariş oluşturulduğunda
bus.publish("order_created", {
    "product": "Laptop",
    "customer": "ali@example.com",
    "total": 15000
})

Strategy — Algoritma Değiştirme

Problem

Aynı işi farklı şekillerde yapmak istiyorsun. Hangi yöntemi kullanacağın çalışma zamanında belli oluyor.

Analoji

Navigasyon uygulaması gibi. Hedefe arabayla mı, yürüyerek mi, toplu taşımayla mı gideceksin? Aynı hedef, farklı stratejiler. Stratejiyi değiştirdiğinde rotayı yeniden hesaplar.

Python'da Strategy = Fonksiyon Geçirmek

Python'da fonksiyonlar first-class citizendir. Bu yüzden Strategy pattern çok basit:

from typing import Callable

# Stratejiler (sadece fonksiyon)
def bubble_sort(data):
    arr = data.copy()
    n = len(arr)
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

def quick_sort(data):
    if len(data) <= 1:
        return data
    pivot = data[len(data) // 2]
    left = [x for x in data if x < pivot]
    middle = [x for x in data if x == pivot]
    right = [x for x in data if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)

def python_sort(data):
    return sorted(data)

# Context — stratejiyi kullanan sınıf
class Sorter:
    def __init__(self, strategy: Callable = python_sort):
        self.strategy = strategy
    
    def sort(self, data):
        print(f"Strateji: {self.strategy.__name__}")
        return self.strategy(data)

# Kullanım
data = [64, 34, 25, 12, 22, 11, 90]

sorter = Sorter(bubble_sort)
print(sorter.sort(data))

sorter.strategy = quick_sort  # Strateji değiştir
print(sorter.sort(data))

Java'da Strategy için interface + abstract class + concrete class lazım. Python'da bir fonksiyon yeter!

Pratik Örnek: Plugin Sistemi

class TextProcessor:
    def __init__(self):
        self.plugins = []
    
    def register_plugin(self, plugin_func):
        """Yeni plugin ekle"""
        self.plugins.append(plugin_func)
        return plugin_func  # Decorator olarak da kullanılabilir
    
    def process(self, text):
        """Tüm plugin'leri sırayla uygula"""
        result = text
        for plugin in self.plugins:
            result = plugin(result)
        return result

# Plugin'ler — basit fonksiyonlar
processor = TextProcessor()

@processor.register_plugin
def remove_extra_spaces(text):
    return " ".join(text.split())

@processor.register_plugin
def capitalize_sentences(text):
    sentences = text.split(". ")
    return ". ".join(s.capitalize() for s in sentences)

@processor.register_plugin
def add_period(text):
    if text and not text.endswith("."):
        return text + "."
    return text

# Kullanım
raw_text = "  merhaba   dünya.  bugün hava   güzel.  python harika  "
result = processor.process(raw_text)
print(result)
# "Merhaba dünya. Bugün hava güzel. Python harika."

Her plugin basit bir fonksiyon. Yeni plugin eklemek = yeni fonksiyon yazmak. Mevcut koda dokunmaya gerek yok!


Decorator Pattern — Wrapper (Sarmalayıcı)

Problem

Bir nesnenin davranışını, nesneyi değiştirmeden genişletmek istiyorsun.

Analoji

Hediye paketi gibi. Bir kitabı alıyorsun, kurdeleli kağıda sarıyorsun, kartpostal ekliyorsun. Kitap aynı kitap ama "süslenmiş" versiyonu var.

Not: Bu, Python'ın @decorator syntax'ından farklı bir kavram (ama ilişkili). Burada OOP pattern'inden bahsediyoruz.

Python'da Decorator Pattern

class TextComponent:
    """Temel arayüz"""
    def render(self):
        raise NotImplementedError

class PlainText(TextComponent):
    def __init__(self, text):
        self.text = text
    
    def render(self):
        return self.text

class BoldDecorator(TextComponent):
    def __init__(self, component):
        self.component = component
    
    def render(self):
        return f"<b>{self.component.render()}</b>"

class ItalicDecorator(TextComponent):
    def __init__(self, component):
        self.component = component
    
    def render(self):
        return f"<i>{self.component.render()}</i>"

class ColorDecorator(TextComponent):
    def __init__(self, component, color):
        self.component = component
        self.color = color
    
    def render(self):
        return f'<span style="color:{self.color}">{self.component.render()}</span>'

# Kullanım — decorator'ları zincirleme
text = PlainText("Merhaba Dünya")
bold_text = BoldDecorator(text)
colored_bold_text = ColorDecorator(bold_text, "red")

print(text.render())
# Merhaba Dünya

print(bold_text.render())
# <b>Merhaba Dünya</b>

print(colored_bold_text.render())
# <span style="color:red"><b>Merhaba Dünya</b></span>

Python Fonksiyonu ile Daha Basit

def bold(func):
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def italic(func):
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

@bold
@italic
def greet(name):
    return f"Merhaba {name}"

print(greet("Dünya"))
# <b><i>Merhaba Dünya</i></b>

Python'ın @decorator syntax'ı, Decorator pattern'inin çok daha zarif bir uygulaması.


Iterator — Python'da Built-in

Problem

Bir koleksiyonun elemanlarını sırayla dolaşmak istiyorsun, ama koleksiyonun iç yapısını bilmek zorunda değilsin.

Python'da Her Şey Iterator Olabilir

Python'da __iter__ ve __next__ metodlarını implement eden her şey iterable'dır:

class Countdown:
    """Geri sayım iterator'ı"""
    
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        self.current = self.start
        return self
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

# for döngüsü ile kullanım
for num in Countdown(5):
    print(num, end=" ")
# 5 4 3 2 1

# Manuel kullanım
counter = iter(Countdown(3))
print(next(counter))  # 3
print(next(counter))  # 2
print(next(counter))  # 1
# next(counter)  # StopIteration!

Generator ile Daha Kolay

Aslında Python'da iterator yazmak için genellikle sınıfa gerek yok — yield yeter:

def countdown(start):
    """Generator — otomatik iterator"""
    current = start
    while current > 0:
        yield current
        current -= 1

for num in countdown(5):
    print(num, end=" ")
# 5 4 3 2 1

def fibonacci(limit):
    """Fibonacci sayıları"""
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

print(list(fibonacci(100)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Python'da Iterator pattern dile gömülü. Ayrı bir pattern olarak düşünmeye bile gerek yok — for döngüsü, list comprehension, map, filter hepsi iterator protocol kullanır.


Context Manager — Kaynak Yönetimi

Problem

Bir kaynak (dosya, veritabanı bağlantısı, lock) açıyorsun, kullanıyorsun, kapatman gerekiyor. Exception olsa bile kapatılmalı.

Analoji

Otel odası gibi. Check-in yaparsın, odayı kullanırsın, check-out yaparsın. Erken ayrılsan da, sorun çıksa da, oda temizlenmeli.

__enter__ ve __exit__ ile

class DatabaseConnection:
    def __init__(self, host):
        self.host = host
        self.connected = False
    
    def __enter__(self):
        print(f"🔗 {self.host} bağlantısı açıldı")
        self.connected = True
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"🔌 {self.host} bağlantısı kapatıldı")
        self.connected = False
        if exc_type:
            print(f"⚠️ Hata oluştu: {exc_val}")
        return False  # Exception'ı yeniden fırlat
    
    def query(self, sql):
        if not self.connected:
            raise RuntimeError("Bağlantı yok!")
        print(f"📊 Sorgu: {sql}")
        return [{"id": 1, "name": "test"}]

# with bloğu ile kullanım
with DatabaseConnection("localhost") as db:
    result = db.query("SELECT * FROM users")
    print(result)
# Blok bitince otomatik kapanır

# Exception olsa bile kapanır!
try:
    with DatabaseConnection("localhost") as db:
        db.query("SELECT * FROM users")
        raise ValueError("Bir hata!")
except ValueError:
    pass
# "🔌 localhost bağlantısı kapatıldı" yine yazdırılır

contextmanager Decorator ile Daha Kolay

from contextlib import contextmanager
import time

@contextmanager
def timer(label="Operation"):
    """İşlem süresini ölçen context manager"""
    start = time.perf_counter()
    print(f"⏱️ {label} başladı...")
    yield  # Blok burada çalışır
    elapsed = time.perf_counter() - start
    print(f"⏱️ {label} tamamlandı: {elapsed:.4f}s")

# Kullanım
with timer("Veri işleme"):
    data = [i ** 2 for i in range(1_000_000)]
    total = sum(data)

# ⏱️ Veri işleme başladı...
# ⏱️ Veri işleme tamamlandı: 0.1234s

@contextmanager
def temporary_directory():
    """Geçici dizin oluştur, işlem bitince sil"""
    import tempfile
    import shutil
    
    dirpath = tempfile.mkdtemp()
    print(f"📁 Geçici dizin: {dirpath}")
    try:
        yield dirpath
    finally:
        shutil.rmtree(dirpath)
        print(f"🗑️ Geçici dizin silindi")

with temporary_directory() as tmpdir:
    # tmpdir ile çalış, blok bitince otomatik silinir
    pass

Python'da Pattern'ler Neden Daha Basit?

Java veya C++ ile karşılaştırıldığında, Python'da birçok pattern çok daha basit implement edilir. Nedeni:

1. First-Class Functions

Fonksiyonları değişken gibi geçirebilirsin. Strategy pattern'i bir fonksiyon parametresi ile çözülür.

2. Duck Typing

"Eğer ördek gibi yürüyorsa ve ördek gibi vaklıyorsa, o bir ördektir." Interface tanımlamana gerek yok.

# Java'da: interface gerekli
# Python'da: uygun metodları implement et, yeter

class FileLogger:
    def log(self, msg):
        print(f"[FILE] {msg}")

class ConsoleLogger:
    def log(self, msg):
        print(f"[CONSOLE] {msg}")

def process(data, logger):  # Herhangi bir logger olabilir
    logger.log(f"Processing {data}")

process("test", FileLogger())
process("test", ConsoleLogger())

3. Decorator Syntax

@decorator ile Decorator, Observer, ve benzeri pattern'ler çok zarif uygulanır.

4. Generator ve Context Manager

Iterator ve RAII (Resource Acquisition Is Initialization) pattern'leri dile gömülü.

5. Modüller = Singleton

Bir Python modülü doğal bir singleton'dır.

# Java'da Singleton: 20+ satır kod
# Python'da Singleton: config.py dosyası

# Java'da Strategy: interface + abstract class + concrete classes
# Python'da Strategy: fonksiyon parametresi

# Java'da Iterator: Iterator interface implement et
# Python'da Iterator: yield

⚠️ Over-Engineering Uyarısı

Design pattern bilmek güzel ama her yere pattern koymaya çalışmak, en tehlikeli anti-pattern'dir: Over-Engineering.

# ❌ Over-engineered: 2 sayıyı toplamak için Strategy + Factory + Observer
class AdditionStrategy:
    pass

class NumberFactory:
    pass

class ResultObserver:
    pass

# ... 100 satır kod ...

# ✅ Gerçeklik
def add(a, b):
    return a + b

Ne Zaman Pattern Kullanmalısın?

  1. Problem tekrar ediyorsa: Aynı yapısal sorunu 3. kez çözüyorsan, pattern düşün

  2. Değişim bekliyorsan: "Bu kısmı ileride değiştirmem gerekecek" → Strategy

  3. Gevşek bağlılık istiyorsan: Modüller birbirini bilmemeli → Observer

  4. Doğal geliyorsa: Kodu okurken "bu bir Factory" diyorsan, zaten doğal yoldan pattern uygulamışsın

Ne Zaman Pattern Kullanmamalısın?

  1. Basit sorun için: 10 satır kod yetiyorsa, pattern ekleme

  2. Gösteriş için: "Design pattern biliyorum" demek için değil

  3. İlk seferde: İlk versiyonu basit yaz, gerekirse refactor et

  4. Anlam katmıyorsa: Pattern kodu daha karmaşık yapıyorsa, bir yanlışlık var

"Make it work, make it right, make it fast." — Kent Beck. Önce çalışsın, sonra güzelleştir.


Pratik: Event Bus ve Plugin Sistemi

Öğrendiklerimizi birleştirelim. Observer (Event Bus) ve Strategy (Plugin) pattern'lerini kullanan bir mini framework yazalım:

from typing import Callable, Any
from dataclasses import dataclass, field
from datetime import datetime

# === Event Bus (Observer Pattern) ===
class EventBus:
    def __init__(self):
        self._handlers: dict[str, list[Callable]] = {}
    
    def on(self, event: str, handler: Callable):
        self._handlers.setdefault(event, []).append(handler)
    
    def emit(self, event: str, data: Any = None):
        for handler in self._handlers.get(event, []):
            handler(data)

# === Plugin System (Strategy Pattern) ===
class PluginManager:
    def __init__(self):
        self._plugins: dict[str, Callable] = {}
    
    def register(self, name: str, plugin: Callable):
        self._plugins[name] = plugin
    
    def execute(self, name: str, *args, **kwargs):
        if name not in self._plugins:
            raise ValueError(f"Plugin bulunamadı: {name}")
        return self._plugins[name](*args, **kwargs)
    
    def list_plugins(self):
        return list(self._plugins.keys())

# === Uygulama ===
@dataclass
class Task:
    title: str
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    completed: bool = False

class TaskApp:
    def __init__(self):
        self.tasks: list[Task] = []
        self.events = EventBus()
        self.plugins = PluginManager()
    
    def add_task(self, title: str):
        task = Task(title=title)
        self.tasks.append(task)
        self.events.emit("task_added", task)
        return task
    
    def complete_task(self, index: int):
        task = self.tasks[index]
        task.completed = True
        self.events.emit("task_completed", task)
    
    def export(self, format_name: str):
        return self.plugins.execute(format_name, self.tasks)

# === Kurulum ===
app = TaskApp()

# Event listener'ları kaydet (Observer)
app.events.on("task_added", lambda t: print(f"✅ Eklendi: {t.title}"))
app.events.on("task_completed", lambda t: print(f"🎉 Tamamlandı: {t.title}"))

# Export plugin'lerini kaydet (Strategy)
app.plugins.register("json", lambda tasks: [
    {"title": t.title, "done": t.completed} for t in tasks
])
app.plugins.register("csv", lambda tasks: "\n".join(
    f"{t.title},{'done' if t.completed else 'pending'}" for t in tasks
))

# === Kullanım ===
app.add_task("Python öğren")
app.add_task("Test yaz")
app.complete_task(0)

print("\n--- JSON Export ---")
print(app.export("json"))

print("\n--- CSV Export ---")
print(app.export("csv"))

Çıktı:

✅ Eklendi: Python öğren
✅ Eklendi: Test yaz
🎉 Tamamlandı: Python öğren

--- JSON Export ---
[{'title': 'Python öğren', 'done': True}, {'title': 'Test yaz', 'done': False}]

--- CSV Export ---
Python öğren,done
Test yaz,pending

Özet

  • Design pattern'ler tekrar eden sorunlara kanıtlanmış çözümlerdir — ortak bir dil sağlar

  • Singleton: Tek instance — Python'da modül zaten singleton'dır

  • Factory: Nesne oluşturmayı soyutlar — dictionary + class referans ile basitçe yapılır

  • Observer: Olay sistemi — callback fonksiyonlarla loose coupling sağlar

  • Strategy: Algoritma değiştirme — Python'da fonksiyon geçirmekle çözülür, sınıfa gerek yok

  • Python'da pattern'ler daha basittir çünkü first-class functions, duck typing ve generator gibi dil özellikleri birçok pattern'i gereksiz kılar

  • Over-engineering'den kaçın: Pattern'i sorun gerektirdiğinde kullan, gösteriş için değil