Strategy Design Pattern Nedir ?

Fatih İzgi
Kodcular
Published in
4 min readDec 11, 2022

Design Patterns eğitim serisindeki ilk yazımızda, tasarım desenlerinin “Yazılımcıların sıklıkla karşılaştığı problemlere ve yazılımların doğasında bulunan genel ihtiyaçlara getirilen çözümler” olduğunu söylemiştik. Sık karşılaşılan problemlerden bir tanesi ise; var olan algoritmalardan en uygun olanını uygun yerde ve karmaşıklıktan uzak bir şekilde kullanabilme ihtiyacıdır. Bu yazımızda, bu problemi çözmek için kullanabileceğimiz Strategy Design Pattern yapısını inceleyeceğiz.

Konuya başlamadan önce problemin ne olduğunu biraz analiz edelim :

İnternetten yemek tariflerine baktığınızda bir yemeği yapmak için birden fazla tarif olduğunu görebilirsiniz. Tariflerden birkaç tanesini denediniz ve iki tanesini oldukça beğendiniz. Dolayısıyla bu tarifleri kaydetmek istiyorsunuz. Defterinize tek başlık altında her iki tarifi de yazar mısınız ? Elbette hayır. İç içe geçmiş tarifleri okumak zor olacaktır. İlerleyen süreçte yemeğin farklı bir malzeme ile de çok lezzetli olduğunu düşündüğünüz zaman yine aynı başlık altına yeni bir tarif eklemeniz gerekecektir. Ayrıca, siz okuyabilseniz bile başka biri defterinize bakarak bir yemek yapmak istediğinde adımları takip etmesi oldukça zor olacaktır. Bu durum yazılım dünyasında da geçerlidir. Sıralama algoritmaları, bu çeşitliliğe en çok verilen örneklerden bir tanesidir.

Verileri sıralamak için kullanılabilecek birden fazla algoritma vardır. Her bir algoritmanın ise duruma göre avantajları ve dezavantajları bulunmaktadır. Kullanılan veri yapısı(Array, LinkedList, Tree vb.) ve verilerin sayısı gibi etkenler hangi algoritmanın en performanslı olduğu gerçeğini değiştirir. Uygun koşullarda uygun algoritmanın kullanılması gereklidir. Dolayısıyla akla gelen ilk çözüm her veri yapısının sıralanması gereken yerde uygun algoritmanın yazılmasıdır. Elbette bu hiç sağlıklı bir çözüm olmayacaktır. Öncelikle, istemci yapılan işe karışmış olacaktır. İstediğimiz, istemcinin “nasıl yapıldığından uzak” bir şekilde yalnızca “ne yapıldığı” ile ilgilenmesidir. İstemcilerin, işlemleri kendi gerçekleştiriyor olması hata riskini artıran bir durumdur. Ayrıca, mevcut durumun değişmesi ile istemcilerdeki kodların ayrı ayrı güncellenmesi gerekecektir. Gözden kaçan bir durum olma ihtimali oldukça yüksektir. Bununla beraber proje, copy/paste kodlar ile dolu karmaşık bir yapıya sahip olacaktır. Bu problemin önüne geçmek için sıralama algoritmaları tek bir merkez üzerinden istemcilerin hizmetine sunulabilir. Yalnızca sıralama işlemlerinden sorumlu bir sınıf içerisinde bir sort() metodu tanımlayarak metot içerisinde koşulların kontrol edilmesi ve buna göre bir algoritmanın gerçekleştirilmesi sağlanabilir. Yeni bir durum ile karşılaşıldığında yalnızca bu metodun güncellenmesi gerekecektir ki bu oldukça normal bir durumdur. Burada tartışılan nokta metot güncellemesinin nasıl olacağıdır. Tek bir metot içerisinde uygun koşulların kontrollerinin yapılması metodu if/else if/else yapılarına boğacaktır. Her güncellemede metot karmaşıklığı artacak, okunabilirlik zorlaşacak ve hata yapma riski yükselecektir. Ayrıca, algoritmaların birbirlerine bağımlı olması gibi problemler ile karşılaşılması da oldukça olasıdır. Dolayısıyla, yeni bir çözüme geçmemiz gerekmektedir. Akla gelen bir diğer çözüm ise her sıralama algoritmasının farklı sınıflarda tanımlanması ve tek merkez üzerinde uygun algoritmanın çağırılmasıdır. Merkezdeki kontroller için yine if/else if blokları kullanılsa bile çok daha sade metot yapıları elde edilecek ve hata yapma riski minimum düzeyde tutulacaktır.

