Tasarım Deseni Serisi: Strategy Design Pattern’i Anlamak

Herkese merhaba, bu yazımda Strategy Design Pattern hakkında bilgi vermeye çalışacağım. Bu design pattern seride anlatmak istediğim ikinci pattern olarak yer alıyor. Oldukça sık kullanılan ve sevilen bir pattern olduğunu düşündüğümden dolayı anlatmak için sabırsızlanıyorum. Umarım sizler için faydalı olur, şimdiden teşekkürler. 🙂

Strategy Design Pattern Nedir?

Strategy Design Pattern, bir algoritmanın ailesini tanımlar. Bunları ayrı ayrı kapsülleştirir ve birbirine değiştirilebilir kılar. Bu desen, birbiriyle değiştirilebilen algoritmaların kullanılmasına olanak tanır. Bu uygulamanın genişletilebilirliğini artırır ve yeni stratejiler eklemeyi ya da mevcut stratejileri değiştirmeyi kolaylaştırır.

Strategy Design Pattern genellikle bir Context sınıfı ve bu Context’in ele aldığı birden fazla alternatif servisten oluşmaktadır. Bağlam sınıfı, algoritmanın çağrılacağı yerdir ve bu algoritmayı temsil eden bir strateji nesnesi üzerinden çalışır.

Bu desen, davranışsal tasarım desenleri kategorisine aittir.

Bu tanımlardan sonra deseni daha iyi anlamak için bu desenin hangi problemlere çözüm sağladığını yakından inceleyelim.

Strategy Design Pattern Hangi Problemlemi Çözmeyi Amaçlar?

Benim için bir çözümü anlamak için mühim olan çözümün var olma nedenini benimsemektir yani bu desen hangi probleme çözüm olarak üretilmiştir ve neden tercih edilmektedir? Bunu iyi anlayıp analiz etmek gerekmektedir.

Bu doğrultuda problemi iyi analiz ederek anlarsak, çözümünü de anlamamız oldukça kolaylaşacaktır.

Bu nedenle öncelikle problemden bahsedeceğim ve ardından Strategy Design Pattern çözümünü inceleyeğim.

Problemi örneklemek için şöyle bir senaryo oluşturalım.

Varsayalım ki şirketiniz kullanıcı bilgilerini saklı tutabilmek için kullanıcıların kişisel bilgilerini şifrelemek istiyor ve kullanıcı verilerinin güvenliğini artırmayı hedefliyor.

Şifreleme entegrasyonunun yapılması için seçildiniz ve işe koyuldunuz. Her şey yolunda gitti ve ACrypt adında bir şifreleme yöntemi geliştirip entegrasyonunu yaptınız.

Örnek olması için açısından aşağıdaki kodu gözlemleyelim.

Gördüğünüz üzere ACrypt adında bir sınıf oluşturdum ve bu sınıf aracılığıyla çok basit bir şifreleme yapısı kullanabiliyorum. Bu yöntemle string bir metin alınıyor ve alınan metinin harflerinin sırası değiştiriliyor. Tabii ki gerçek projelerde bu şifreleme yöntemleri çok daha karmaşık olabiliyor ancak sadece örnek olması açısından böyle bir metot ile örnekledim.

Servisi entegre ettiniz ve şifreleme servisiniz oldukça iyi çalışıyor, tüm kullanıcı bilgilerini şifrelemeniz gerektiği yerlerde bu servisi kullandınız ancak yeni bir senaryo oluştu ve artık sizden ACrypt değil de başka bir yöntem kullanmanız istendi.

Biraz araştırma daha yaptınız ve yeni bir yöntem daha keşfettiniz. Örneğin; bu yöntem BCrypt şifreleme olsun. Bu şifreleme yöntemi ise alınan metni ters çevirip şifrelesin.

Şimdi burada düşünmemiz gereken durumlar şunlar;

  1. Bizim ACrypt şifreleme servisini kullandığımız kaç yer var?
  2.  ACrtypt servisini kullandığımız yerler acaba merkezi bir yerden mi çağrılıyor?

Merkezi bir metot aracılığıyla servisi kullanmadıysak işler bizim için oldukça zorlaşacaktır. 100 farklı serviste kullanılan ve her seferinde yeniden yaratılan ACrypt servisi için yüzlerce yeri güncelleyip BCrypt servis ile değiştirmemiz gerecektir.

Merkezi bir yerden çağrıldığını düşünürsek bu durumda merkezi yerde kullanılan ACrypt servisini BCrypt ile değiştirmemiz yeterli olabilir.

