Skip to content

Strategy Pattern

Strateji pattern behavioral(davranışsal) bir pattern olup, bir nesnenin belirli bir işlevselliği farklı şekillerde gerçekleştirmesi gerektiği durumlarda davranışını değiştirerek runtimeda istenen işlevin yerine getirilmesini sağlar. Kısacası belirli bir görevi yerine getirmek için farklı stratejilerin kullanılmasına olanak sağlar diyebiliriz.

Temel fikri ve uygulaması, bir sınıfın davranışını çalışma zamanında değiştirmemizi sağlayacak bir arayüz sağlamaktır. Bu arayüz, sınıfın içindeki bir değişkenin değerine göre, farklı algoritmaların kullanılmasına izin verir ve buda aynı işi yapmak için farklı stratejilerin kullanılmasına olanak tanır aynı zamanda bu stratejiler birbirlerinin yerine kullanılabilir hale gelecektir. Bu sayede esnek, ölçeklenebilir ve bakımı kolay bir yapı oluşturabilmek için altyapı sağlamaktadır, yeni stratejiler kodu değiştirmeye gerek kalmadan eklenebileceğinden ve farklı stratejiler kendilerine özgü işlev/özellikleri içereceğinden SOLID prensiplerinden open/open ve single responsibility prensiplerine doğrudan uygundur diyebiliriz. Ayrıca, farklı algoritmaların kullanılabileceği durumlarda “if-else” veya “switch-case” yapılarının kullanılması yerine, daha iyi bir çözüm sunar. Örnek bir senaryo üzerinden gidecek olursak; bir e-ticaret sistemi geliştirdiğinizi düşünün başlangıçta ödeme yöntemi olarak, banka hesabı ile ödeme ve kredi kartı ile ödeme seçenekleri olsun, ödeme aşamasında seçilen ödeme yöntemine göre farklı davranışlar sergilenmeli ve ödeme işlemi gerçekleşmelidir. bu senaryoda strategy pattern kullanıldığında, ileride yeni entegre edilecek ödeme yöntemleri için (apple pay, paypal vs.) yeni stratejiler oluşturarak kolay şekilde geliştirmelerimize devam edebiliriz. Örnek senaryomuzu koda dökecek olursak;

from abc import ABC, abstractclassmethod

class PaymentStrategy(ABC):
    
    @abstractclassmethod
    def pay(self, amount):
        pass


class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number, expiry_date, cvv):
        self.card_number = card_number
        self.expiry_date = expiry_date
        self.cvv = cvv

    def pay(self, amount):
        print(f"Amount {amount} is paid by credit card {self.card_number}")


class BankAccountPayment(PaymentStrategy):
    def __init__(self, account_number, sort_code):
        self.account_number = account_number
        self.sort_code = sort_code

    def pay(self, amount):
        print(f"Amount {amount} is paid from bank account {self.account_number}")


class PaymentContext:
    def __init__(self, payment_strategy):
        self._payment_strategy = payment_strategy
        
    @property
    def strategy(self):
        return self._payment_strategy
    
    @strategy.setter
    def strategy(self, payment_strategy):
        self._payment_strategy = payment_strategy

    def make_payment(self, amount):
        self._payment_strategy.pay(amount)


# Create a payment context with credit card payment strategy
payment_context = PaymentContext(CreditCardPayment("1234-5678-9012-3456", "12/25", "123"))
payment_context.make_payment(100)

# Change the payment strategy to bank account payment strategy
payment_context.strategy = BankAccountPayment("1234567890", "11-22-33")
payment_context.make_payment(200)

--------------------------------------------------------------------
Output:
>>> Amount 100 is paid by credit card 1234-5678-9012-3456
>>> Amount 200 is paid from bank account 1234567890

Sonuç kısmına geçmeden önce strategy pattern ile kurduğumuz yapının uml diagramı:

https://www.dofactory.com/img/diagrams/net/strategy.png

Herhangi bir tasarım desenini uygulamaya karar vermeden, uygulamanın ihtiyaçlarına uygunluğu ve avantajları ile dezavantajları arasında bir denge kurmak önemlidir. Strategy pattern için bir kaç dezavantaj olabilecek durumu şöyle sıralayabiliriz:

  • Karmaşıklık: Strategy pattern gereksiz karmaşıklığa neden olabilir. Farklı stratejiler için ayrı sınıflar oluşturmak ve bu sınıfların bir araya getirilmesi, kodun olması gerekenden daha karmaşık hale gelmesine neden olabilir.
  • Hız: Strategy pattern ekstra bir seviyede soyutlama katmanı ekleyebilir ve bu da performansı etkileyebilir. ( PaymentContext ödemeyi gerçekleştirebilmek için bir ödeme stratejisine ihtiyaç duymakta, bu nedenle PaymentStrategy sınıfından türeyen CreditCardPayment veya BankAccountPayment nesneleri oluşturulur ve buda performansı negatif yönlü etkiler, çünkü CreditCardpayment veya BankAccountPayment’i direkt kullansaydık tüm bu aradaki işlemlerin yapılmasına gerek kalmayacaktı.)
  • Dependency Inversion Principle (DIP, Tersine Dönük Bağımlılık İlkesi): Strategy pattern DIP ilkesine uymamaktadır. (Context içinde somut strateji sınıfları kullanılıyor.)
  • Yeniden Kullanılabilirlik: Strateji sınıfları contextin beklediği işleri yerine getirecek şekilde oluşturuldan her durumda yeniden kullanımı mümkün olmayabilir.

Sources:

  • https://refactoring.guru/design-patterns/strategy/
  • https://www.dofactory.com/net/strategy-design-pattern
Published inBehavioral Design PatternsDesign Patterns

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *