Tasarım desenleri makale serimize yine Behavioral tasarım kalıpları grubunda yer alan Strategy tasarım deseni ile devam edeceğiz. Açıkçası en sevdiğim GOF(Gang of Four) desenlerinden birisidir de diyebilirim. 🙂

Evet haydi bakalım neymiş bu Strategy?

İsminden de anlaşılacağı üzere bir işi yapabilecek birden fazla algoritmamız var ise orada hemen Strategy tasarım deseni ben buradayım diye bağırır. 🙂 En sık kullanılan tasarım deseni olduğunu da belirtmek isterim.

Neden kullanmalıyız?, sorusuna baktığımızda ise size kısaca şunu diyebilirim:

Yapılması istenilen bir işimiz var ve bu işi birden farklı yollarla yapma ihtiyacımız var. Bu gibi durumlarda var olan işi ilgili sınıfı sürekli refactor ederek if-else blokları ile yapmak yerine, yeni bir sınıf daha ekleyerek istenilen durumda ilgili işi ilgili sınıfta yapmamıza olanak sağlar. Böylece var olan sınıfımız üzerinde değişiklik yapmadan sistemimizi geliştirmiş olacağız. Burada en önemli tasarım prensiplerinden birisini olan Open-Closed prensibi söz konusudur.

İlgili desenimizin UML Diyagramına baktığımızda:

Diyagrama göz attığımızda Context ile Strategy sıfını arasında aggregation(bu tür bir ilişkide ilgili nesnelerin yaşam döngüleri birbirlerinden ayrıdır) türünde bir ilişki olduğunu görmekteyiz.

Strategy: Bir arayüz(Interface) tasarlayarak ortak olan tüm algoritmalarımızı burada toplarız.
ConcreteStrategy: Ilgili algoritmayı gerçekleyen gerçek sınıfımız.

Lafı fazla uzatmadan terminolojiyi bir kenara bırakarak hemen bir real-world örneği yapalım:

Örneğimizde bir e-ticaret sitesindeki ödeme kütüphanesini ele alalım. Birden fazla yöntemle ve banka ile çalışıyor olabiliriz bu sistemde. İster banka transferi ile ister mail order yöntemi ile veya sanal pos yöntemi ile ödemeyi çekiyor olabiliriz.

Öncelikle yukarıda bahsettiğimiz gibi ortak olan algoritmamız örneğimiz gereği ile MakePayment metotu olsun ve ilgili arayüzümüzü tasarlamaya geçelim.

    /// <summary>
    /// Strategy - Tüm ödeme strategy'lerimiz bu interface'den türeyecek.
    /// </summary>
    interface IPayment
    {
        void MakePayment();
    }

İlgili arayüzü tasarladığımıza ve MakePayment metotunu tanımladığımıza göre gerçekleyecek olan ilgili ConcreteStrategy‘lerimizi oluşturmaya başlayalım.

    /// <summary>
    /// ConcreteStrategy - Mail order yöntemi ile ödeme strategy'miz.
    /// </summary>
    class MailOrderStrategy : IPayment
    {
        public void MakePayment()
        {
            Console.WriteLine("Mail order yöntemi ile ödeme yapıldı.");
        }
    }

Mail order ödeme yöntemini sistemimize uyarladık ve ilgili MakePayment metotunu bir mesaj yazdırarak doldurduk. Sistem her zaman gelişime açık olmalı demiştik ya? Birde şirketler sürekli yeni bir şeyler isterler malum 🙂 Müşterimiz bizden bu seferde banka transferi yöntemi ile ödeme tipinin de entegre edilmesini istedi.

    /// <summary>
    /// ConcreteStrategy - Havale yöntemi ile ödeme strategy'miz.
    /// </summary>
    class BankTransferStrategy : IPayment
    {
        public void MakePayment()
        {
            Console.WriteLine("Havale yöntemi ile ödeme yapıldı.");
        }
    }

İşte bu kadar basit. IPayment arayüzünü implemente ederek sistemimizi genişletmiş olduk. Evet durmak yok! Bu seferde sanal pos yöntemi ile ödeme çekilmesini istediler bizden, hazırlıklı olduğumuz için hemen yine işe koyuluyoruz. 🙂

    /// <summary>
    /// ConcreteStrategy - Kredi kartı ile ödeme strategy'miz.
    /// </summary>
    class CreditCardStrategy : IPayment
    {
        public void MakePayment()
        {
            Console.WriteLine("Kredi kartı yöntemi ile ödeme yapıldı.");
        }
    }

