|
"Chain of Responsibility(CoR)" (Sorumluluk Zinciri) Tasarım Deseni |
|
Gönderiliyor lütfen bekleyin... |
|
|
Tasarım desenleri
ile ilgili geçen yıl başlamış olduğum yazı dizisine GoF desenlerinden biri olan
Chain of Responsibility yani Sorumluluk Zinciri isimli desenin açıklaması ile
devam ediyoruz. Yazının bundan sonraki bölümlerinde bu desenden kısaca CoR
şeklinde bahsedeceğim. Yazı disini başından beri takip etmeyenler için aşağıda
daha önceden yazmış olduğum makalelere bağlantı bulabilirsiniz.
"Singleton" Tasarım Deseninin(Pattern)
C# ile Gerçekleştirilmesi
Abstract
Factory Tasarım Deseni(Design Pattern)
"Builder"
Tasarım Deseninin C# ile Gerçekleştirilmesi
"Prototype"
(Prototip) Tasarım Deseni
CoR deseni GoF
olarak anılan desenlerden "Behavioral" yani nesneler arasındaki lişkiyi
düzenleyen kategorisine girmektedir. Bu ve bundan sonraki yazılarımda Behavioral
- Davranışsal desenleri inceleyeceğim.
CoR bütün olarak
ele alınabilecek bir dizi işlemi gerçekleştiren nesnelerin birbirlerinden bağımsız
bir şekilde çalışabilmesini ve her sınıfın sadece kendisi ile ilgili işleri
yapmasını sağlayan bir desendir. Sorumluluk zinciri ismide bu tanımdan gelmektedir.
Birbirinden farklı olarak çalışan bu nesneler arasındaki tek bağ ise ortak bir
mesaj(request) yapısıdır. Bütün nesneler bu mesaj yapısını kullanarak işlerini
gerçekleştirir. Bu tasarım deseni, nesnelerin aynı görevde yer alması fakat
birbirlerinin yaptığı işten çok da haberdar olmama durumunu temel alır ki nesnelerin
bu şekildeki ilişkisine terminolojide loosly coupled (gevşek bağlı)
nesneler denilmektedir.
CoR tasarım deseni daha çok, aynı işte görev alan bir çok nesnenin birbirinden
haberdar olmamasını aynı zamanda bu nesneleri kullanacak istemcinin de (asıl
program) bu nesnelerin ne şekilde ilişkide olduğunu bilmemesi gerektiği durumlarda
kullanılır. CoR deseni olmazsa yapılacak iş sadece bolca if-else blokları yazmaktır.
Bu da kontrolü ilerleyen zamanlarda kaybetmek anlamına gelecektir.
Daha somut örneklerden
yola çıkarsak daha net anlaşılacaktır. Bu anlamda CoR deseninin bir kaç kullanım
amacını incelemekte fayda var.
Basit bir iş akışı(workflow) uygulaması düşünün. Bu uygulamada şirket içinde
yapılacak olan projelerin maliyetlerinin personele ödenmesi için bir masraf
geri ödeme uygulaması yaptığımızı farzedelim. Proje maliyeti 1000 YTL ve altında
ise bir üst yöneticinin onayı, 1000 YTL ile 2000 YTL arasında ise insan kaynakları
müdürünün onayı ve 2000 YTL den fazla ise genel müdürün onayının olması gerektiğini
farzedelim. Ve her bir durumda (case) farklı işlemlerin yapılacağını varsayalım.
İşlem zincirinin en alt yöneticiden başladığı için ilk onay alınması gereken
yer bir üst yoneticidir. Eğer masraf 1000 YTL den büyükse bu durumda gelen istek
bir sonraki yoneticiye (sorumluluk zincirinin bir sonraki adımı) pas edilir.
Ve bu böylece zincirin son halkasına kadar devam eder. Bu durumun yazılıma dökülmesi
bir çok yöntemle yapılabilir, örneğin her bir durumu ifade eden if blokları
yazılıp her if bloğunda ilgili sınıf ın ilgili metotları çağrılabilir. Ancak
durumların ileride değişebileceği ve daha da karmaşıklaşabileceği ihtimalide
gözönünde bulundurularak nesne tasarımını daha sistematik hale getirmek nerdeyse
zorunludur. Zaten şunu açık yüreklilik ile soyleyebilirim : bir kaç
istisna durum dışında eğer programınızda onlarca if -else satırı yer alıyorsa
programınızda nesnel tasarım açısından mutlaka bir sorun var demektir.
CoR desenine göre, verdiğimiz bu örnekte her bir onay adımı ayrı bir sınıf ile
temsil edilir ve her bir sınıfın çalışması diğerinden soyutlanır, ve her bir
nesnenin ortak olarak kabul ettiği bir istek (request) yada mesaj (message)
vardır. Bu ornekte mesaj proje maliyet tutarıdır. CoR deseninin asıl amaçlarından
birisi de her bir onayı temsil eden sınıfların ne şekilde birbirleri ile iletişime
geçeceği ve sorumluluk zincirinde ilerlemenin nasıl sağlanacağının koda dökülmesidir.
Bu örnekte verdiğim sorumluluk zincirinin şeması aşağıdaki gibi çizilebilir.
Şekil 1 - Örnek bir CoR Zinciri
Pratikte CoR deseninin
bir çok kullanım alanı vardır. İsterseniz bu örnekleri inceleyelim. Bu arada
aşağıdaki örnekler CoR desenini kullanmak için seçilmiş uygun uygulamalardır,
ancak örnekler bu kadarla sınırlı değildir. Boyle bir yapının varlığını bir
şekilde zihninize yerleştirdikten sonra bir çok uygulamanın çatısını oluştururken
CoR deseninden faydanacağınızı söyleyebilirim.
Mail Sunucularındaki
Mesaj Filtreleri
Bir çok mail sunucusunda
mesaj mail kutunuza gelene kadar bir çok filtreden geçer. Size biri mail gönderdiğinde
bu mail ilk önce spam filtresinden geçer ki bu ayrı bir modüldür genelde. Spam
filtresinden geçen mail eğer başarılı ise ardından çeşitli mail filtrelerinden
de geçtikten sonra en son mailin iletilmesi modülüne gönderilir. Spam belirleme,
fake mail belirleme ve mail gönderme modülleri birbirinden tamamen bağımsız
ve habersizdir dolayısıyla mail gönderme işlemini bir bütün olarak ele alabilmek
için her bir modülün ortak mesaj/request olan mail nesnesi üzerinde çalışması
gerekir. CoR deseni bu işlemleri gerçekleştirebilmek için nasıl bir çatı kurmamız
gerektiği hakkında ipucu verir.
Derleyiciler
Bildiğiniz üzere nesne yönelimli programlamanın temelinde türetme mantığı yatar.
Birbirinden türemiş nesne zincirinde hiyerarşinin an altında bulunan bir sınıf
nesnesi üzerinden herhangi bir metodu çağırdığımızda derleyici o metodun hangi
sınıflarda bulunduğunu tespit etmek ve hangi sırada çalışacağını belirlemek
amacıyla CoR desenindeki mantıktan faydalanır. Çağrılan metot ilk sınıfta yok
ise bir sonraki sınıfa eğer ondan da yoksa daha üstlere doğru ilerleyerek uygun
sınıfı bulana kadar zincirde ilerlemeye devam eder. Eğer zincirde uygun çağrılabilecek
metot ise yok ise derleyiciler çoğunlukla derleme hatası dediğimiz hataları
vermektedir. Bir çok derleyicinin metot çağırma mantığı bu şekildedir.
Yorumlayıcılar
Bir çok yorumlayıcı
içeren uygulama girdi olarak aldığı komutları çeşitli mekanizmalara göre katorgorize
eder. Örneğin komut olarak girilen yazı çalıştırılabilir dosya ise yeni bir
proses açıp onu çalıştırması, kout bir resim dosyasının açılması veya mail gönderilmesi
ise ilgili aksiyonların alınması hep belirli bir öncelik sırasında gerçekleşir.
Zira bir komut iki anlama geliyorsa zincirin halkasındailk hangi blok var ise
o devreye girecektir. Komutların bu tarz sıralı bir şekilde işletilmesi ve ilgili
modülün devreye girmesinin altında yatan tasarım da yine CoR deseni ile açıklanabilmektedir.
Görsel
Event(Olay)’ların Yakalanması
Bir windows uygulamasında
container dediğimiz bir çok kontrol bulunmaktadır. Her bir container farklı
sayıda diğer kontrolleri yapısnda bulundurabilir. Bir çok kontrol ve konteyner
kontroller aynı türden olaylara tepki verebilirler. Örneğin farenin ekranda
hareket etmesini hem bir Panel hemde textbox hemde form nesnesi yakalayabilir.
Ancak ekranın her noktasında form veya panel bulunmamakta, bu durumda olayların
hangi kontroller tarafından ele alınacağı nasıl belirlenecek? Yani farenin hareket
etmesine hangi kontroller nasıl cevap verecek? İşte bu noktada CoR alt yapısından
faydalanılır. Olaya tepki verebilecek nesneler zinciri kurulur ve bir noktadan
başlayarak zincirde devam edilir, taki son halkaya kadar.
Loglama
Literatürde logloma
ile ilgili herhangi bir uygulama alanı varmı bilmiyorum ama CoR tasarım desenini
bir uygulamada önermiştim. Uygulamada karşılaşılan problem şuydu : Uygulama
çalışırken alınan hatalar bir şekilde bir veri kaynağına yazılmalı ve saklanmalıydı.
Bu veriler ile programın çalışma zamanında ne kadar istisna yarattığının ölçülmesi
hedefleniyordu. Ama bu veri kaynağının ne olacağı çok da tahmin edilemiyordu.
Duruma göre bir text dosyası, SQL sunucusu yada event log mekanizması olabilirdi.
Bunun yanında şu şekilde bir istek daha vardı. Eğer ulaşılabilecek ve yazma
hakkı olan bir SQL Server tanımlanmış ise loglama bu veritabanına yazılacak
yoksa event log’a yazılacak bunada imkan tanınamıyorsa yazma hakkı tanımlanmış
varsayılan bir dizindeki metin dosyasına yazılması isteniyordu. Yani öncelik
şu şekilde : Veritabanı, Event Log, Metin Dosyası. Bu durumda her bir loglama
çeşidini farklı bir sınıf ile temsil edip ortak Exception sınıfı ile çalışabilecek
hale getirip CoR altyapısını kullanmayı tavsiye ettim. Bu makalede bu örneğin
ana çatısını CoR ile ne şekilde oluşturabileceğimizi ayrıntılı bir şekilde inceleyeceğiz.
CoR
UML Modeli
CoR tasarım deseni
temel olarak 3 ana bileşenden oluşmaktadır. Bu bileşenlerin açıklaması ve UML
diyagramı aşağıda verilmiştir.
1 - Temel Chain(Zincir) Sınıfı : Örneğimizdeki LogBase sınıfına
karşılık gelmektedir. CoR zincirini oluşturan her bir eleman bu sınıftan türemiştir.
Zincirin her elemanının ortak olan mesajı işlemesi için gerekli olan arayüzü
(interface) sunar. Bu sınıf içerisinde ayrıca zincirin bir sonraki halkasına
referans vardır. Bu referansın türüde yine kendi cinsindendir. Bu çok mantıklı
bir yaklaşımdır , çünkü zincirin diğer bütün halkalarıda bu sınıftan türemiştir.
2 - Somut
Zincir Elemanları : Örneğimizdeki DatabaseLogger, EventLogger ve TextLogger
sınıflarına karşılık gelmektedir. Teorik olarak bu sınıfların sayısı istenildiği
kadar olabilir. Ancak pratikte CoR deseni, zincirdeki eleman sayısının 10’u
aşmadığı durumlar için uygundur. Diğer durumlar için ayrı bir tasarım deseni
mevcuttur, ilerleyen makalelerimde bundan da bahsedeceğim. Bu sınıfların her
biri tamamen birbirinden bağımsızdır, ve aralarındaki tek ortak nokta yukarıda
soylediğimiz temel sınıftan türemiş olması ve ortak bir mesaj üzerinde çalışmasıdır.
Burada geliştireceğimiz örnekte bu mesaj Exception sınıfı türünden bir nesne
olacaktır.
3 - İstemci
: CoR tasarım deseninde geliştirilen nesneleri kullanan ve zinciri şekillendiren
istemci nesnesidir. Bu makaledeki örnekte Main metodu istemcidir. Zincirdeki
elemanların sırasını belirleyen ve zincirdeki ilk elemana isteği (request) gönderen
de yine client(istemci)’tır.
Not : CoR tasarım
deseninde klasik olarak mesajı işleyen yalnızca bir başarılı zincir halkası
vardır. Mesajı başarılı bir şekilde işleyen halkaya successor denilmektedir.
Ancak bu bir zorunluluk değildir. İstenirse mesajı birden fazla halkada başarılı
bir şekilde işleyebilir. Bu makaledeki örneğimizde de yalnızca bir halka başarılı
olacaktır. Benim kişisel tavsiyemde bu yönde olacaktır. Zira
CoR desenin temel çıkış amacı da işi yapacak halkayı bulabilmektir. Eğer her
halkada mesaj işlenecekse CoR deseninin bir anlamı kalmamaktadır.
Aşağıda resim, yukarıda bahsettiğim nesnelerin örnek UML diyagramıdır.
UML diyagramından
CoR tasarım deseninin ne şekilde koda dökebileceğimizi az çok tahmin etmiş olmalısınız.
Gördüğünüz gibi SqlLogger,EventLogger yada TextLogger yapısındaki bir üye yardımıyla
LogBase’deki LogYaz metodunu çağırarak zincirin bir sonraki halkasına isteği
göndermektedir. Şimdi sırasıyla diyagramda görülen yapıların C# ile nasıl gerçekleştiğine
bakalım.
CoR Deseninin C# ile Gerçekleştirilmesi
LogBase
Sınıfı
public
abstract class
LogBase
{
protected LogBase
NextLogger;
public
void
SetNextLogger( LogBase logger )
{
this.NextLogger
= logger;
}
abstract
public void LogYaz(
Exception exc );
}
|
Görüldüğü üzere
LogBase sınıfında, diğer sınıflarda override edilmek üzeri LogYaz isimi bir
metot Exception türünden bir nesne parametresi ile bildirilmiştir. LogBase sınıfında
ki bu metodun kullanımı bir anlam ifade etmeyeceği için abstract olarak bildirilmiş
ve metodun gövdesi yazılmamıştır. Ayrıca zincirin (chain) bir sonraki halkasını
referans etmek için yapısında LogBase türünden bir referans barındırmaktadır.
Bu referans ise SetNextLogger isimli metot ile belirlenmektedir. İstemci ve
diğer somut sınıflara ait kaynak kodları inceledikten sonra amacımızı daha net
anlayacığınızı tahmin ediyorum.
SqlLogger,EventLogger,TextLogger
Sınıfları
public
class
SqlLogger :LogBase
{
public override void
LogYaz( Exception exc )
{
try
{
//Veritabanıa
exc nesnesine ait verinin yazılması işlemi
//Bu
bölümün yazılması size bırakılmıştır.
}
catch
{
if(NextLogger
!= null)
NextLogger.LogYaz(exc);
}
}
}
public
class
EventLogger :LogBase
{
public override void
LogYaz( Exception exc )
{
try
{
//Event
Loga exc nesnesine ait verinin yazılması işlemi
//Bu
bölümün yazılması size bırakılmıştır.
}
catch
{
if(NextLogger
!= null)
NextLogger.LogYaz(exc);
}
}
}
public class TextLogger
:LogBase
{
public override void
LogYaz( Exception exc )
{
try
{
//Metin
dosyasına exc nesnesine ait verinin yazılması işlemi
//Bu
bölümün yazılması size bırakılmıştır.
}
catch
{
//Zincirin
son halkası. Eğer burada da başarılı olunamıyorsa ona göre aksiyon alınmalıdır.
}
}
} |
Yukarıdaki her
3 sınıfında tek ortak yani LogBase den türemiş olmaları ve dolayısıyla LogYaz
isimli metoda ve NextLogger isimli metoda sahip olmalarıdır. Boylece eğer herhangi
bir adımda hata alınırsa bir sonraki adıma geçilerek devam edilir. Hata alma
kontrolü bu ornekte try-catch blokları ile yapılmıştır. Sizler kendi koşullarınızı
pek tabiki farklı yontemlerle gerçekleştirebilirsiniz.
Son olarak xxxLogger
isimli sınıflardan belirlediğimiz çalışma sırası koşulu dahilinde nasıl zincir(sorumluluk)
oluşturacağımızı ve işlemi başlatacağımız istemci nin kaynak kodunu yazalım.
SqlLogger
sqllogger = new SqlLogger();
EventLogger eventlogger = new EventLogger();
TextLogger textlogger = new TextLogger();
sqllogger.SetNextLogger = eventlogger; // Zincirin
adımlarını oluşturuyoruz.
eventlogger.SetNextLogger = textlogger; // Zincirin
adımlarını oluşturuyoruz.
try
{
//Programın normal akışı
}
catch(Exception exc)
{
sqllogger.LogYaz(exc); //
bir hata oluştuğunda logluyoruz. Zincir kendi içinde işlemey başlıyor.
}
|
Gördüğünüz gibi
başlangıç adımını attıktan sonra gerisi ile client ilgilenmemekte. Böylece ilerleyen
zamanlarda yeni bir log kaynağımız oldugunuda yapacağımız tek şey ilgili log
sınıfını yazmak ve zincirin istediğimiz halkasına eklemek. Üstelik bunun için
client programda herhangi bir değişiklik yapmıyoruz. Tabi zinciri oluşturma
işlemini ayrı bir sınıfi içerisinde yaptığınızı varsayıyorum.
Bu makalede temel bir CoR çatısını nasıl yazabileceğimizi ve CoR tasarım deseninin
kullanılabileceği gerçek uygulamalara örnek verilmiştir. Yukarıda kodları ile
verdiğim örnek gerçek bir uygulamadan alınmıştır. Sizlerde bu tür tasarım problemlerinize
çözüm aramadan önce varolan desenleri incelemeyi unutmayınız. Bir sonraki tasarım
deseni makalesinde görüşmek üzere herkese iyi çalışmalar diliyorum.
Makale:
"Chain of Responsibility(CoR)" (Sorumluluk Zinciri) Tasarım Deseni C#, Visual C# ve .NET Sefer Algan
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|