- Strategy deseni hangi sorunu çözer?
- Yapının parçaları: bağlam, strateji arayüzü, somut stratejiler
- Adım adım uygulama: indirim hesaplama örneği
- Fonksiyonel yaklaşım: her zaman sınıf gerekmez
- Strateji seçimini yönetmek: eşleme tablosu ve çalışma zamanı değişimi
- Strategy, State ve Template Method nasıl ayrılır?
- Sık yapılan hatalar
- Bu beceriyi Mindset programında nerede çalışırsınız?
- Sık sorulan sorular
Strategy (Strateji) Deseni: Değişen Algoritmayı Ayırmak
Uzun if-else zincirlerini strateji desenine adım adım dönüştürün; kod örneği, State ve Template Method ile farkı ve sık yapılan hatalar.
Strategy deseni hangi sorunu çözer?
Aynı işi farklı yollarla yapabilen bir kodunuz olduğunu düşünün: kargo ücretini hesaplamak, bir listeyi sıralamak, indirim uygulamak, ödemeyi farklı sağlayıcılardan almak. Başlangıçta bir if bloğu yeterli görünür. Zamanla her yeni kural bloğa bir dal ekler, dallar birbirinin değişkenlerine bağımlı hale gelir ve fonksiyon her değişiklikte yeniden test edilmesi gereken bir yığına dönüşür.
Strategy (strateji) deseni, değişen algoritmayı kendi arayüzü arkasına alır. Her yöntem ayrı bir nesne ya da fonksiyondur; bunları kullanan sınıf (bağlam) hangi yöntemin çalıştığını bilmez, yalnızca ortak sözleşmeyi çağırır. Yöntemi seçmek de bağlamın dışına taşınır. Sonuçta yeni bir kural eklemek mevcut kodu değiştirmeden yeni bir sınıf yazmak anlamına gelir; bu, açık/kapalı prensibinin somut bir örneğidir.
Desenin işaret ettiği koku, aynı kavram etrafında dönen uzun koşul zincirleridir. Koşullar sabit ve az ise desen gereksizdir; kurallar sık değişiyor, yeni varyantlar bekleniyorsa değer katar.
Yapının parçaları: bağlam, strateji arayüzü, somut stratejiler
Desen birkaç parçadan oluşur ve her birinin rolü net olmalıdır.
- Strateji arayüzü: Tüm yöntemlerin uyduğu tek sözleşme. Örneğin bir sepet tutarı alıp indirimli tutarı döndüren tek bir metot.
- Somut stratejiler: Her biri bir yöntemi gerçekler: yüzde indirimi, sabit tutar indirimi, ilk alışverişe özel indirim.
- Bağlam (context): Stratejiyi saklayan ve iş gerektiğinde çağıran sınıf. Hangi stratejinin içinde olduğunu bilmez, yalnızca arayüzü kullanır.
Önemli ayrım şudur: strateji seçimi bağlamın görevi değildir. Seçimi yapan, çoğunlukla uygulamanın kurulum noktası, bir fabrika ya da kullanıcı girdisini işleyen katmandır. Seçim bağlamın içine gömülürse if zinciri yalnızca yer değiştirmiş olur.
Adım adım uygulama: indirim hesaplama örneği
Örneğin bir e-ticaret sepetinde indirimler başlangıçta tek bir fonksiyonda toplanmış olsun:
function indirimUygula(sepet: Sepet, tur: string): number { if (tur === "yuzde") { return sepet.tutar * sepet.kalanOran; } if (tur === "sabit") { return sepet.tutar - sepet.indirimTutari; } if (tur === "ilkAlisveris") { /* başka bir mantık */ } return sepet.tutar; }
Bu fonksiyonu desene dönüştürmek için şu adımları izlersiniz.
Birinci adım, ortak sözleşmeyi çıkarmaktır:
interface IndirimStratejisi { uygula(sepet: Sepet): number; }
İkinci adım, her dalı kendi sınıfına taşımaktır. Davranış değişmez, yalnızca yer değiştirir:
class YuzdeIndirimi implements IndirimStratejisi { uygula(sepet: Sepet) { return sepet.tutar * sepet.kalanOran; } } class SabitIndirim implements IndirimStratejisi { uygula(sepet: Sepet) { return sepet.tutar - sepet.indirimTutari; } }
Üçüncü adım, bağlamı yalnızca arayüze bağlamaktır:
class SepetHesaplayici { constructor(private strateji: IndirimStratejisi) {} toplam(sepet: Sepet) { return this.strateji.uygula(sepet); } }
Dördüncü adım, seçimi dışarı almaktır: kampanya türüne bakıp doğru nesneyi veren küçük bir eşleme ya da fabrika yazılır. Böylece SepetHesaplayici hiçbir kampanya adı bilmez.
Fonksiyonel yaklaşım: her zaman sınıf gerekmez
Strateji arayüzü tek bir metottan oluşuyorsa, birçok dilde bunu doğrudan bir fonksiyon olarak geçirmek yeterlidir. Bağlam, bir fonksiyon alır ve onu çağırır:
type Indirim = (sepet: Sepet) => number; const yuzde: Indirim = (s) => s.tutar * s.kalanOran; function toplam(sepet: Sepet, indirim: Indirim) { return indirim(sepet); }
Bu biçim tören azaltır: sınıf hiyerarşisi, arayüz tanımı ve yapıcı yoktur. Sıralama fonksiyonlarına verdiğiniz karşılaştırıcı aslında bir stratejidir; çoğu geliştirici bu deseni farkında olmadan her gün kullanır.
Sınıf tabanlı biçimi seçmenizi gerektiren durumlar da vardır: strateji kendi durumunu taşıyorsa (yapılandırma, önbellek), birden fazla metodu varsa ya da stratejinin dışarıdan bağımlılığı varsa ve bunların enjekte edilmesi gerekiyorsa sınıf daha okunaklıdır.
Strateji seçimini yönetmek: eşleme tablosu ve çalışma zamanı değişimi
Stratejileri bir anahtara göre eşleyen küçük bir tablo, kod içindeki if zincirinin yerine geçer:
const indirimler: Record<string, IndirimStratejisi> = { yuzde: new YuzdeIndirimi(), sabit: new SabitIndirim(), }; const strateji = indirimler[kampanya.tur] ?? new IndirimYok();
Bilinmeyen anahtar için hata fırlatmak mı, güvenli bir varsayılan (hiç indirim yok) döndürmek mi daha uygun olduğu iş kuralına bağlıdır; ama bu karar tek yerde, açıkça verilmelidir.
Desenin bir avantajı da çalışma zamanında değişim imkânıdır. Bir kullanıcı ödeme yöntemini değiştirdiğinde ya da bir özellik bayrağı açıldığında bağlama yeni bir strateji verirsiniz; bağlamın kodu aynı kalır. Strateji sık değişiyorsa bağlamın alanını sabit bırakmak yerine her çağrıda parametre olarak geçirmek de seçeneklerden biridir.
Strategy, State ve Template Method nasıl ayrılır?
Bu desenlerin sınıf diyagramı benzerdir; ayrım niyette yatar.
Ölçüt: Kim seçer? Strategy: Seçimi dışarıdaki bir istemci yapar ve çoğunlukla sabit kalır. State: Geçişleri nesnenin kendisi yönetir; bir durumdan diğerine kuralla geçilir.
Ölçüt: Ne değişir? Strategy: Tek bir işin nasıl yapıldığı değişir. State: Nesnenin davranışının bütünü duruma göre değişir. Template Method: İş akışının iskeleti sabit, belirli adımlar alt sınıfta yazılır.
Ölçüt: Mekanizma nedir? Strategy: Bileşim, yani bağlam stratejiyi bir alan olarak taşır. Template Method: Kalıtım, yani alt sınıf üst sınıfın adımlarını doldurur.
Bir sipariş "hazırlanıyor, kargoda, teslim edildi" durumlarında yön değiştiriyorsa bu bir durum makinesidir. Kargo ücretinin hesaplanma biçimi değişiyorsa bu bir stratejidir. Aynı sistemde bu desenler bir arada da bulunabilir.
Sık yapılan hatalar
- Tek dallı koşul için desen kurmak: Birkaç sabit durum için sınıf hiyerarşisi yazmak, düz bir if'ten daha okunaksızdır. Önce koşulun gerçekten büyüyüp büyümeyeceğini sorun.
- Seçimi bağlamın içine saklamak: Bağlam kendi stratejisini new ile yaratırsa bağımlılık geri döner ve test sırasında stratejiyi değiştiremezsiniz.
- Strateji arayüzünü şişirmek: Her yöntem farklı parametre isterse arayüz büyür. Ortak bilgiyi tek bir bağlam nesnesinde toplayın ya da stratejiyi yapıcıda ihtiyaç duyduğu bilgiyle kurun.
- Stratejiler arasında paylaşılan durum bırakmak: Strateji nesneleri yeniden kullanılıyorsa üzerlerinde değişebilir alan tutmayın; eşzamanlı çağrılarda hata kaynağı olur.
- Her stratejiyi ayrı test etmemek: Desenin kazancı, her yöntemin bağımsız birim testiyle sınanabilmesidir. Bu avantajı kullanmayan bir uygulama, karmaşıklığı artırıp getiri sağlamaz.
Bu beceriyi Mindset programında nerede çalışırsınız?
Yazılım Mimarisi ve Tasarım Desenleri programında strateji gibi davranışsal desenler ayrı bir modülün konusudur ve SOLID ile tasarım prensipleri modülünde işlenen açık/kapalı fikrinin üzerine kurulur. Program, iş kurallarının sık değiştiği kurumsal sistemlerde çalışan geliştiriciler ve kod incelemesinde tasarım kararlarını gerekçelendirmek isteyen teknik liderler için uygundur.
Ekip düzeyinde kazanç, fiyatlandırma, bildirim, ödeme gibi değişken kuralların hangi noktada desene çevrilmesi gerektiği konusunda ortak bir ölçüt oluşturmaktır; böylece desen reflekse değil ihtiyaca göre seçilir.
İlgili Mindset programı
Yazılım Mimarisi ve Tasarım Desenleri
Sık sorulan sorular
Strateji deseni ne zaman if-else zincirinden daha iyidir?
Koşul zinciri aynı kavram etrafında büyüyorsa, yeni varyantlar bekleniyorsa ya da her dalın bağımsız test edilmesi gerekiyorsa strateji daha uygundur. Dal sayısı az ve sabitse düz koşul daha okunaklıdır. Ölçüt, yeni bir kural geldiğinde mevcut kodu değiştirmeniz mi yoksa yalnızca yeni bir parça eklemeniz mi gerektiğidir.
Strategy deseni için mutlaka sınıf mı yazmam gerekir?
Hayır. Arayüzü tek metotlu ise birçok dilde bir fonksiyonu doğrudan parametre olarak geçirmek aynı işi görür. Sınıf, stratejinin kendi durumu, yapılandırması ya da enjekte edilen bağımlılıkları varsa daha uygun olur. Seçimi sade tutmak desenin amacını korur: değişen davranışı çağıran koddan ayırmak.
Strategy ile State deseni arasındaki fark nedir?
Strategy'de yöntemi dışarıdan bir istemci seçer ve genellikle sabit kalır; State'te ise nesne kendi durumuna göre davranışını değiştirir ve geçişleri durum nesneleri yönetir. Kargo ücretinin hesaplanma biçimi strateji, siparişin hazırlanıyor, kargoda ve teslim edildi gibi aşamalardan geçmesi durum örneğidir.
Strateji seçimini kim yapmalı?
Seçim bağlam sınıfının içinde değil, onu kuran katmanda yapılmalıdır: uygulamanın kök noktası, bir fabrika ya da girdiyi işleyen bir servis. Seçim bağlama gömülürse koşul zinciri yalnızca yer değiştirir ve bağlam yine somut sınıflara bağımlı olur. Eşleme tablosu bu işi sade tutmanın yaygın yoludur.
Strateji deseni performansı etkiler mi?
Tipik uygulamalarda ek bir arayüz çağrısı ölçülebilir bir fark yaratmaz; asıl maliyet çoğunlukla stratejinin yaptığı işin kendisidir. Performans gerçekten kritikse önce profilleme ile darboğazı bulun. Desen okunabilirlik ve değişime açıklık sağlar, bu kazanç çoğu iş uygulamasında küçük çağrı maliyetinden ağır basar.
İlgili rehberler
Kategori: Yazılım Geliştirme