Intercepting Filter Pattern(Filtreleme Tasarım Deseni)
Merhaba,
Bu içeriğimizde, kullanıcıdan gelen isteği(request) işleme almadan önce filtreleyip süzgeçten geçirmemizi sağlayan ya da istek neticesinde kullanıcıya döndürülecek cevabı(response) modifiye ederek o şekilde döndürmemize imkan tanıyacak olan Intercepting Filter Pattern‘ı inceliyor olacağız.
Intercepting Filter Pattern Nedir?
Bu pattern’ı izah edebilmek için direkt olarak şöyle bir örnek üzerinden misal verebiliriz; Bir uygulamada(web, masaüstü, console, mobile vs.) kullanıcıdan gelen istek üzerine işlem yapmadan önce kullanıcının oturum açıp açmadığını kontrol etmek ve session bilgileri bulunmadığı taktirde kullanıcıya isteğin başarısız olduğunu bildirip login sayfasına yönlendirmek isteyebiliriz. Ayriyetten oturum açmışsa eğer gerekli validation kurallarını kontrol etmek ve isteği ancak bu şekilde başarılı kılmak isteyebiliriz. Bunun yanında kullanıcıya bir cevap dönerken bu cevap üzerinde de farklı modifikasyonlar gerçekleştirip o şekilde cevabı dönmek isteyebiliriz. İşte bu ve bunun gibi istek süreçlerinin basamaklı bir şekilde bölümleme gereksinimi duyulan çalışmalarda, tasarımsal açıdan Intercepting Filter Pattern‘ı uygulayarak daha pratik ve stratejik çözümler getirebiliriz.
Intercepting Filter Pattern’ın Mantığı Nedir?
Intercepting Filter Pattern’da merkezi olarak filtre görevi gören sınıflar bulunmaktadır. Bu sınıflar sıralı bir şekilde tetiklenerek işlevsellik göstermektedirler.
Filtrelerin işlevleri sırasında herhangi bir filtrede hatalı bir durum meydana gelir yahut tespit edilirse kendinden sonraki gelen filtreler devreye girmeden işlem durdurulur ve kullanıcıya duruma dair gerekli bildiride bulunulur.
Intercepting Filter Pattern, istekleri karşılayacak ve handle edecek olan temel kod üzerinde, değişikliklere ve modifikasyonlara gerek kalmaksızın ortak hizmetleri standart bir şekilde işlemek amacıyla takılabilir filtreler oluşturmamızı sağlayan bir modeldir. Bu filtreler mevcut kodu değiştirmeksizin uygulamaya eklenerek devreye sokulabilir yahut çıkarılarak nitelikleri pasifize edilebilir.
Intercepting Filter Pattern’ın Uygulanması
Intercepting Filter Pattern’ını uygulayabilmek için yandaki şemada olduğu gibi ‘Filter Manager’, ‘Filter Chain’, ‘Filters’ ve ‘Target’ aktörlerinin kurguda yer alması gerekmektedir.
Bu aktörlerin kısaca ne olduğuna değinmemiz gerekirse eğer;
- Filter Manager
Filtre işlemeyi yöneten sınıftır. Doğru sıralamada filtre zincirini(Filter Chain) oluşturur ve işlemeyi başlatır. - Filter Chain
Filtreleri belirli bir target’a özel sıralı bir şekilde listeleyen(bir başka deyişle tutan) ve tetikleyen sınıftır. - Filters
Bir işe odaklanmış filtrelerdir. Bu filtrelerin sıralı bir şekilde işlenmesi filtre zinciri tarafından kontrol edilir. - Target
Client tarafından talep edilen kaynaktır/servistir/işlemdir.
Pratik Örnek
Şimdi, Intercepting Filter Pattern stratejisini yukarıdaki ilk görselde verilen mantığa uygun bir senaryo üzerinden örneklendirelim. Senaryomuz; client’tan personel(EmployeeService) ve müşteri(CustomerService) bilgileri için gelen istek süreçlerinde, öncelikle kullanıcı kimlik doğrulama kontrolü(AuthenticationFilter) ve ardından validasyon kontrolü(ValidationFilter) yapılarak her iki durumun doğrulanması neticesinde istekle ilgili sonucun üretilip döndürülmesini, aksi taktirde herhangi bir filtrede oluşan aykırı duruma binaen kullanıcıyı uyararak isteğin hedef servise erişemeden iptal edilmesini gerçekleştirelim.
- Adım 1 – Target Interface
Herşeyden önce client’ın erişmek istediği hedef(target) servislerini inşa ederek başlayacağız. Bunun için de öncelik olarak Target Interface‘i tasarlayarak başlayalım.//Target Interface public interface ITarget { void Handler(); }
- Adım 2 – Targets
‘EmployeeService’ ve ‘CustomerService’ sınıflarını oluşturarak somut hedeflerimizi(concrete target) oluşturalım.//Concrete Target public class EmployeeService : ITarget { public void Handler() => Console.WriteLine("Employee Service Ok"); }
//Concrete Target public class CustomerService : ITarget { public void Handler() => Console.WriteLine("Customer Service Ok"); }
Burada örnek servis kodlarına dikkat ederseniz eğer içlerinde farazi olacak şekilde ekrana belirli ifadeler basılmaktadır. Tabi ki de gerçek bir senaryoda ilgili servisler ihtiyaca istinaden geliştirilecek ve gerekli davranışları sergileyecektirler.
- Adım 3 – Filter Interface
Artık bu adıma kadar client’ın talep edeceği hedef sınıflarımız hali hazırda mevcuttur. Bundan sonra client’tan gelecek olan talebin yaşam döngüsü sürecinde devreye girecek olan filtreleri temsil edecek olan Filter Interface arayüzünü tasarlayarak yola devam edebiliriz.//Filter Interface public interface IFilter { void Execute(); }
Sistemde kullanılacak tüm filtreler, oluşturulan bu ‘IFilter’ arayüzünü implemente etmek mecburiyetindedirler.
- Adım 4 – Filters
Senaryo gereği kullanıcı kimlik doğrulama kontrolünü gerçekleştirecek olan ‘Authentication Filter’ ile birlikte validasyon kontrolünü sağlayacak olan ‘Validation Filter’ sınıflarını tasarlayalım.//Filter public class AuthenticationFilter : IFilter { public void Execute() { bool isAuthenticated = Configuration.IsAuthenticated; if (isAuthenticated) Console.WriteLine("User Authenticated"); else throw new AuthenticationException(); } }
//Filter public class ValidationFilter : IFilter { public void Execute() { bool isValidated = Configuration.IsValidated; if (isValidated) Console.WriteLine("Validated"); else throw new ValidationException(); } }
Yukarıdaki filtre sınıflarını incelerseniz eğer sorumluluklarına uygun gerekli kontrolleri yaptıktan sonra ya isteğin akışına izin vermektedirler ya da aksi bir durum söz konusuysa hata fırlatarak süreci sonlandırmaktadırlar. Burada birazdan gerçekleştireceğimiz test sürecinde filtreleri daha rahat deneyebilmek için aşağıdaki ‘Configuration’ static sınıfı üzerinden değerler kontrollü bir şekilde verilmektedir.
public static class Configuration { static public bool IsAuthenticated { get; set; } static public bool IsValidated { get; set; } }
- Adım 5 – Filter Chain
Oluşturulan bu filtreleri belirli bir hedef/target’a özel sıralı bir şekilde tutacak ve zincirleme tetikleyecek olan Filter Chain sınıfını oluşturalım.//Filter Chain public class FilterChain { readonly List<IFilter> _filters = new(); ITarget _target; public void AddFilter(IFilter filter) => _filters.Add(filter); public void RemoveFilter(IFilter filter) => _filters.Remove(filter); public void ExecuteFilters() { _filters.ForEach(filter => filter.Execute()); _target.Handler(); } public ITarget Target { set => _target = value; } }
Yukarıdaki kod bloğuna göz atarsanız eğer, belirli bir hedefe özel(_target) filtreleri ‘IFilter’ türünden olan koleksiyon(_filters) içerisinde tutmakta ve ilgili filtrelerin eklenmesi, çıkarılması ve çalıştırılması için gerekli fonksiyonları barındırmaktadır.
Özellikle ‘ExecuteFilters’ metodunun içeriğine nazar eylerseniz eğer önce ‘_filters’ koleksiyonundaki filtreler execute edilmekte ardından ‘_target’ referansındaki hedef servis handle edilerek tetiklenmektedir. Esasında burada filtre işlev önceliği tarafımızca belirlenerek target’tan önce ya da sonra olacak şekilde çalışmalar gerçekleştirebiliriz. Haliyle bu metot, client’ın yapacağı isteğin yaşam döngüsünün işlevsel kritiğini barındıran noktadır diyebiliriz.
- Adım 6 – Filter Manager
Ve son olarak client’ın hedeflediği(target) servis için yapılan isteğin yaşam döngüsünde, filtre zincirini barındıran Filter Chain sınıfını yönetebilmek için Intercepting Filter Pattern’ın kalbi olan Filter Manager sınıfını tasarlayarak, oluşturalım.//Filter Manager public class FilterManager { FilterChain _filterChain; public FilterManager(ITarget target) { _filterChain = new(); _filterChain.Target = target; } public void AddFilter(IFilter filter) => _filterChain.AddFilter(filter); public void RemoveFilter(IFilter filter) => _filterChain.RemoveFilter(filter); public void ExecuteFilters() => _filterChain.ExecuteFilters(); }
Görüldüğü üzere Filter Manager sınıfı içerisinde barındırdığı Filter Chain referansı sayesinde constructor’da aldığı target’a özel uygulanacak filtreleri yönetmekte ve execute etmektedir.
- Adım 7 – Test
Geliştirdiğimiz bu yapılanmayı aşağıdaki gibi bir çalışmayla teste tabi tutarsak eğer;class Program { static void Main(string[] args) { Configuration.IsAuthenticated = true; Configuration.IsValidated = true; try { FilterManager filterManager = new(new EmployeeService()); filterManager.AddFilter(new AuthenticationFilter()); filterManager.AddFilter(new ValidationFilter()); filterManager.ExecuteFilters(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } }
yandaki gibi bir ekran çıktısıyla karşılaşacağız. Nihayetinde kimlik doğrulama(IsAuthenticated) ve validasyon(IsValidated) kontrolleri için gerekli property’lere
true
değerini verdikten sonra(yani kısmi olarak kimliğin doğrulandığı ve validasyonların geçerli olduğunu düşünürsek), ‘FilterManager’ sınıfı üzerinden ‘EmployeeService’ isimli servise yapılan talep sürecinde eklenmiş olan ‘AuthenticationFilter’ ve ‘ValidationFilter’ filtreleri sırasıyla tetiklenmekte ve herhangi bir aksaklık olmadığından dolayı servis execute edilmektedir.Misal, validasyonların(IsValidated) doğrulanmadığını düşünürsek ‘ValidationFilter’da hata fırlatılacağından dolayı aşağıdaki gibi bir çıktı verilecektir;
Intercepting Filter Pattern’in Faydaları Nelerdir?
- Intercepting Filter Pattern, iyileştirilmiş yeniden kullanılabilirlik sağlar. Nihayetinde herhangi bir target’a yapılacak istek sürecinde, önceden geliştirilmiş bir filtre ihtiyaca binaen tekrar rahatlıkla kullanılabilir. Nihayetinde bir filtrenin kullanılabilirliği ‘IFilter’ arayüzü sayesinde uygulamadaki tüm target’lara genellenebileceğinden dolayı yeniden kullanılabilirlik hat safhaya çıkacaktır.
- Intercepting Filter Pattern, filtrelerin istek süreçlerinde opsiyonel olarak eklenilip çıkarılabilmesine imkan tanıdığı için esneklik sağlamaktadır.
Ayrıca unutulmamalıdır ki, gereksiz yere uzun işlev yapan filtreler client’tan gelen isteğin yaşam döngüsünün süresini uzatacağından dolayı performans açısından zarara uğratabilir.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Hocam AOP gibi bir tarz gördüm burada.