CodeBlanks Logo Icon CodeBlanks

Meydan okuma - Tasarım desenleri

PHP kursu · İleri seviye · Ders 90/92

Factory, Strategy ve Observer'la esnek bir bildirim sistemi yazarsın.

Bildirim sistemi: üç desen

Bu derste esnek bir bildirim sistemi yazarsın. Factory deseni nesne oluşturmayı NotificationFactory::make($type) yöntiminde toplar; istemci somut sınıfı bilmeden kanal ister. Strategy deseni her kanalı aynı NotificationChannel arayüzüyle sunar; EmailChannel ve SmsChannel birbirinin yerine kullanılır.

Observer deseni olay yayını için NotificationObserver arayüzü ve attach() yöntemiyle çalışır. NotificationService::notify() bir kanal üretir, mesajı gönderir ve tüm gözlemcilere sonucu bildirir. Böylece kanal değişimi ya da log ekleme tek yerden yönetilir.

Observer deseni

Observer deseni bir konu (subject) ve ona abone olan dinleyicilerden oluşur. NotificationService konu; LogObserver dinleyicidir. attach(NotificationObserver $o) dinleyiciyi bir diziye ekler. notify() çağrıldığında her dinleyicinin onSent(string $log) yöntemi çalışır.

Böylece bildirim gönderildiğinde log, analitik veya e-posta gibi yan etkileri NotificationService'i değiştirmeden ekleyebilirsin. Yeni bir dinleyici yazıp attach() ile eklemen yeterli. Açık-kapalı ilkesi uygulanmış olur: sınıf değişikliğe kapalı, genişlemeye açıktır.

Uygulama NotificationFactory ve NotificationService yaz

Başlangıç kodunda arayüzler ve kanal sınıfları (EmailChannel, SmsChannel, LogObserver) hazır. Sen NotificationFactory ve NotificationService sınıflarını yazacaksın.

NotificationFactory::make(string $type): NotificationChannel statik yöntemi match ile "email" için EmailChannel, "sms" için SmsChannel döndürsün; varsayılan dal InvalidArgumentException fırlatsın. NotificationService sınıfı özel array $observers = [] tutsun; attach(NotificationObserver $o): void dinleyici eklesin; notify(string $type, string $message): string önce NotificationFactory::make ile kanal üretip send() çağırsın, sonra her gözlemcinin onSent($result) yöntemini çalıştırıp $result döndürsün.

Örnek çözümü göster
NotificationChannel { return match($type) { "email" => new EmailChannel(), "sms" => new SmsChannel(), default => throw new InvalidArgumentException(...) }; } } yaz. class NotificationService { private array $observers = []; public function attach(NotificationObserver $o): void { ... } public function notify(string $type, string $message): string { $channel = NotificationFactory::make($type); $result = $channel->send($message); foreach ($this->observers as $o) { $o->onSent($result); } return $result; } } yaz. Sonra demoyu çalıştır.
Örnek çözüm:
<?php
// Ürün API'si + güvenlik + bildirimler: NotificationFactory ve NotificationService yaz

class ProductRepository {
    private PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->pdo->exec("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER)");
        $stmt = $this->pdo->prepare("INSERT INTO products (name, price) VALUES (?, ?)");
        $stmt->execute(["Kale", 5]);
        $stmt->execute(["Defter", 15]);
    }

    public function all(): array {
        $rows = $this->pdo->query("SELECT id, name, price FROM products ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
        return array_map(fn($r) => [
            "id" => (int)$r["id"],
            "name" => $r["name"],
            "price" => (int)$r["price"]
        ], $rows);
    }
}

function handleGet(PDO $pdo): string {
    $repo = new ProductRepository($pdo);
    return json_encode(["products" => $repo->all()], JSON_UNESCAPED_UNICODE);
}

class AuthService {
    private array $users = [];
    private string $csrf = "";

    public function register(string $user, string $pass): void {
        $this->users[$user] = password_hash($pass, PASSWORD_DEFAULT);
    }

    public function login(string $user, string $pass): bool {
        if (!isset($this->users[$user])) return false;
        return password_verify($pass, $this->users[$user]);
    }

    public function csrfToken(): string {
        if ($this->csrf === "") {
            $this->csrf = bin2hex(random_bytes(8));
        }
        return $this->csrf;
    }

    public function applyHeaders(): void {
        header("X-Frame-Options: DENY");
        header("X-Content-Type-Options: nosniff");
    }
}

interface NotificationChannel {
    public function send(string $message): string;
}

class EmailChannel implements NotificationChannel {
    public function send(string $message): string {
        return "E-posta: " . $message;
    }
}

class SmsChannel implements NotificationChannel {
    public function send(string $message): string {
        return "SMS: " . $message;
    }
}

interface NotificationObserver {
    public function onSent(string $log): void;
}

class LogObserver implements NotificationObserver {
    public array $logs = [];
    public function onSent(string $log): void {
        $this->logs[] = $log;
    }
}

class NotificationFactory {
    public static function make(string $type): NotificationChannel {
        return match ($type) {
            "email" => new EmailChannel(),
            "sms" => new SmsChannel(),
            default => throw new InvalidArgumentException("Bilinmeyen kanal"),
        };
    }
}

class NotificationService {
    private array $observers = [];

    public function attach(NotificationObserver $o): void {
        $this->observers[] = $o;
    }

    public function notify(string $type, string $message): string {
        $channel = NotificationFactory::make($type);
        $result = $channel->send($message);
        foreach ($this->observers as $o) {
            $o->onSent($result);
        }
        return $result;
    }
}

// Demo:
$svc = new NotificationService();
$logger = new LogObserver();
$svc->attach($logger);
echo $svc->notify("email", "Sipariş alındı") . "\n";
echo $svc->notify("sms", "Kargo yola çıktı") . "\n";
echo "Log sayısı: " . count($logger->logs) . "\n";

Kendini test et

  1. Hangi tasarım deseni nesne oluşturmayı merkezi bir yere toplar?

    • Observer
    • Strategy
    • Singleton
    • Factory
    Cevabı göster

    Factory — Doğru. Factory deseni nesne oluşturmayı tek bir yere toplar; istemci hangi sınıfın örneklendiğini bilmez.

  2. NotificationChannel arayüzünü tamamla

    interface NotificationChannel {
        public function ____(string $message): string;
    }
    Cevabı göster
    interface NotificationChannel {
        public function send(string $message): string;
    }
  3. Tasarım desenlerini eşleştir

    Factory
    Nesne oluşturmayı toplar
    Strategy
    Davranışı çalışma anında seçer
    Observer
    Olay aboneliği bildirir
    Interface
    Sözleşme tanımlar

İleri seviye bölümündeki tüm dersler