İşte bu da bu kadar basit. 🙂 İyi güzel hoş, gerçekleyen sınıflarımızı oluşturduk istekler doğrultusunda ama ben bunu nasıl kullanacağım? Huh?

Hemen Context‘imizi yani örneğimiz gereği PaymentOperation sınıfını hazırlayalım. Bir nevi ilgili ödeme işlemini sarmalayıp developerlara daha basic bir kullanım sunan wrapper sınıfımız.

    /// <summary>
    /// Context'imiz. IPayment strategy'mizin içeriğindeki metotları sarmalar.
    /// </summary>
    class PaymentOperation
    {
        private IPayment _odeme;
        public PaymentOperation(IPayment _odeme)
        {
            this._odeme = _odeme;
        }

        public void MakePayment()
        {
            this._odeme.MakePayment();
        }
    }

Sarmalayıcı sınıfıda constructor injetion yaparak hazırladığımıza göre hemen örneğimizin kullanımına bir göz atalım:

        static void Main(string[] args)
        {
            PaymentOperation paymentOperation = null;

            // Client gelecek olan değere göre runtime'da istediği gibi ödeme tipini seçebilir.
            string paymentType = "BankTransfer";

            // If-Else bloklarını ise gerektiğinde bir kaç satır Reflection kodu ile aşabiliriz.
            // Fakat gerekmedikçe over architectur'ada kaçınılmaması gerekmektedir.
            // Attığımız taş, ürküttüğümüz kurbağaya değecek mi? Buna karar vererek. :)

            if (paymentType == "BankTransfer")
            {
                paymentOperation = new PaymentOperation(new BankTransferStrategy());
            }
            else if (paymentType == "CreditCard")
            {
                paymentOperation = new PaymentOperation(new CreditCardStrategy());
            }
            else if (paymentType == "MailOrder")
            {
                paymentOperation = new PaymentOperation(new MailOrderStrategy());
            }

            paymentOperation.MakePayment();

            Console.ReadLine();
        }

Basit tutmaya çalışarak ve real-world örneği vererek daha anlaşılır bi hale gelmesini istedim. Bir sonraki makalemde görüşmek dileğiyle. 🙂

 

Gökhan Gökalp

View Comments

  • Eline, ağzına sağlık Gökhan'ım. Kanka Factory pattern'i anlatırken, Strategy pattern'le aralarındaki farkı anlatırsan, bide benim araştırdığım bazı makalelerde beraber kullanılmaları tavsiye ediliyor, o konuda da yazarsan on numara olur.

    • Merhaba, bu iki pattern birbirine benzemektedir. Factory sadece object creation ile ilgilenirken, Strategy ise farklı algoritmalarla işlem yapabilmek için bir mekanizma sağlamaktadır. Factory'yi farklı strategy'ler create edebilmek için de kullanabiliriz.

  • Merhaba,

    Yine if - else yazmış olduk. Buradaki amaç if else yazmadan uygulamada farklı kodları çalıştırmak değil mi ? Burada sanki kodları class'lar içine alarak daha düzenlemiş olduk. Open - Close prensibi nerede kullanılmış oldu :)

    • Merhaba evet haklısınız, if-else i burada kullandığımız kısım, oluşturulan "PaymentOperation" ın kullanıldığı noktada. Dilerseniz o noktayı da küçük bir reflection ile generic hale getirebilmek mümkündür. :) Veya "PaymentOperation" ı herhangi bir DI container'ı ile de inject edebilirsiniz farklı senaryolar karşısında. Tamamen use-case'e göre değişiklik gösterebilecek bir durum açıkcası.

Recent Posts

Overcoming Event Size Limits with the Conditional Claim-Check Pattern in Event-Driven Architectures

{:en}In today’s technological age, we typically build our application solutions on event-driven architecture in order…

2 months ago

Securing the Supply Chain of Containerized Applications to Reduce Security Risks (Policy Enforcement-Automated Governance with OPA Gatekeeper and Ratify) – Part 2

{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…

7 months ago

Securing the Supply Chain of Containerized Applications to Reduce Security Risks (Security Scanning, SBOMs, Signing&Verifying Artifacts) – Part 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

9 months ago

Delegating Identity & Access Management to Azure AD B2C and Integrating with .NET

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 year ago

How to Order Events in Microservices by Using Azure Service Bus (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 years ago

Providing Atomicity for Eventual Consistency with Outbox Pattern in .NET Microservices

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 years ago