froquiz-logoFroquiz
  • Anasayfa
  • Quizler
  • Blog
  • Hakkımızda
Froquiz

Yazılım mühendisleri için en kapsamlı quiz platformu. 5000+ soru ile kendinizi test edin ve kariyerinizi geliştirin.

LinkedIn

Platform

  • Quizlere Başla
  • Konular
  • Blog
  • Profilim
  • Giriş Yap

Hakkında

  • Biz Kimiz?
  • İletişim

Yasal

  • Gizlilik Politikası
  • Kullanım Koşulları

© 2026 Froquiz. Tüm hakları saklıdır.Teknoloji tutkuyla yapıldı
Blog/Design Patterns: Her Geliştiricinin Bilmesi Gereken 7 Temel Pattern

Design Patterns: Her Geliştiricinin Bilmesi Gereken 7 Temel Pattern

Design pattern'lar tekrar eden yazılım problemlerine denenmiş çözümlerdir. Singleton'dan Observer'a, Factory'den Strategy'ye — en sık karşılaşılan 7 pattern'ı gerçek kod örnekleriyle anlıyoruz.

Yusuf SeyitoğluYusuf Seyitoğlu|
5 Mart 20265 Mar
|
68 görüntülenme
|
11 dk okuma

Design Patterns: Her Geliştiricinin Bilmesi Gereken 7 Temel Pattern

1994'te dört yazar — Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides — "Gang of Four" olarak bilinen kitabı yayınladı: Design Patterns. 30 yıl sonra hâlâ her iş görüşmesinde soruluyor, her büyük kod tabanında izleri var.

Ama design pattern'lar öğrenilirken yapılan klasik hata: kalıpları ezberlemek, ne zaman kullanılacağını anlamamak. Bu yazı her pattern'ı bir problemi çözen araç olarak ele alıyor.

Design Pattern Nedir?

Design pattern, yazılım tasarımında sık karşılaşılan problemlere tekrar kullanılabilir çözüm şablonlarıdır. Kod değil, fikir. Direkt kopyalanmaz, probleme uyarlanır.

Üç kategoride incelenir: Creational (nesne oluşturma), Structural (yapısal ilişkiler), Behavioral (davranış ve iletişim).

1. Singleton — Creational

Problem: Bir sınıftan yalnızca bir örnek oluşturulmalı ve bu örneğe global erişim sağlanmalı.

Gerçek kullanım: Database connection pool, logger, uygulama konfigürasyonu.

class DatabaseConnection { private static instance: DatabaseConnection; private connection: Connection; private constructor() { this.connection = createConnection({ host: process.env.DB_HOST, database: process.env.DB_NAME }); } static getInstance(): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(); } return DatabaseConnection.instance; } query(sql: string) { return this.connection.execute(sql); } } // Her yerden aynı instance const db1 = DatabaseConnection.getInstance(); const db2 = DatabaseConnection.getInstance(); console.log(db1 === db2); // true

Dikkat: Singleton global state yaratır ve test etmeyi zorlaştırır. Dependency injection'ı tercih edin, Singleton'ı zorunlu olmadıkça kullanmayın.

2. Factory — Creational

Problem: Nesne oluşturma mantığını, nesneyi kullanan koddan ayırmak istiyorsunuz. Hangi sınıfın örnekleneceği runtime'da belirleniyor.

Gerçek kullanım: Farklı ödeme sağlayıcıları, farklı bildirim kanalları, farklı depolama sağlayıcıları.

interface PaymentProvider { charge(amount: number): Promise<PaymentResult>; refund(transactionId: string): Promise<void>; } class StripeProvider implements PaymentProvider { async charge(amount: number) { /* Stripe API */ } async refund(transactionId: string) { /* Stripe API */ } } class PayPalProvider implements PaymentProvider { async charge(amount: number) { /* PayPal API */ } async refund(transactionId: string) { /* PayPal API */ } } // Factory class PaymentFactory { static create(provider: string): PaymentProvider { switch (provider) { case "stripe": return new StripeProvider(); case "paypal": return new PayPalProvider(); default: throw new Error(`Unknown provider: ${provider}`); } } } // Kullanım: hangi provider olduğunu bilmeden çalışır const payment = PaymentFactory.create(process.env.PAYMENT_PROVIDER); await payment.charge(99.99);

