BLOG

AOP (Aspect Oriented Programming)

30/01/2017 tarihinde Mustafa Bora ŞİMŞEK tarafından yazılmıştır.

Merhaba arkadaşlar, bu yazımda sizlere aspect oriented programming’den bahsedeceğim, çevirisiyle Cephe Yönelimli Programlama.
Her iyi programcı modüler kodlamanın ne kadar önemli olduğunu bilir. Eminim ki hepimiz, belli bir yazılımı, başkasından devralıp da geliştirmeye devam etmemiz gereken durumlarda, devraldığımız kodun hiçbir düzeni olmadığında rahatsız oluruz. Hatta zaman zaman, ‘Baştan yazsam daha az zaman alırdı.’ gibi şikayetlenenler duymuşuzdur. İşte Aspect Oriented Programming (AOP), bu konuya derman olmak amacında.

Cross-Cutting-Concerns yani Ortak Endişeler


Her kurumsal yazılımda bulunması gereken bazı özellikler vardır. Kullanıcının kim olduğunu belirleme, loglama, güvenli işlem yapma vb. AOP bu gibi hemen hemen her uygulamada var olması gereken şeyleri cross-cutting-concern diye adlandırıyor. Yazımın devamında bu cross-cutting-concern kavramını ‘ortak endişeler’ olarak kullanacağım.

Aspect Mi?

AOP, bu ortak endişelerimize aspect adı verilen çözümlerle yaklaşıyor. Aspect, ya da Türkçe’si özellik, sınıflarımıza ve içindeki metodlarımıza bazı özellikleri ekleyebilen yazılım parçalarıdır. Fakat burada dikkat edilmesi gereken bir nokta var ki, aspect’leri eklemek ya da çıkarmak için kendi inheritance(kalıtım) yapımızı düzenleme zorunluluğumuz yok. İş mantığı katmanımızı hiçbir şekilde değiştirmiyoruz. Yani bu aspect kavramına bizim işimize yarayan eklenti veya kısaca özellik diyebiliriz.
Örnek bir kod parçacığımız olsun, kod belli bir miktar parayı ‘from’ banka hesabından ‘to’ banka hesabına para aktarımı yapacak. Şöyle bir kod olmasını düşünürüz:
void transfer(from, to, amount) {
	withdraw(from, amount);			//alınacak hesaptan parayı çek
	deposit(to, amount);				//yatırılacak hesaba parayı yatır
}

Ama gerçek hayatta çözümümüz bu kadar basit olmaz, bizden beklenen para transferi işlemini şu şekilde kodlarız:
void transfer(from, to, amount) {
	if(from kişisi gerçekten o kişi mi VE to kişisi uygun bir kişi mi ) {	//doğrulama
		log.append(kişiler uygun, devam ediliyor);		//loglama
	}
	else {
		log.append(from kişisi zararlı bir yazılım! Aktarım yapılmayacak.);
		return;
	}
	if(amount > 0) {					//doğrulama
		withdraw(from, amount);
		deposit(to, amount);
		log.append(hesaptan hesaba aktarım yapılacak);
		transaction.commit();				//aktarım güvenliği
		log.append(Hesaptan hesaba aktarım yapıldı);
	}
	else {
		log.append(miktar yanlış girilmiş);
		return;
	}
}

Bu kodun asıl amacı güvenlik testi, doğrulama(validation) veya loglama yapmak değil, sadece aktarımı yapmak. Ama böyle şeyler aşağı yukarı her uygulamada zorunlu olduğu için kodlamalıyız. Projede aşağı yukarı her yerde kullanılan doğrulama, güvenlik, loglama gibi şeyleri biz de kodumuza eklemek zorunda kaldık.
Şimdi böyle bir kodu, çok da tecrübesi olmayan bir geliştiricinin devraldığını düşünelim. Geliştiriciden bazı güncellemeler yapması istenmiş olsun. İlgilenmesi gereken şey sadece hesaptan hesaba para yatırmak olacakken, bir anda kendisini loglama nasıl yapılır, miktarın mantıklı bir miktar olup olmadığı nasıl kontrol edilir, aktarımda güvenlik nasıl sağlanır gibi soruların cevabını ararken bulacak.
İlk olarak sistemimiz bakım yapılabilir bir sistemden uzak. Mesela loglama metodu değiştiğini düşünelim. Tüm projede loglama yapılan yerler tek tek bulunup yeni sisteme uyumlu olarak güncellenmek zorunda.
İkincisi modülerlik ilkelerinden biri olan iş bölümünden de uzaklaşmaktayız. İlk hesabımıza göre transfer yapması gereken metod, ekstradan validasyon, güvenlik ve loglama da yapıyor.
Üstüne bir de tüm sistem genelinde yapılması gereken bir bakım yaptığımızı düşünelim, tek tek her sınıfın güncellenmesinin ne kadar kaos yaratabileceğini tahmin edersiniz. Ne kadar modüler yazmış olursak olalım, ortak endişemizi çözmek için 1 satır bile yazıyor da olsak, bakım aşamasında proje genelinde tüm sınıflarda bu 1 satırı bulmak ve değiştirmek önemli bir sorun olacaktır.
Bu gibi sorunlara AOP güzel bir çözüm sunuyor. AOP sayesinde modülerliği tam anlamıyla sağlayabildiğimiz bir yazım şekli oluşturabiliyoruz. Ayrıntılarına yazımın devamında değineceğim.

Inversion of Control veya Yeni Adıyla Dependency Injection

Kendi yazdığımız kodları bir yapboz olarak düşünürsek, AOP aspect’lerini kendi yapbozumuza eklenebilen veya çıkarılabilen başka yapboz parçaları olarak göreceğiz. Ama tam da bu noktada IOC (inversion of control) kavramı devreye giriyor. IOC’nin Türkçe’ye bire bir çevirisi kontrolün tersyüz edilmesi.
AOP’de kontrol bizde değil, çatı, kendi yaşam döngüsünde ortak endişelerimizi hallederken gerekli noktalarda bizim yazdığımız kodu çağırıyor, yani aslında biz onu değil, o bizi yönlendiriyor. Kontrol tamamen yapılandırma dosyasında. Gerçek yaşamdan bir örnek verecek olursam: Size nasıl ulaşabilirim? Diye sorduğunuz birisinin size, ‘Siz bizi aramayın, biz ihtiyaç olduğunda sizi buluruz.’ demesi gibi.
Programın işleyişi, AOP’de, Java’nın işleyişinde değil de, aslında bir yapılandırma(configuration) dosyası üzerinden yürüyor. Sadece gereken yerlerde bizim kodlarımız çağırılıyor. Kontrolün Tersyüz Edilmesi (Inversion of Control) de bu şekilde sağlanmış oluyor.

Peki Bu Anlattıklarımı Nasıl Sağlıyor?

Genel endişelerimizin çözümünü Aspect adı verilen özel sınıflarda yazıyoruz. Mesela loglama aspect çözümümüz şu görünüme sahip olabilir:
Log:
	Log dosyasını bul
	Log dosyasını aç
	İçerisine yazılacak metini yaz
	Log dosyasını yeniden başka loglamalara hazır hale getir

Veya doğrulama yapan bir aspect çözümümüz şuna benzer bir şey olacaktır:
Validation:
	İlgili bankaya bağlan
	Bankaya hesap numarasını gönder
	Bankadan bu hesap numarasının gerçek bir hesap olup olmadığını öğren.
	Gelen cevaba göre sonuçlandır.

Bu gibi aspect örneklerini gördükten sonra aspect çözümlerinin, proje genelinde kullanılan sorunlara çözüm bulacak, bu yüzden de birçok yerde kullanılacak ‘ortak çözümler’ olduğunu anlamışsınızdır.
Bütün bu aspectleri yöneten bir Aspect Configuration (Özellik Ayarları) belgemiz var ve bu belgede de hangi özelliği nerede / ne zaman çağıracağımızı belirteceğiz.
Örneğin yukarıdaki transfer fonksiyonu için ayar komutlarımız şunun gibi bir hal alacak:
before transfer:					//transfer metodu öncesinde çağırılacak aspect’ler
log aspect, validation aspect, security aspect	//tek tek çağırılacak özellik isimleri
after transfer					//transfer metodundan sonra çağırılacaklar
log aspect, transaction aspect

İş mantığımızın ve ortak endişelerimizin çözümleri farklı yerlerde yazılacak demiştik ya, işte bu aspect configuration belgemiz sayesinde, geliştiriciler sadece kendilerine verilen soruna çözüm bulacak ve başka şeylerden soyutlanmış olacak. Kendilerine verilen görevi, odaklandıkları şeyden başka hiç birşeyi düşünmeden aşağıdaki şekilde halledebilecekler:
void transfer(from, to, amount) {
	withdraw(from, amount);			//alınacak hesaptan parayı çek
	deposit(to, amount);				//yatırılacak hesaba parayı yatır
}

Ayar dosyası hakkında aklınıza şöyle bir soru gelebilir: Acaba her metoda böyle tek tek öncesinde ve sonrasında çalışacakları yazarsak o yapılandırma dosyası ne hale gelir? Tek tek her metoda bu ortak endişe (cross-cutting-concern) satırlarını yazmak ayar belgemizi şişirmez mi? Merak etmeyin AOP bu sıkıntıyı da ele almış: sınıflarımıza toplu olarak aspect çağırmak mümkün. Böylece ayar dosyalarımızın şişmesi engellenmiş oluyor.
Örnek AOP Çatıları:
Son olarak AOP’nin teorik bir kavram olduğunu söylemekte yarar görüyorum. AOP bir kitap veya dergi gibi bir şey. Yazı ve gösterimsel tablolardan oluşuyor. AOP’yi kullanmak için somut gerçekleştirimlerini kullanmamız gerekiyor. AOP’yi projesinde kullanmak isteyenler, Java dünyasında Spring ve AspectJ çatılarını kullanabilir. .NET dünyasında da Compile-Time-Weaving ve Run-Time-Weaving isminde yapılarla AOP’yi kullanabiliriz. PostSharp, LOOM.NET, Aspect.NET, DotSpect, Aspect# gibi gerçekleştirimler .NET dünyasında mevcut.

Mustafa Bora ŞİMŞEK

Bilgisayar Mühendisi

Yorumlar

Deneme

30/01/2017

deneme

Cevapla

Yorum Yaz