Strategy Design Pattern, farklı algoritmaların farklı koşullarda uyum içerisinde çalışabilmesini karmaşıklıktan uzak bir şekilde sağlamak amacı ile kullanılır. Ayrıca istemci, algoritma içeriğinden uzak tutulur. Davranışsal(Behavioral) tasarım desenlerinden bir tanesidir ve kullanılması ile sistem performansının yüksek düzeyde tutulması, karmaşıklıktan uzak bir yapının elde edilmesi, hata riskinin düşürülmesi gibi faydalar elde edilir.

Öncelikle problemimizi yazılıma aktarmak adına örnek bir yapı oluşturalım :

Kullanabileceğimiz sabitler için bir OperationConstants sınıfı, temel işlemleri türetebileceğimiz Operation arayüzü ve OperationAdd-OperationSubtract-OperationMultiply-OperationDivide sınıfları ile birlikte temel dört işlemi gerçekleştirebileceğimiz bir yapı tasarladık. Artık hangi problem karşısında hangi çözümü uygulamamız gerektiğini belirleyebiliriz :

Görüldüğü gibi, OperationStrategy sınıfı parametre olarak verilen matematiksel işlemin ne olduğu tespit eder ve ona göre işlemi gerçekleştirecek olan sınıfı belirler. Bu noktada, istemcilerin probleme karşı izleyeceği stratejiyi kendi içlerinde bulmasına gerek kalmaz. Böylece yazılımın bakım maliyeti azalacaktır. İlgili tasarım deseninin temel mantığı budur fakat halen istemcilerin işlemleri kendi içerisinde gerçekleştirmesi gerekmektedir. Dolayısıyla tatmin edici bir sonuca ulaşamadık. Bu sebeple, belirlenen stratejiye göre işlemleri gerçekleştirecek merkezi bir yapı kurabiliriz :

Calculator sınıfı problemi tam anlamıyla çözmek ile sorumlu olan sınıftır. Problemin analizi ve çözüm yöntemi için OperationStrategy sınıfından faydalanmaktadır. İstemcilerin tek yapmaları gereken bu sınıf üzerinden problem çözümüne ulaşmak olacaktır. İstemci tarafını inceleyecek olursak :

Görüldüğü üzere, istemci sınıf yalnızca problemi verir ve sonucu elde eder. Ne problemi çözmek için hangi işlemin uygulanması gerektiği ile uğraşır ne de çözümü gerçekleştirir. Dolayısıyla, problem çözmek isteyen A, B, C gibi pek çok farklı istemcinin bulunduğu bir yapıda, işlemler tek merkezden yönetildiğinden dolayı yazılımın bakım maliyeti düşük olacaktır.

Not : Elbette ilerleyen süreçte değişikliğe gidilmesi gerektiği zaman, Strategy ve Calculator sınıflarının bir bakım maliyeti olacaktır. Örneğin, trigonometrik ve logaritmik işlemlerin de projeye eklenmesi durumunda çözüm yönteminin belirlenmesi için Strategy sınıfının güncellenmesi gerekmektedir. Bu noktada Strategy içerisine yeni koşul yapıları eklemek, ilgili tasarım deseninin mantığına karşı olacaktır. Bunun yerine Trigonometrik işlemler için farklı; Logaritmik işlemler için farklı Strategy yapıları oluşturmak ve var olan yapıya göre ilk Strategy sınıfını güncellemek çok daha doğru olacaktır. Strategy Desing Pattern, iç içe kullanılabilir bir desen olduğundan karmaşıklık ne kadar artarsa artsın, bakım maliyetinin kontrolsüz bir şekilde artmasının önüne geçer. Örneğin;

TÜM YAPI

Strategy Design Pattern konusunun ayrıntılarını ve inceliklerini öğrendiğimize göre tüm yapıyı incelemeye başlayabiliriz :

Yararlandığım Kaynaklar :

1- Refactoring Guru

2- HowToDoInJava

3- GeeksForGeeks

--

--