Yeni bir ödeme sağlayıcısı eklendi mi? Sadece yeni bir sınıf yazıp factory'ye bir case ekleyin. Mevcut kod değişmez.

3. Observer — Behavioral

Problem: Bir nesnenin durumu değiştiğinde, bağımlı nesnelerin otomatik olarak haberdar edilmesi gerekiyor.

Gerçek kullanım: Event sistemleri, state management (Redux), DOM event listener'lar, WebSocket mesajları.

interface Observer { update(event: string, data: any): void; } class EventEmitter { private listeners: Map<string, Observer[]> = new Map(); subscribe(event: string, observer: Observer): void { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event)!.push(observer); } emit(event: string, data: any): void { const observers = this.listeners.get(event) || []; observers.forEach(observer => observer.update(event, data)); } } // Kullanım const orderService = new EventEmitter(); // Farklı servisler aynı event'i dinliyor orderService.subscribe("order.placed", { update: (event, order) => emailService.sendConfirmation(order) }); orderService.subscribe("order.placed", { update: (event, order) => inventoryService.reserveItems(order) }); orderService.subscribe("order.placed", { update: (event, order) => analyticsService.track(event, order) }); // Sipariş verildi — tüm observer'lar otomatik tetiklendi orderService.emit("order.placed", { id: "123", items: [...] });

Observer pattern, sistemin bileşenlerini gevşek bağlar. Email servisi, sipariş servisini bilmek zorunda değil. Sipariş servisi, kimin dinlediğini bilmek zorunda değil.

4. Strategy — Behavioral

Problem: Bir algoritmanın birden fazla versiyonu var ve runtime'da hangisinin kullanılacağı seçilebilmeli.

Gerçek kullanım: Sıralama algoritmaları, ödeme stratejileri, sıkıştırma algoritmaları, authentication stratejileri.

interface SortStrategy { sort(data: number[]): number[]; } class QuickSort implements SortStrategy { sort(data: number[]): number[] { // QuickSort implementasyonu — büyük veri setleri için hızlı return [...data].sort((a, b) => a - b); } } class BubbleSort implements SortStrategy { sort(data: number[]): number[] { // BubbleSort implementasyonu — küçük veri setleri için const arr = [...data]; for (let i = 0; i < arr.length; i++) { for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; } } return arr; } } class Sorter { constructor(private strategy: SortStrategy) {} setStrategy(strategy: SortStrategy) { this.strategy = strategy; } sort(data: number[]): number[] { return this.strategy.sort(data); } } // Runtime'da strateji değiştirilebilir const sorter = new Sorter(new QuickSort()); sorter.sort([3, 1, 4, 1, 5, 9]); sorter.setStrategy(new BubbleSort()); sorter.sort([3, 1, 4]);

5. Decorator — Structural

Problem: Bir nesneye, mevcut sınıfı değiştirmeden yeni davranış eklemek istiyorsunuz.

Gerçek kullanım: Middleware'ler, logging, caching, authentication katmanları.

interface DataService { getData(id: string): Promise<any>; } class RealDataService implements DataService { async getData(id: string) { return db.query(`SELECT * FROM items WHERE id = ?`, id); } } // Cache decorator class CachedDataService implements DataService { constructor(private service: DataService, private cache: Redis) {} async getData(id: string) { const cached = await this.cache.get(`item:${id}`); if (cached) return JSON.parse(cached); const data = await this.service.getData(id); await this.cache.setex(`item:${id}`, 3600, JSON.stringify(data)); return data; } } // Logging decorator class LoggedDataService implements DataService { constructor(private service: DataService) {} async getData(id: string) { console.log(`Fetching item: ${id}`); const start = Date.now(); const data = await this.service.getData(id); console.log(`Fetched in ${Date.now() - start}ms`); return data; } } // Decorator'ları birleştir const service = new LoggedDataService( new CachedDataService( new RealDataService(), redis ) );

Her decorator tek bir sorumluluk üstleniyor. RealDataService veritabanından okumayı biliyor, cache'lemeyi değil. CachedDataService cache'lemeyi biliyor, veriyi nereden çekeceğini değil.

6. Repository — Structural

Problem: Veri erişim mantığını iş mantığından ayırmak istiyorsunuz. Veritabanı implementasyonu değişse bile iş mantığı etkilenmemeli.

Gerçek kullanım: Neredeyse her kurumsal uygulama. Domain Driven Design'ın temel taşı.

interface UserRepository { findById(id: string): Promise<User | null>; findByEmail(email: string): Promise<User | null>; save(user: User): Promise<User>; delete(id: string): Promise<void>; } // PostgreSQL implementasyonu class PostgresUserRepository implements UserRepository { async findById(id: string) { const row = await db.query("SELECT * FROM users WHERE id = $1", [id]); return row ? mapToUser(row) : null; } // diğer metodlar... } // Test için in-memory implementasyonu class InMemoryUserRepository implements UserRepository { private users: Map<string, User> = new Map(); async findById(id: string) { return this.users.get(id) || null; } // diğer metodlar... } // Service sadece interface'i biliyor class UserService { constructor(private userRepo: UserRepository) {} async getUserProfile(id: string) { const user = await this.userRepo.findById(id); if (!user) throw new Error("User not found"); return user; } } // Production'da PostgreSQL const service = new UserService(new PostgresUserRepository()); // Test'te in-memory const testService = new UserService(new InMemoryUserRepository());

7. Builder — Creational

Problem: Karmaşık bir nesneyi adım adım oluşturmak istiyorsunuz. Constructor parametreleri çok fazla ya da oluşturma süreci çok adımlı.

Gerçek kullanım: Query builder'lar, test fixture'ları, karmaşık konfigürasyon nesneleri.

class QueryBuilder { private table: string = ""; private conditions: string[] = []; private columns: string[] = ["*"]; private limitValue?: number; private orderByColumn?: string; from(table: string): this { this.table = table; return this; } select(...columns: string[]): this { this.columns = columns; return this; } where(condition: string): this { this.conditions.push(condition); return this; } limit(n: number): this { this.limitValue = n; return this; } orderBy(column: string): this { this.orderByColumn = column; return this; } build(): string { let query = `SELECT ${this.columns.join(", ")} FROM ${this.table}`; if (this.conditions.length > 0) { query += ` WHERE ${this.conditions.join(" AND ")}`; } if (this.orderByColumn) query += ` ORDER BY ${this.orderByColumn}`; if (this.limitValue) query += ` LIMIT ${this.limitValue}`; return query; } } // Okunabilir, zincirleme API const query = new QueryBuilder() .from("users") .select("id", "email", "name") .where("active = true") .where("age > 18") .orderBy("created_at") .limit(10) .build();

Pattern'ları Ne Zaman Kullanmalı?

Design pattern'lar çözüm değil, araçtır. Her araç gibi yanlış yerde kullanıldığında zarar verir.

Pattern kullanmadan önce sormak gereken sorular: Hangi problemi çözüyorum? Bu pattern olmadan bu problemi çözebilir miyim? Bu complexity'yi eklemek gerçekten değer mi?

"Pattern kullandım" diye karmaşıklık eklemek yerine, "bu pattern bu problemi temiz çözüyor" diyebileceğiniz durumları arayın. En iyi kod, okuyucunun pattern ismini bilmesine gerek kalmadan anlayabileceği koddur.

Yazar Hakkında

Yusuf Seyitoğlu

Yusuf Seyitoğlu

View Profile →

Diğer Yazılar

  • CI/CD Pipeline Nedir? Kod Yazmaktan Production'a Giden Otomatik Yol

    6 Mar

  • TypeScript Nedir? JavaScript Geliştiricisinin Tip Sistemine Gerçek Giriş

    6 Mar

  • Kubernetes Nedir? Container Orkestrasyon'u Sıfırdan Anlamak

    6 Mar

← Tüm Bloglar