| Editor Login | Register | ||
| > Bilgisayar > Nesne Programlama > C# |
|
|
| TransparentProxynin Gizemli Yapısı ile Dinamik Proxy Yaratmak | ||||||||||||
Değerli C#nedir?com okuyucuları; öncelikle uzun bir aradan sonra iş yoğunluğumdan dolayı yazamadığım yazılarıma yeniden başlamanın heyecanı içerisinde olduğumu belirtmek isterim. .NET platformunun sunduğu hemen hemen bütün yapıların C# seviyesinde tip(type) bakımından bir açıklaması vardır. Ancak bazı öyle kavramlar vardırki tip kavramından yola çıkarak açıklayamayız. Örneğin her sınıfın object’ten türemesi, delegate yapısında BeginInvoke ve EndInvole yapısının olması, Remoting altyapısında bulunan TransparentProxy gibi. Bu gibi yapılar genellikle derleyici ile doğrudan ilişkili değil çalışma zamanı ve CLR ile ilgili olmaktadır. İşte bu makalemde sizlere proxy tasarım kalıbı ile doğrudan ilgili olan .NET’in sunduğu dinamik proxy tasarım kalıbından bahsedeceğim. Bu yazımın anlamlı olabilmesi için öncelikle GoF tasarım kalıplarından olan Proxy ve kullanım alanlarından bahsetmek istiyorum. Proxy kelime anlamı olarak temsilci demektir. GoF kalıbı olarak ise bir nesnenin görevini başka bir nesne yardımıyla yerine getirmek anlamında kullanılır. Örneğin iş nesnelerinize ait metotların tamamının çağrılmadan önce belirli güvenlik kontrollerinden geçirmek isterseniz, yada kritik olarak işaretlediğiniz operasyonların çağrılmasından önce bir takım kayıtlar (log) tutmak isterseniz, yada belirli fonksiyon gruplarının merkezi bir noktadan yönetilmesini sağlamak isterseniz aslında yapmanız gereken iş nesneleriniz için bir proxy(vekil yada temsilci) tanımlamaktır. Pratik olarak .NET içerisinde bir çok yerde proxy sınıfları kullanılmıştır. Bunlardan en önemlisi Remoting altyapsında bulunan proxy yapısıdır. Proxy sınıfları daha çok nesnenin yaratılması güç olan sınıflarda işlemleri basitleştirmek ve kolay yönetebilmek amacıyla yapılır. Son zamanlarda adını sıkça duyguğumuz AOP (Aspect Oriented Programming) metodolojisinin .NET’te uygulanabilmesi içinde yine Proxy sınıflarından faydalanıyoruz. AOP’a göre çapraz kesen ilgi (cross cutting concerns) dediğimiz loglama (logging), ön bellekleme (caching), güvenlik(security), izleme (trace), istisna yönetimi (exception handling) ve doğrulama (validation) gibi rutin işlemlerin iş sınıflarından (bussiness classes) tamamen ayrılması ve bağımsızlaştırılması gerekir. Ve bu ilgilerin kod içerisine gömülmesi derleme aşamasından bağımsız yapılmalıdır. Bu kural AOP’un en temel kuralıdır. Ancak AOP’un tam olarak platformalarda uygulanabilmesi için derleyicilerin bu yeni modele göre geliştirilmesi gerekir. C# dünyasında şu anda böyle bir oluşum olmadığından AOP gerçek manada uygulayamıyoruz ancak çeşitli yollarla (reflection, attribute , proxy vs ) simule edebiliyoruz. Java dünyasında ve .NET dünyasında AOP mimarisini uygulayan AspectJ isimli bir altyapı bulunmakta. Konumuz AOP olmadığı için daha fazla detaya girmeyeceğim ancak sonraki makalelerimde AOP’a detaylı girmeyi planlamaktayım. Tekrar konumuza dönecek olursak; AOP benzeri bir modeli makalemizinde konusu olan TransparentProxy sayesinde gerçekleştirebiliyoruz. Geçtiğimiz aylarda yayınlanan Enterprise Library 3.0 içerisinde bulunan Policy Injection Application Block (PIAB) içerisinde de yine bu yaklaşım mevcuttur. Proxy kalıbı aynı zamanda mevcut bir altyapıya kendi bileşenlerimizi entegre etmek amacıylada kullanılabilir. Örneğin Oğuz Yağmur’un Debugger ile ilgili yazdığı yazıda bulunan DebugerTypeProxy sınıfı buna bir örnek olarak gösterilebilir. .NET içerisindeki bu sihirli mekanizmadan bahsetmeden önce GoF tasarım kalıplarından Proxy kalıbı hakkında temel bilgi ve uygulama vermek istiyorum. GoF, Proxy kalıbını tanımlarken statik bir proxy sınıfından bahsetmiştir. Yani asıl ulaşmak istediğimiz nesneye başka bir nesne yardımıyla erişiyoruz. Proxy tasarım kalıbının genel sınıf diyagramı aşağıdaki gibi gösterilebilir.
Yukarıdaki örnek kod kafanızda bir çok soru işaretine neden olduğunu görüyor gibiyim. Evet, isterseniz bu tarz bir proxy (aslında statik proxy de denilebilir) kalıbının avantajları ve dezavantajlarını madde madde yazalım ve sonraki bölümlerde dezavantajına dinamik proxy ile cevap bulalım. Statik proxy tasarım kalıbının dezavantajları; Her sınıf için ayrı bir proxy sınıfının yazılması ki bu büyük bir problemdir. Tutarlılık için her sınıfın arayüzünün çıkarılması (zorunlu değil)ve avantajları; İş nesnesine ait erişimi kontrol altına almış oluyoruz. Asıl iş nesnesine erişmeden önce yada sonra bir takım ek işlemler yapabiliyoruz. Asıl iş nesnemize dokunmadan ek özellikler eklemiş oluyoruz. Dağıtık(distributed) mimaride önemli bir konudur. Tip güvenliği korunur (proxy gerçek nesne ile aynı arayüze sahiptir). Bu durum programcı için önemlidir.Yukarıdaki listeden de görüleceği üere proxy kalıbını bu şekile kullanmayı bir kaç özel istisna durum dışında pek tercih edilmeyeceği görülmektedir. Çünkü her iş nesnesi için yeni proxy sınıfları yaratmak gerekiyor. Bu durum itici ve yorucu bir iştir. Bu yüzden dinamik proxy sınıfları gündeme gelmiştir. Yazımızın ana konusuda işte dinamik proxy sınıflarının programcının üretkenliğinide düşürmeyecek şekilde .NET ile nasıl yapılacağıdır. İşte bu işi yapacak olan .NET Framework içersinde bulunan ReelProxy ve TransparentProxy isimli gizemli sınıflardır. TransparentProxy ve ReelProxy sınıflarının çalışma biçimini incelemeden önce bu sınıflar olmadan dinamik proxy üretilebilirmi sorusuna cevap arayalım? Aslında üretirbilir. Cevabı ise çok basit: reflection kütüphanesi ve generic mimari ile. Örneğin aşağıdaki gibi bir tanımı içerek bir DinamikProxy nesnesi ile her sınıf için ortak çalışabilecek bir proxy nesnesi yaratılabilir.
Yukarıdaki gibi bir Proxy yaratıldığında bu sefer proxy sınıfında gerçek nesneye ait üye elemanlar ancak çalışma zamanında (runtime) çözülebileceği için performans problemi yaratacaktır. Aynı zamanda programcı kod yazarken metot ve üye eleman güvenliğini tam sağlayamama riski ile karşı karşıya kalacaktır. Buda kod yazmayı bir hayli zorlaştırağı için malesef tercih edilemeyecek yöntemler arasında yerini alacaktır. Tabi yine istisnalar kaideyi bozmaz. Mecburen tercih edilen durumları yok sayıyoruz. İşte tam bu durumda imdadımıza .NET içerisinde bulunan TransparentProxy ve ReelObject yetişiyor. Şimdi 3 farklı yöntemle gerçek dinamik proxy sınıflarını nasıl üreteceğimizi inceleyeceğiz. TransparentProxy ve ReelProxy System.Runtime.Remoting.Proxies isim alanınında bulunan ReelProxy nesnesinden türeyen bir sınıf ile herhangi bir nesneye ait metodunun çağrımına müdahale edebilirsiniz. ReelProxy içerisinde Invoke isimli metot override edilerek asıl metot çağrılmadan önce herhangi bir işlemi gerçekleştirebilirsiniz. Bu işlem için elbette yine Reflection kullanacağız. TransparentProxy ise herhangi bir nesnenin yerine geçen saydam bir sınıftır. Yani programcı kod yazma aşamasında HavaleYap isimli fonksiyonu çağıracaktır ancak aslında çalışma zamanında HavaleYap fonksiyonu çağrılmayacaktır. Bunun yerine ReelProxy sınıf içerisinde bulunan Invoke isimli fonksiyon devreye girilecektir. Invoke fonksiyonu içerisinde metot çağrımından önce ve sonra istenilen işlem icra edilebilir. Bu işlemi yapabilmek için ReelProxy sınıfı içerisinde bulunun GetTransparentProxy isimli metot çağrılarak nesne yaratılmalıdır. Bu nesne aslında object tipinde olmasına rağmen saydamlığından ötürü biz object ile uğraşmayız direkt olarak asıl nesneymiş gibi erişebiliriz. Bu durum C# dil kuralları ile açıklamak mümkün değildir. TransparentProxy nesneleri derleyici ve CLR tarafından özel işleme tabi tutulurlar. Bunu bir örnekler aşağıda göstereceğim. Yukarıdaki paragrafı şematik olarak göstermek istersek en basit dinamik proxy yapısını kavramış oluruz.
Yukarıdaki şemadan anlaşılacağı gibi TransparentProxy ile saydam proxy yaratabilmenin şartı gerçek sınıfın MarshalByRefObject sınıfından türeme zounluluğudur. (MarshalByRefObject sınıfı System isim alanında bulunur.) Buna göre adım adım hangi sınıfları yazmamız gerektiğini inceleyebiliriz.
Şimdi ise BankOPerationObject isimli sınıfa erişmeye yarayacak ReelProxy sınıfını oluşturabiliriz. Bunun Proxy sınıfımızı System.Runtime.Remoting.Proxies isim alanında bulunan ReelProxy isimli sınıfta türetmemiz gerekecektir. ProxyObjectCreator.cs
Yukarıda bulunan kod Proxy sınıfını incelediğinizde gerçek nesneye ait bir referans bulunmaktadır. Ancak gerçek nesnenin yaratılma işlemi kendimize ait static CreateObject() isimli metot ile yaratmamız gerekiyor. Bu metodun geri dönüş değer BankOperationObject olmasına rağmen biz metotdan geriye GetTransparentProxy() sınıfın döndürdüğü object nesnesini aktarabiliyoruz. Bu işlemi tür dönüştürme işlemi ile legal hale getiriyoruz. İstemci bu proxy üzerinden HavaleYap yada farklı bir metot çağırdığında aslında nesnenin ilgili metodu çağrılmayacak direkt olarak ReelProxy içerisindeki Invoke isimli metot devreye girecektir. Burada yapmak istediğimiz herşeyi yapıp metodu çağırabileceğimiz gibi bir exception da üretebiliriz. Örneğin yetkisiz bir çağrım ifadesi oluşursa metodu çağırmakayabiliriz.
Dikkat ederseniz yukarıdaki kod bloğunda HavaleYap metodu çağrılmasına rağmen TransparentProxy sayesinde aslında bu fonksiyon çağrılmayacaktır. Bunun yerine çalışma zamanında ReelProxy’de bulunan Invoke metodu devreye girecektir. Yani biz aslında metot çağrımına müdahale etmiş olduk. Metodu çağırmadan önce daha önce bahsetmiş olduğumuz çapraz kesen ilgilerin hepsini Invoke metodu içerisinde gerçekleyebiliriz. Bu yöntem ile bir önceki yöntemde bulunan tip güvenliğini kaybetme dezavantajını bertaraf etmiş olduk. Ancak bu örnek ile hala her sınıf için proxy nesne yaratma handikapından kurtulamadık. Bu sorunuda biraz sonra aşacağız. TransparentProxy ve ReelProxy (2.Yöntem) Bu yöntem iş nesnelerimizin proxy ile yönetilcek arayüzlerini bir interface olarak tanımlamamız gerekiyor. Bu amaçlar BankOperationObject sınıfımızı aşağıdaki gibi değiştirmemiz gerekiyor.
İkinci aşamada ise ReelProxy sınıfından türeteceğimiz sınıf içerisinde gerçek nesne yerine IBankOperation arayüzünü kullanmamız gerekiyor. Bu amaçla ProxyObjectCreator sınıfımızı aşağıdaki gibi değiştirmemiz gerekiyor.
Bu durumda istemci kodumuzda aşağıdaki gibi değiştirilmek zorunda.
2. yöntemle kazandığımız tek şey MarshalByRefObject sınıfından kurtulmak oldu. Hala her sınıf için proxy yazma zahmetinden kurtulamadık. Bu aşamada ProxyObjectCreator sınıfımızı geliştirmemiz lazım. Şükür ki .NET 2.0 ve C# 2.0 ile birlikte Generic mimarisi ile şablon türler tanımlayabiliyoruz. 3. yöntemde ise bu sorunuda bertaraf etmek için ProxyObjectCreator isimli sınıfımızı iş sınıfların tamamen soyutlayacağız. Bunun için Reflection ve generic mimarisine hakim olmamız lazım. Generic mimarisi ve reflection kütüphanesi ile ilgili sitemizde bir çok makale bulunmaktadır. Arama bölümünden bu yazılara erişebilirsiniz. Bu yöntemde iş sınıflarımız değişmeyecektir. (Tek koşul aynı remoting mimarisinde olduğu gibi MarshalByrefObject sınıfından türetilmesidir.) Yeni yazacağımız generic Proxy sınıfı ile her türlü iş nesneimize proxy yarıdmıyla dinamik bir şekilde erişme şansına sahip olacağız. Dolayısıyla iş nesnelerimiz aşağıdaki gibi olabilir.
Sıra en önemli sınıfımızı tasarlmaya geldi. Dikkat ederseniz bu sınıf içerisinde artık RealObject yerine sınıf bazında tanımlanan TObject isimli bir şablon tür kullanılmıştır.Her sınıfın bir yapılandırıcısı olma zorunluluğunu koyduğumuza dikkat ediniz. Bu proxy içerisinde gerçek nesnelerin yaratılması için zorunludur. Kısaca özetlemek gerekirse MarshalByrefObject sınıfından türeyen ve içinde bir adet varsayılan yapıcı metot içeren her iş nesnesi bu proxy sınıfı yardımıyla temsil ettirilebilecektir. GenericProxyCreator.cs
İstemci kodumuz ise aşağıdaki şekillerde olabilecektir. Dikkat ederseniz her türlü nesneyi rahatlıkla proxyGenerator sınıfında kullanabiliyoruz.
Generic mimari üzerine inşa ettiğimiz Proxy sınıfı TransparentProxy yardımıyla nihayet istediğimiz kıvama gelmiştir. Bu aşamadan sonra yapmak gereken AOP’un bahsettiği çapraz kesen ilgileri bir şekilde uygulamak. Örneğin iş nesnelerimizdeki üyelere çeşitli attribute’lar koyarak gereken cache,güvenlik, doğrulama vs gibi işlemleri organize edebiliriz. Tabi bu niteliklerin (attribute) Invoke metodunun içerisinde ele alınması lazım.
Gördüldüğü gibi TransparentProxy ile iş nesneleri rutin olarak kullanılan çapraz kesen ilgilerden bağımsız bir hale getirilebilir. Buda AOP’un temel ilkesine bizi oldukça yaklaştırmaktadır. Ancak AOP bundan ötesinide sunmaktadır elbette. İlerleyen yıllarda C# dili ve CLR, AOP ilkelerine göre değiştirilirse belkide bu tarz işlemlere bile gerek kalmadan yapabileceğiz. |
|
| Bağlantılar: bilgininefendisi.net |
| Open Source Document Project | AUP&TOS |