
Yeni yazılarımı www.developersland.net sitesinde bulabilirsiniz.
Software Developer
Veritabanıyla çok fazla alış veriş içerisinde olan uygulamalar yoğun veritabanı trafiği nedeniyle performans düşüklüğü gösterirler. Bu sorunu çözmek için en iyi çözüm yolu cache algortimaları kullanmaktır.
Cache daha önce yaptığımız sorgular sonucu ulaşılan verileri tutmaktadır genel olarak. Böylece aynı veriye tekrar ulaşmak istediğimizde bu veri cache’den getirilerek bu veriye daha hızlı ulaşmamızı sağlanır. Ancak farklı bir veriye ulaşmak istediğimizde tekrar veritabanıyla bağlantı kurulmak zorundadır.
Hibernate iki farklı cache sunmaktadır. Bunlardan birincisi 1. Seviyede cache diğeri ise 2. Dereceden cachedir.
Birinci Seviye Cache: Session nesnesiyle ilişkilidir. Hibernate default olarak bu cache yöntemini kullanır.Bir transection’un birden fazla kez gerçekleştirilmesinin önüne geçer. Kullanıcının yaptığı değişiklikleri her seferinde veritabanına bağlanıp gerçekleştirmek yerine bütün değişikliklertransaction sonunda gerçekleşir.
İkinci Seviye Cache : Session Factory Nesnesiyle ilişkilidir.Veritabanına bağlantı sonucu getirilen veriler tek kullanıcı için geçerli olmak yerine tüm kullanıcılar için geçerli oluyor. Dolayısıyla cache’de bulunan bilgiler için database transaction gerçekleştirmemiz gerekmez.Burada query level cache de kullanılabilirdi.
Hibernate 4 farklı cache yöntemini destekler. Bunlar;
EHCache (Easy Hibernate Cache)Bu yöntem güçlüdür(powerful). Esnektir. Read only ve read write hafıza desteklidir. Hafıza bazlı ve disk bazlı cache destekler. Gruplama için temel destek sağlar (JavaGroups veya JMS ile).
Gruplama bazlı cache’dir. Read only ve nonstrict read/write cache desteklidir. Okuma işlemlerinin yazma işlemlerinden daha fazla olduğu uygulamalar için daha iyidir.
Güçlü bir yöntem. transaction-capable caching mimarisine ihtiyaç duyduğumuz durumlar için çok etkilidir.
Hiçbir cache sağlayıcısı tüm cache özelliklerini bir arada sağlamaz.
Read-Only: Okumanın çok sık olduğu ama hiç update yapılmadığı durumlar için çok etkin bir özellik. Bu özellik çok basit. Gruplamayı kullanmak açısından çok güvenli
Read-Write: Verilerimizi update etmek için bu özellikten faydalanırız.Bu özelliği gruplama için kullanmak istersek locking desteğinin olması gerekmektedir.
Nonstrict Read-Write: Verilerimizi çok sık update ettiğiz durumlarda bu özellik çok işimize yaramaktadır.
Kaynak: http://www.javabeat.net/articles/37-introduction-to-hibernate-caching-1.html
Stajda nesne yönelimli programlama tekniğini daha iyi anlamam için head first yayınlarının object oriented analysis and design kitabını okumam istendi. Kitap daha önce karşılaşmadığım bir anlatım tekniğiyle yazılmıştı. Kitapta bolca görsel öğeler kullanılarak anlatılacak konular okuyucuyu sıkmayacak bir şekilde aktarılmaya çalışılmıştır. Kitapta nesne yönelimli programlamanın önemini vurgulamak amacıyla çeşitli problemler tanımlanıyor. Problemler ilk başta nesne yönelimli programlama teknikleri kullanmadan da çözülebiliyor olmasına rağmen daha sonradan programa yeni bir şeyler eklenmek istendiğinde nesne yönelimli programlamanın önemi, yenilenebilirlilik kavramı açısından önemi daha iyi anlaşılıyordu.Bundan ilave kitap boyunca sürekli kullanıcıya sorular sorularak hem kullanıcılarla etkili iletişim kurulmuş hem de okuyucunun dikkati canlı tutulmaya çalışılmıştır.Kitap boyunca sürekli iyi bir yazılımın nasıl olması gerektiği hakkında çeşitli bilgiler verilimiştir. İyi bir yazılımın iyi tasarlanmış,iyi kodlanmış kolayca değiştirilebilir, kolayca yeniden kullanılabilir ve kolayca yenilikler eklenebilir olması gerektiği kitap boyunca üstüne basıla basıla çeşitli örnek problemler de ortaya atılarak vurgulanmıştır. Kitapta vurgulanan bir diğer önemli nokta iyi yazılımın müşterinin isteğine uygun yapılmış olması gerektiğidir. Yazılım tasarlarken nesne yönelimli programlama tekniğini düzgün kullanmamızın yazılımı sonradan müşterinin yeni isteklerine göre değiştirmemiz istendiğinde kolayca değiştirebileceğimizi göstermek amacıyla örnek bir problem tanımlanmıştır. Bu problemde ailenin şöyle bir sorunu vardır; köpekleri her gece havlayarak dışarı çıkmak istemektedir ve aile üyeleri gece yataktan kalkarak kapıyı açmaktan bıkmıştır.Köpek havladığı zaman dışarı çıkmasına izin veren bir kapı sisteminin tasarlanması istenmiştir. Bunun için yazılımcılar aile üyelerinin gece yataktan kalkarak köpeğe kapıyı açmalarının karşısını almak amacıyla uzaktan kumanda sistemi kullanılarak havlama sesi duyulduğunda kapıyı uzaktan kumandayla açabilmeleri sağlanmış ve müşteriler gece yataktan kalkmak zorunda kalmadıkları için programdan memnun kalmışlardır. Belli bir süre programı kullandıktan sonra aile üyeleri yeni isteklerini yazılımcılarına bildirmişlerdir. Bu sefer müşteriler sürekli gece uyanarak uzaktan kumandaya basmaktan bıktıklarını belirtmiş ve havlama sesine duyarlı otomatik kapı tasarlanmasını istemişlerdir. Yazılıma köpek sesi tanıyıcı eklendikten sonra müşterilerin istediği yazılım onlara sunulmuştur. Müşteriler mutlu bir şekilde yazılımın yeni versiyonunu kullandıktan sonra yeni bir istekle tekrar yazılımcılara müracaat etmişlerdir. Bu sefer müşteriler köpek sesi tanıyıcının tüm köpeklerin sesine tepki vererek açıldığından yakınmış ve köpek sesi tanıyıcısının sadece onların köpeğinin sesine duyarlı olmasını istemişlerdir. Yazılımcılar bu sorunu da çözerek köpek sesi tanıyıcısının sadece kendi köpeklerinin sesine duyarlı olmasını sağlamışlardır. Kitapta tüm bu değişikliklerin nesne yönelimli programlama tekniği kullanılarak ne kadar rahat yapılabildiği vurgulanmış ve kod üzerinde gösterilmiştir.
Hakkımızda Sayfası: Kullanıcı bu sayfada oyunun geliştiricisi hakkında bilgiye ulaşabilir. Geliştiriciyle iletişime geçerek yorumlarını iletebilir. Bu sayfada ayrıca programın ilerki versiyonlarında yapılması planlanan değişiklikler yer almaktadır.
Programın maç yaptırma algoritması şu şekilde çalışmaktadır: Tüm oyuncuların bir savunma ve hucum gücü değeri bulunmaktadır. Takımların sahaya sürülen ilk on bir oyuncusunun hucum ve defans değerlerinin aritmetik ortamaları takımın hücum ve defans değeri olmaktadır. Bir takımın hücum değeri diğer takımın defans gücü değerinden ne kadar yüksekse takımın gol atma olasılığı artmaktadır. Ayrıca kendi sahasında oynayan takımların rakip takıma göre biraz daha avantajlı olması sağlanmaktadır. Rakibine göre göstericileri çok düşük olan takımların bile gerçek hayatta kazanma olasılığı bulunduğu göz önüne alınarak az olasılıkla da olsa bu takımlara bir veya iki gol atma şansı tanınmıştır.
JPA'da @version tagı veritabanına birden çok kişinin aynı anda erişmesi durumunda optimistic locking mekanizmasının kullanılmasını sağlar. Bir tabloda sadece bir tane version alanı olabilir. Normal şartlarda veritabanlarında optimistic veya pessimistic locking kullanılmadığında birden çok kişi aynı anda veritabanina eriştiğinde birbirini ezme prensibi kullanılıyor yani en son yapılan değişiklik daha önceki değişiklikleri eziyor. Arkadaşımla yaptığımız denemelerde aynı anda veritabanina eriştikten sonra önce ben değişiklik yapıp commitledim daha sonra arkadaşım değişiklik yapıp commitledi ve onun yaptığı değişiklik benim yaptığım değişikliği ezdi. Version alanı kullanarak yaptığımız denemelerde ise ben veritabanina eriştim ve bazı değişiklikler yaptım ve commitlemedim, daha sonra arkadaşım tabloya erişerek bazı değişiklikler yapmaya çalıştı ama sistem buna ben commit yapmadan izin vermedi.Sistem sadece veritabaninin o anki durumunu görmesine izin verdi.
Version alanının kullanılma mantığı şu şekildedir, kullanıcı veritabanında bir alana eriştiğinde version alanındaki değer kontrol ediliyor sistem tarafından ve kullanıcı herhangi bir değişikli yaptığı anda version alanındaki değer bir kez daha kontrol ediliyor ve eğer bu değer sisteme ilk girildiğinde kontrol edilen değere eşitse (bu değer veritabanında herhangi bir değişiklik yapıldığı zaman değişiyor.) o zaman kullanıcının değişiklik yapmasına izin veriliyor. Version alanındaki değer her değişiklik sonrası bir arttırılıyor.Eğer alana eriştiğimiz andaki version değeriyle değişiklik yaptığımız andaki version değeri farklıysa bu başka birinin bizim sisteme giriş yaptığımız ve değişiklik yaptığımız zaman arasında o alanda başka bir değişiklik yaptığını gösterir ve değişikli yapmamıza izin vermez.Version alanı timestamp( en son değişiklik yapılma tarihi ) veya numeric türde değişken kullanılabilir ama Oracle numeric değişkenlerin kullanılmasını öneriyor.çünkü time stamp değişkenler veritabanında %100 kesin değerleriyle tutulamaz ve neredeyse nano saniye farkla yapılan farklı commitler arasındaki farkı algılayamayabilir.
Not: Arkadaşım Anıl Mercana yardımlarından dolayı teşekkür ediyorum.
Bu sorunu çözecek en iyi çözem yolu çoklu kalıtımdır! Ancak Java çoklu kalıtım çok fazla karışıklık yarattığından dolayı desteklemez. Peki bu durumda ne yapmamız gerekiyor?
İşte bu durumda imdadımıza Interface yetişiyor çünkü javada bir sınıfı birden çok abstract sınıftan extend edemeyiz ancak birçok interfaceden implemente edebiliriz. Ayrıca bir sınıfı bir abstract sınıftan ve bir veya birden çok interfaceden implemente etme şansımız da vardır.
Sorunumuza dönersek, Pet adında bir interface yazarız ve bu interface’in içerisinde sevimliOl() oyna() gibi gövdesi boş metodlar tanımlarız(interfacelerde içi dolu metodlar tanımlayamıyoruz sadece içi boş metodlar tanımlayabiliriz. İşte bu yüzden java abstract sınıflarla çoklu kalıtım yapmamıza izin vermediği halde interfacelerle çoklu kalıtım yapmamıza imkan sağlıyor. ). Daha sonra kedi ve köpek gibi evcil hayvanları hayvan sınıfından extend ve pet interface’den implement ederiz. Böylece nesne yönelimli programlamanın polimorfizm gibi çok değerli özelliklerinden faydalanabileceğiz.
Nesne Yönelimli Programlamanın Temel İlkeleri
Veri Gizleme
Sınıf ve Nesne
Yordamlar ve İletiler
Çokbiçimlilik
Arayüz Oluşturma
Türetme ve Çokbiçimlilik
Erişim denetimi
Liskov Yerine Geçme İlkesi - LSP (Liskov Substitution Principle)
Açık Kapalı İlkesi - OCP (Open-Closed Principle)
Bağımlılık Ters Çevirme İlkesi - DIP (Dependency Inversion Principle)
Arayüz Ayırma İlkesi – ISP (The Interface Segregation Principle)
Sürüm Yeniden Kullanımı Eşdeğerlik İlkesi – REP (The Release Reuse Equivalency Principle)
Ortak Kapatma İlkesi – CCP (The Common Closure Principle)
Ortak Yeniden Kullanım İlkesi – CRP (The Common Reuse Principle)
Çevrimsiz Bağımlılık İlkesi – (ADP) (The Acyclic Dependencies Principle)
Kararlı Soyutlamalar İlkesi – SAP (The Stable Abstractions Principle)
Kararlı Bağımlılıklar İlkesi – (SDP) (The Stable Dependencies Principle)
Genel olarak tasarım kalıpları programlama dillerinden bağımsız olarak tanımlansalar da, nesneye yönelimli programlama dillerine uygun tasarım kalıpları daha çok bilinir. Bu kalıplar, nesneler ve sınıflar arasındaki ilişkileri ve etkilişimleri gösterirler. Programcı bir tasarım kalıbını elindeki soruna bakarak özelleştirip kullanabilir.
Bu kalıpları uygulayabilmek için kulanılması gereken prensipler
Açık Kapalı Prensibi (Open Closed Principle)
Yazılmış bir modülün genişlemeye açık ama değişikliğe kapalı olması gerekliliğini vurgular. Yani yazılan kod üzerine bir eklenti yapılmak istendiğinde önceden yazılmış kodlarda değişiklik gerekliliği olmamalıdır. Gerekli eklentiler sadece eklenti için gereken yeni özelliğin kodlanması ile sonuçlanmalıdır.
Liskov Ayrışabilme Prensibi (Liskov Substitution Principle)
"Türemiş sınıflar, türedikleri temel sınıfların yerini herhangi bir problem çıkmadan alabilmelidir." prensibidir. Yani temel sınıftan yaratılmış bir nesneyi kullanan bir fonksiyon, temel sınıf yerine bu temel sınıftan türemiş bir sınıftan yaratılmış bir nesneyi de aynı yerde kullanabilmelidir.
Bağımlılığın Azaltılması Prensibi (Dependency Inversion Principle)
Kullanıcı ile sınıflar arasındaki ilişkinin olabildiğince soyutlanmış yapılar üzerinden yapılmasını önerir. Yani tasarımda ilişkilerin gerçek sınıflardan türemiş nesneler ile değil, ilgili arayüzler(interface) ve soyut sınıflar kullanılarak gerçeklenmesi gerekir.
Tek Sorumluluk Prensibi (Single Responsibility Principle)
"Bir sınıf sadece tek bir sorumluluğu yerine getirmelidir ve yerine getirdiği sorumluluğu iyi yapmalıdır." prensibidir. Her sınıf sadece kendisi ile ilgili tek bir sorumluluğu yerine getirir. Her sınıfın sorumluluğu farklı olduğu zaman, değişmesi için tek bir sebep olur,o da ihtiyaçların değişmesidir. Sınıfların birden fazla sorumluluğunun olması bağımlılığın artmasına neden olur.
Arayüz Ayırma Prensibi (Interface Segregation Principle)
Kullanılacak methodların ve özelliklerin gruplandırılarak her biri ayrı işlevi tanımlayan farklı arayüzlere bölünmesini savunur. Sonuç olarak, bir sınıfın kullanmadığı method ve özellikler programın içeriğine alınmamış olur.
Tasarım kalıplarını inceleyecek olursak temelde 3 ana gruba ayırabiliriz:
Yaratılış Kalıpları (Creational Patterns): Nesnelerin nasıl yaratılacağı hakkında alternatifler sunar. Farklı durumlarda yaratılması gereken nesnelere karar verir.
Yapısal Kalıplar (Structural Patterns): Nesneleri ve sınıfları birleştirerek karmaşık kullanıcı arayüzleri gibi daha geniş yapılar oluşturur.
Davranışsal Kalıplar (Behavioral Patterns): Nesne grupları arasındaki iletişimin tanımlanmasında kullanılır ve daha karmaşık programlarda akış kontrolünü sağlar. Nesnelere işlevsel sorumluluklarını atar.
Tasarım kalıplarının listesi:
Davranışsal Kalıplar
Chain of responsibility
Command
Interpreter
Iterator
Mediator
Memento
Observer
State
Strategy
Template method
Visitor
Yapısal Kalıplar
Adapter
Bridge
Composite
Decorator
Facade
Flyweight
Proxy
Yaratılış Kalıpları
Abstract Factory
Builder
Factory Method
Prototype
Javada metodları static yaptığımız zaman bu bize instance yaratmadan o metodu kullanmamızı sağlar. Aynı şekilde bu değişkenler için de geçerli, sınıf içinde bir değişkenin static olması bizim bu değişkene instance yaratmadan erişmemizi sağlar.
Örneğin:
public class Araba{
public static int a;
public int b;
}
Araba.a //diyerek a değerine erişebiliriz ancak aynı şekilde Araba.b diyerek b değişkenine erişmemiz mümkün değil. Bunun için bir instance yaratmamız gereklidir.
Araba bmw=new Araba();
bmw.b //diyerek b değişkenine erişebiliriz.
Static metodlara örnek olarak da aşağıdaki örneği verebiliriz
public class Araba {
static void hiz1()
{
System.out.println("hiz1 kullanılıyor");
}
void hiz2()
{
System.out.println("hiz2 kullanılıyor");
}
}
//Main içerisinde Araba.hiz1() diyerek hiz1 metoduna instance yaratmadan erişebiliriz ancak hiz2'ye aynı şekilde erişmemiz mümkün değil.
İnner classlar için durum biraz farklıdır. Bir inner class'ı static yapsak da yapmasak da bu class'a instance yaratmadan erişebiliriz. Örneğin:
public class Araba {
public class Teker1{
}
public static class Teker2{
}
}
Araba.Teker1 ve Araba.Teker2 diyerek her iki class'a erişebiliriz.
Not: Static metodların içerisinde static olmayan değişkenler kullanamayız. Bunun nedeni static olarak tanımlanan herşeyin ilk olarak oluşturulmasıdır. Bu nedenle static bir metodun içerisinde static olmayan bir değişken kullanmaya çalıştığımız zaman o değişken henüz oluşturulmadığından derleyici hata mesajı verecektir.
Java da interface kullanmamızın en büyük nedeni interfaceden türetilecek sınıflar için bir standart(zorunluluk da diyebiliriz) belirlemektir. Örneğin Telefon adında bir interface üretiyoruz ve daha sonra interfaceden sınıflar implement ediyoruz. Bütün bu sınıfların bazı ortak özelliklere sahip olmasını bir zorunluluk haline getirebiliyoruz. Örneğin interfacede arama() adında bir metod yazıyoruz ( içini boş bırakmak zorundayız interfacelerde) ve türetilecek bütün sınıflar kendi özelliklerine uygun olarak arama() metodunu dolduruyor. Böylece bütün telefonların arama özelliğine sahip olmasını bir standart haline getirmiş oluyoruz.
Veritabanıyla ilgili bir örnek de vermek istiyorum. Örneğin çok geniş bir yazılım yazdığımızı varsayalım. Kullanıcıya bir veritabanı seçtirip buna göre işlemler yaptırmak istiyoruz. O zaman kodumuzda şöyle satırlar olacaktır:
if(secim=="MySQL")
Veritabani vt =new MySQL();
if(secim=="ORACLE")
IVeritabani vt = new ORACLE();
.... bir kaç tane daha olabilir...
Daha sonra.. vt.baglan() diyerek baglantimizi sagliyoruz.
Biz daha önce veritabanlarini tek bir Veritabani adlı interfaceden implement etmiştik ve orada baglan() adinda bir metod tanımlayarak içini boş bırakmnıştık ve tüm veritabanlarinin içine kendi özelliklerine uygun olarak baglan() metodunu farklı şekillerde tanımlamıştık.
Böylece hepsinde baglan() metodunun olmasını garanti etmiştik.
Eğer böyle yapmasaydık mesela mySQL için vt.connect() yazmamiz gerekirdi ve bu kodumuzda gereksiz uzamalara neden olabilirdi. Interfaceyle bunun önüne geçmiş oluyoruz.