Image 236
Design Pattern Blog Serisi: 1 – Chain of Responsibility

Merhabalar,
Neredeyse her iş mülakatında sorulan o soruyla başlamak istiyorum 🙂 Hangi desing pattern’leri biliyorsunuz, nerelerde uyguladınız ya da bu desing hangi pattern’e karşılık geliyor? Her ne kadar uygulamadan incelediğimiz, göz gezdirdiğimiz pattern’ler olsa da yapısını ve mantığını tam olarak anlamadığımızda hem uygulamada hem de aktarmada sorunlar yaşıyoruz.

Ben de bu durumdan dolayı design pattern’ler ile ilgili akılda kalıcı bir şekilde design pattern blog serisi yapmaya karar verdim. Aslında her pattern belirli bir sorunu çözmek üzere oluşturulmuş yapılardır. Geliştirme yaparken bilmeden kendimizce uyguladığımız pattern’ler olduğu gibi kullandığımız mevcut yapılarda uygulanmış pattern’lerle de karşılaşıyoruz. Serinin ilk örneği olarak aklımda yıllardır yer edinmiş olan Chain of Responsibility pattern ile başlamak istedim. Behavioral pattern grubunda bulunan ve adında da anlaşıldığı üzere bir iş için sorumlukların (handlers) birbirini takip edecek bir akış (zincir) oluşturması mantığına dayanan bir tasarım desenidir.
Not: Pattern grupları ile ilgili detaylı bilgi için buraya tıklayabilirsiniz.

Yukarıdaki zincirde olduğu gibi zincirdeki her nesne kendinden sonraki nesneyi tanır ve ona sadece zinciri devam ettirmek için ihtiyaç duyduğu kadar (Loosely Coupled) bağlıdır. Bu yapı için gündelik hayattan bazı örneklerle konuyu somutlaştıralım. İlk okuduğum günden beri aklımdan çıkmayan bir örnekle başlayalım. Her yerde görebilceğiniz yiyecek otomatları Chain of Responsibility ile ilgili güzel bir örnek olacaktır.
Bir şey almak istediğiniz zaman otomata bozuk para atıyorsunuz ve bu para cihaz içinde doğru zincir nesnesini bulana kadar her bir zincire uğruyor ve doğru zincir nesnesine ulaştığında paranın tutarı tespit ediliyor.


İşte tüm bu süreç Chain of Responsibility’nin bir parçası haline geliyor. Yukarıdaki resimde olduğu gibi istek geldiğinde, zincirin bir nesnesi gelen isteği işleyerek sorumluluğunu yerine getirir veya eğer gelen istek zincirin o nesnesine uygun değilse, isteği bir sonraki handler’a aktarır.
Şimdi bu pattern’i code ile uygulamak için yine günlük hayatta sıkça kullandığımız ATM’den para çekme işlemi ile ele alalım. ATM’ye çekilecek tutarı girdik ve ATM en uygun banknotlar ile paramızı bize verecek. ATM içerisinde her banknot için ayrı bir kasanın olduğunu düşünelim. Kasalarda para varsa bize o banknot ile parayı verecek, eğer yoksa bir sonraki zincirde bulunan para ile yeniden hesaplama yapıp paramızı vereceği basit bir örnek oluşturalım. Öncelikle uygulamamız için CashDrawerRequest adında bir modeli oluşturalım. Bu model bizim tüm handler’larımızda gezecek olan process’in temel modelidir diyebiliriz.


Ardından CashBoxHandler adında zincirin temelini oluşturacak Ihandler’dan türemiş bir abstract class oluşturalım. IHandler ile farklı farklı handler’lar oluşturarak projenize implemente edebilirsiniz.

Yukarıda gördüğünüz gibi her zincir parçası (handler) temelde iki görev gerçekleştiriyor.
1.     _nextHandler: Kendinden sonra zincirin diğer elemanının kim olduğunu bilmek için kullanılıyor.
2.     ProcessRequest: Gelen request zinciri elemanın sorumluluğunda ise işleme, değilse bir sonraki zincir parçasına iletmek için kullanılmaktadır.
Zincirin temelini oluşturduktan sonra artık her bir parçayı specific sorumlulukları için oluşturmanın vakti geldi. Bizim örneğimiz için nakit kasalarını banknot türlerine göre ayırarak 4 farklı nakit kasası oluşturduk.

Her bir zincir parçası (handler), request’i alıp kendi sorumluluğunda ise işleyip, değilse bir sonraki handler’a gönderecek şekilde kurgulandı.
Ardından main içerisinde request ve zincir parçalarını oluşturduk. SetNextCashBoxHandler metodu ile zincir parçalarımızı birbirine loosely coupled şekilde birbirine bağlamış olduk.

Uygulamamızı çalıştırdığımızda aşağıdaki çıktı ile hangi handler gelen isteği işlemiş bunu görmüş oluyoruz.

Uygulamanın örnek kodlarını bağlantıdan indirebilirsiniz.
Bu pattern’in kullanım örneklerinden bir tanesi de .Netcore içerisindeki middleware mekanizmasıdır. Middleware istekleri ve yanıtları işlemek için bir pipeline ile bir araya getirilen handler’lar olarak düşünülebilir. Her bir middleware bileşeni kendisinden sonraki parçaya request’in geçip geçemeyeceğini seçer. Aşağıda .Netcore için örnek bir custom middleware yapısını görebilirsiniz. Örneğimiz içerisinde bulunan handler’a yapısal olarak benzemektedir.


Umarım faydalı olmuştur. Bir sonraki yazımızda görüşmek üzere. 😊

Referanslar

  1. refactoring.guru/design-patterns/chain-of-responsibility
  2. kurumsaljava.com/2009/10/09/chain-of-responsibility-tasarim-sablonu/
Mehmet Can İnci
Mayıs 23 , 2022
Diğer Blog İçerikleri