Buradan çıkarmamız gereken sonuç şudur; bu gibi senaryolarda alternatif servislerin kullanımı söz konusu ise context yapısı kullanmak bize avantaj sağlayabilir.

Peki Context nedir? Aşağıda bu terimi anlatmak için bir kod parçası paylaşacağım.

Context sınıfı aslında bizim servislerimizi yaratıp yönetmemize olanak sağlayan yapıdır. Yukarıdaki kod içerisinde görüldüğü üzere Context sınıfımız bir encryption servisi oluşturmakta ve bu servisin EncryptUserInfo metodunu kullanmamıza imkan sağlamaktadır.

Context sınıfını ekledik artık yeni eklenecek şifreleme servislerini buradan yönetebiliriz o zaman ACrypt olarak tanımlanmış kodları hemen BCrypt olarak güncelleyelim.

Nereleri değiştirmem gerektiğini yukarıda dikdörtgen kutucuklar ile işaretleyerek gösterdim.

Artık yüzlerce değişiklik yapmama gerek yok sadece 4 değişiklik ile alternatif şifreleme yöntemlerimi yönetebilirim.

Bu çözüm oldukça güzel görünüyor ancak hâlâ eksik olan bir nokta var.

Eksik olan nokta şudur; Context sınıfımız servislere sıkı sıkıya bağlanmış durumdadır. Yani her metot değişikliğinde bu Context sınıfını tekrar güncellemek durumunda kalacağız ve bu durumda gevşek bağlılık prensibine ters düşmüş olacağız ve bu durum ileride bize sorun çıkarabilecek potansiyeldedir.

Peki acaba servislerimizi Context sınıfından soyutlamak mümkün müdür?

Cevap; tabi ki mümkündür işte aslında burada soyutlama gerçekleştirdiğimizde tam olarak bir Strategy Design Pattern yapısı ortaya çıkacaktır ve sonuç aşağıdaki gibi olacaktır.

İlk etapta soyutlama için bir interface oluşturdum. Bu interface örneğimizde IEncrpytService olarak yer alıyor.

Daha sonra iki servisimizi de bu interface’den miras alacak şekilde güncelledim ve bu sayede artık IEncrpytService servis adı altında hem ACrypt hem de BCrpyt servisini kullanabileceğim.

Sonraki aşamada ise Context sınıfının içerisini güncelledim madem IEncrpytService adı altında servislerimin ikisini de kullanabiliyorum öyleyse artık IEncrpytService üzerinden yani interface üzerinden ilerleyebilirim.

Sonuçta interface aracılığıyla Context sınıfını servislerden soyutlamış olduk.

Bu durumda artık hangi servisi kullanacağımızı Context içerisinde belirlemiyoruz. Context sınıfını oluşturduğumuz kısımda belirliyoruz. Bu demek oluyor ki Context sınıfı için IEncrpytService interface’i altındaki herhangi bir servis dışarıdan parametre olarak alınabilir.

Main metodunun içerisine baktığımız zaman sadece Context sınıfını oluştururken bir kereliğine BCrypt servisini kullanması için parametre olarak yolladık ve bu bizim için yeterli oldu.

Şimdi son durumda BCrypt servisinden ACrypt servisine geçiş yapmaya çalışalım.

Yalnızca Main metodu içerisindeki dikdörtgen ile işaretlenmiş servisin tipini değiştirmem benim için yeterli oldu J Artık BCrypt yerine ACrypt kullanıyorum.

Birinci senaryoda 100 değişiklik yaparak çözüme ulaşmıştık, ikinci senaryoda 4 değişiklik yaparak çözüme ulaşmıştık ancak Context sınıfına bağımlıydık, üçüncü senaryoda ise hem daha az değişiklik yaparak çözüme ulaştık hem de Context sınıfına sıkı sıkıya bağlı değiliz.

Sonuç olarak Strategy Design Pattern; servislerin entegrasyonunda ve alternatif servislerin uygun stratejilere göre kullanılmasına olanak sağlar.

Strategy Design Pattern, bu örnekteki gibi durumlarda sistemdeki değişiklikleri minimize etmeyi ve gelecekteki ihtiyaçlara uyum sağlamayı mümkün kılar. Bu desen; modülerlik, esneklik ve sürdürülebilirlik gibi avantajlar sağlayarak yazılım tasarımını geliştirmeye yardımcı olur.

Umarım bu yazı sizler için yararlı olur, okuduğunuz için teşekkür ederim.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir