Bilgisayarlar varolmaya başladıktan sonra bir takım yazılımların da onlarla birlikte     varolması kaçınılmaz bir hal almaya başlamıştı. Ancak ilk etapta bu makineleri     programlamak o kadar da kolay değildi. Kimi zaman bazı entegreleri değiştirerek     kimi zamansa üç dört harften meydana gelen komutları peşi sıra     dizmekle bu işlem gerçekleşiyordu. Elbette her adımda bilim insanları bu     konularda çalışmalar yapmış ve belli çözümler üretmeye     başlamışlardı. Nispeten bu çözümlerin birazcık da olsa sonuna yetişmiş     biri olarak pek çok yazılım problemini deneyimleme şansına erişebildiğim     için kendimi çok şanslı hissediyorum.
Yazılım geliştirmeye BASIC (Beginner’s All-purpose Symbolic Instruction Code;     evet adı kendisi kadar kolay değil!) ile başlamış biri olarak git gide büyüyen     programların nasıl yönetilememeye başladığını görmemiş olsaydım belki     de şu anda hala daha neden nesneye yönelik bazı metodları kullanmam gerektiğini     tam olarak anlayamıyor olabilirdim. Ancak ne güzel ki bunları yaşamış ve sıkıntılarını     görmüş ve artık nerelerde nasıl yöntemler kullanmam gerektiğini algılayabiliyorum     hatta buna bağlı olarak nerelerde nasıl soyutlamalar yapmam gerektiğini çok     daha kolay algılayabiliyorum.
Nesneye Yönelim,
Evet, herşeyin aslında nesne olduğunu düşünün. Yarattığınız her sınıfın,     kullandığınız her şeyin aslında yaşayan birer nesne olduklarını ve onların birbirleri     arasında da haberleşebildiklerini hatta bir nesnenin aslında başka nesneler olarak     da varolabildiğini, bir takım özellikleri de atalarından miras aldıklarını     düşünün. Herkesin tek bir işten mesul olduğunu ve bu uyumları sayesinde     eskilerinin yerine yenilerinin çok kolay bir şekilde adapte olabildiklerini     düşünün. Yine düşünün, bir nesnenin sadece bir tane     olduğunu ve o spesifik işi yapabilmeleri için birbirlerini özelleştirebildiklerini     düşünün.
Peki yazılım üretmeye bu pencereden bakarsak neleri çözebiliriz?     Böyle düşünmemizin bize projelerimizde ne katkısı olur? Aslında bu     sorulara cevap verebilmek için ben size bir kaç soru sormakla başlamak     istiyorum; Kod yazarken hiç kopyala yapıştır yaptığınız oluyor mu? Yeni bir     projeye başlarken herşeyi yeniden baştan düşünüp tekrar tekrar mı     yazıyorsunuz? Kodlarınıza baktığınızda bir sınıf içerisinde binlerce satır     kod görüyor musunuz? Eğer bu sorduğum sorulara “evet” cevabını     verdiyseniz muhtemelen bir şeyler yanlış gidiyordur. Lütfen bundan sonrasını     daha dikkatli okumaya gayret edin.
Nesneye yönelik programlama bu gibi sorunları çözüp, tekrar     kullanabilirliği(reuseability) arttırmak adına bir çözüm olarak     sunulmuştur. Ancak kimi durumlarda yetmediği bazı noktalar da vardır. Birazdan bunlardan     da bahsediyor olacağım. Ama öncelikle aşağıdaki örneği incelemenizi rica     ediyor olacağım;
Diyelim ki bir proje geliştiriyorsunuz. Projenizi bitirdiniz teslim ettiniz. Ancak     müşteriniz ya da sponsorunuz size yaptığınız programın bazı noktalarında performansın     çok düşük olduğunu ve bunların da bir şekilde takip ediliyor olması     gerektiğini söyledi. Buna bağlı olarak da sizden bununla ilgili bir geliştirme     talebinde bulundu. Sizde kolları sıvadınız ve          sadece oop tekniklerini en doğru şekilde kullanıp bir çözüme     doğru ilerlemeye koyuldunuz. Bunun için hemen bir PerformanceTracker nesnesi     yaptınız ve sorun olduğunu düşündüğünüz her metodun içerisine     bu performans takip kodlarını yazmaya koyuldunuz. Ancak bunun uygulamanız gereken     neredeyse elli metod mevcut ve ne yazık ki bir sebepten ötürü bunları     ortaklaştırmanız da mümkün değil. O zaman başınızı eğip klavyenin başına     geçtiniz ve tamamen “oop tekniklerini kullanarak kopyala yapıştır“     yapmaya başladınız ve aşağıdaki gibi bir sonucu elde ettiniz,
public void ExecuteScript(string scriptCode)         
{         
                    PerformanceTracker.Begin(“ExecuteScript”, scriptCode);         
                    .....         
                    PerformanceTracker.End();         
}
Bu yaptığınızı elli method üzerinde tekrar ettiniz. Evet çözüm     buldunuz. Ancak sizden sonra gelen programcıların da bu yöntemi kullanması     gerektiğini onlara anlatmanız ve aynı zamanda bundan sonraki her method için     bunu tekrarlamanız gerekmekte. Tamam. Müşteriniz bu durumdan çok memnun     kaldı, problemler çözüldü. Bir süre sonra müşteriniz     yine size geldi ve “Methodları kimlerin çağırdığını ve çağıran     kişilerin bu methodu çağırmaya izni var mı yok mu onu da ayırt etmek istiyorum.”     gibi masum bir istekte daha bulundu. Bunun üzerine siz yine kolları sıvadınız     ve methodunuzu aşağıdaki şekle sokmaya başladınız,
public void ExecuteScript(string scriptCode)         
{         
                        AuthenticationManager.Authenticate();         
                        PerformanceTracker.Begin(“ExecuteScript”, scriptCode);         
                        .....         
                        PerformanceTracker.End();               
}
Benzer şekilde bunu da elli method içerisinde tekrar ettiniz. Tebrik ederim!     Şu anda müşterinizin her isteğini karşılamış durumdasınız. Peki sizce bir şeyler     yanlış gitmiyor mu? Yani her adımda bir sürü şeyi tekrar tekrar yazmak     zorunda kaldınız. Muhtemelen kopyala yapıştır da yaptınız. Peki ya bu durum sizce     nesneye yönelik programlama’ya karşı bir durum değil mi?
Kesinlikle öyle. Bu durumu çözebilecek başka bir takım yöntemler     elbette mevcut, hatta oop içerisinde bunları çözebilecek bazı     tasarım desenleri (design pattern) bile var. Ancak içlerinden benim en çok     beğendiğim yaklaşım, Aspect Oriented Programming (AOP).
Bakış Açısı,
Aspect kelime anlamı gereği, bakış açısı anlamına gelmektedir. Aspect oriented     da “Seperation of Concern” (İlişkilerin ayrılması) yaklaşımıyla ortaya     çıkmış bir metodolojidir. İlgilendiği şeylerle nesneye yönelik programlamanın     üstüne eklenmiş bir tuğla daha gibidir ve en temel amacı tekrar kullanılabilirliği     daha da üst düzeylere çıkartmaktır. Peki yukarıdaki problemi aspect     oriented bir şekilde nasıl çözebilirdik? Şimdi şu durumda konuyu biraz     daha kolaylaştırmaya çalışalım. Diyelim ki elinizde bir attribute olsun ve     bu attribute ile işaretlediğiniz tüm sınıflar bir anda ilgili bir özelliği     tanımlamaya koyulsunlar. Ve bu özelliği de ancak eklenen attribute varoldukça     tanımlasınlar. Diğer hiç bir durumda bu özellik çalışmasın. Nasıl     mı? Buyrun bir örneği,
[PerformanceTracker]         
public class ScriptInvoker : ContextBoundObject         
{         
                        public void ExecuteScript(string scriptCode)         
                        {                                              
.....         
                        }         
}
Yukarıdaki durumda dikkat ederseniz method’umuz tamamen işine yönelmiş     oldu. Yani kendi yapması gerekenden başka bir işi üstlenmemiş durumda. Dolayısıyla     bakımını yaparken ya da yeni bir takım methodlar eklerken ilgili sınıf dahilinde     hiç bir ekstra işlem yapmamış oluyoruz. Bu durumda bu sınıfın geliştirilmesinden     sorumlu olan yazılımcı kesinlikle performansı nasıl takip ettiğinden de haberdar     değil. Sizce de daha güzel bir çözüm olmadı mı? Peki bu çözümü     gerçekten nasıl uygulayabiliriz isterseniz şimdi de o konuda .Net ‘in     bize sunduğu bir kaç güzelliğe göz atalım.
Dikkat ederseniz bu durumda nesnemiz ContextBoundObject adında bir nesneden türemiş     durumda. Peki nedir bu ContextBoundObject? Bir sınıf eğer ContextBoundObject nesnesinden     türüyorsa, üzerindeki metodları stack-based çalıştırma modelinin     aksine mesajlar göndererek çalıştırmaya çalışır. Böylece     ilgili sınıfı bu nesneden türeterek yeni bir instance ı yaratıldığında ve herhangi     bir metodu çağrıldığında aradaki mesajları yakalamış olabiliriz . Bu özellik     daha çok remoting ve COM interoplarında kullanılır. Zaten dikkat ederseniz     ContextBoundObject nesnesi MarshalByRefObject nesnesinden türemiştir. Bu nesneden     türetmemiz durumunda .net çalışma zamanında ilgili nesnemiz için     ayrı bir tane context oluşturur ve bu context nesnemiz dispose olana kadar nesnemizle     birlikte hayatta kalır.
Nesnemizi ContextBoundObject nesnesinden türettikten sonra yapmamız gereken     artık aspect dediğimiz nesneleri hazırlamaktır. Bu durumda aspect dediğimiz şeyler     bizim nesne üzerine ekleyeceğimiz bazı özel attribute sınıfları olacaktır.     Böylece bunları bir sınıfa “uygulayabilir” hale gelmiş olacağız.     Bunu yapabilmek için yine .net üzerindeki ContextAttribute sınıfından     türeyen bir sınıf yapacağız. Böylece context oluşturulduğunda bir takım     özellikleri implemente edeceğimiz IContextProperty leri context üzerinde     çalışır hale getirmiş olacağız. Bunun için IContextProperty      ve IContributeObjectSink arayüzleriyle işlem yapıyor olacağız.
Aslında burada çok fazla kod yazıp kafa karıştırmak istemiyorum. Bu yüzden     kod örneğini          buradan indirebilirsiniz. Ben kısaca çalışma mantığından bahsedip     bu yazıyı tamamlayacağım;
Şu anda elimizde bir sınıfımız var ve bu sınıfımız ilk kez örneklenirken bir     context oluşturuyor. Bunu gerçekleştirirken de biz bu context in içine     bir takım context property ler ekliyoruz. Bu property lerin ekleneceğini de sınıfımızın     üzerine yazdığımız bazı attribute lar ile gerçekleştiriyoruz. Bu durumda     bunu gerçekleştirebilmek için ;
    - ContextBoundObject nesnesinden türemiş bir sınıfa,
 
    - ContextAttribute ten türemiş bir attribute sınıfına –ki böylece         context üzerine yeni özellikler kazandırabilelim,
 
    - Mesajları ilgili aspectlere gönderip çalıştırabilecek bir ContextProperty         sınıfına,
 
    - Ve sonunda mesajların gönderilmesinden önce gelen mesajları işleyip istediğimiz         aksiyonu almamızı sağlayacak olan ve bizim aspect sınıfımız olan bir MessageSinksınıfına         ihtiyacımız olacak.
 
Bu ihtiyaçları karşıladıktan sonra adım adım nasıl çalıştığını izlersek     aşağıdaki sıralamaya ulaşabiliriz,
    - Nesnenin instance ı alınırken üzerindeki attribute lar okunur.
 
    - Attribute lar nesnenin ilgili context üzerine kendi bildikleri context özelliklerini         eklerler,
 
    - Bir metodun çağrımı esnasında oluşturulan mesaj öncelikle bu property         üzerinden geçer ve o sırada GetObjectSink metodu ile birlikte ilgili         Aspect nesnemiz olan ve IMessageSink ten implemente olan nesne yaratılır.
 
    - Mesajın gönderilip, bir sonraki mesajın aktarılabilmesi için mesaj ilgili         MessageSink nesnesine gönderilir ve orada işlenir.
 
Böylece yaptığımız bu kısa işlem sonrasında hangi metodun ne zaman çağrıldığını,     izin kontrollerini vs. gibi bir takım özellikleri sınıfın içine tek     satır kod yazmadan gerçekleştirmiş oluruz. Böylece sınıfımız kendi işine     odaklanır ve sadece kendi işinden sorumlu hale gelmiş olur böylece Single Responsibility     prensibini de tam manasıyla karşılamış oluruz.
Bir sonraki yazıda görüşmek üzere.