Open Closed Principle (OCP) – Açık Kapalı Tasarım Prensibi

Yazılım disiplininde değişmeyen birşey varsa o da değişikliğin kendisidir. Birçok program müşteri gereksinimleri doğrultusunda ilk sürümden sonra değişikliğe uğrar. Bu doğal bir süreçtir ve müşteri programı kullandıkça ya yeni gereksinimlerini ya da mevcut fonksiyonlar üzerinde adaptasyonları gerekçe göstererek programın değiştirilmesini talep edecektir.

Tanınmış yazılım ustalarından Ivar Jacobson bu konuda şöyle bir açıklamada bulunmuştur:

“All systems change during their life cycles. This must be born in mind when developing systems are excepted to last longer than the first version.”

Şu şekilde tercüme edilebilir:

“Her program görev süresince değişikliğe uğrar. Bu ilk sürümden ötesi düşünülen programların yazılımında göz önünde bulundurulmalıdır.”

Değişiklik kaçınılmaz olduğuna göre, bir programı gelecekte yapılması gereken tüm değişiklikleri göz önünde bulundurarak, şimdiden buna hazır bir şekilde geliştirmek mümkün müdür? Öncelikle şunu belirtelim ki gelecekte meydana gelecek değişikliklerin hepsini kestirmemiz mümkün değildir. Ayrıca çevik süreçlerde ilerde belki kullanılabileceğini düşündüğümüz fonksiyonların implementasyonu kesinlikle tabudur (istenilmeyen bir durum anlamında). Bu durum bizi programcı olarak gelecekteki değişiklikleri göz önünde bulundurarak, bir nevi hazırlık yapmamızı engeller. Çevik süreç sadece müşteri tarafından dile getirilmiş, kullanıcı hikayesi haline dönüştürülmüş ve müşteri tarafından öncelik sırası belirlenmiş gereksinimleri göz önünde bulundurur ve implemente eder. Kısacası çevik süreç geleceği düşünmez ve şimdi kendisinden beklenenleri yerine getirir. Böylece müşteri tarafından kabul görmeyecek bir sistemin oluşması engellenmiş olur.

Çevik süreç büyük çapta bir tasarım hazırlığı ile start almaz. Daha ziyade mümkün olan en basit şekilde yazılıma başlanır. Her iterasyon başlangıcında implemente edilmesi gereken kullanıcı hikayeleri seçildikten sonra, mevcut yapının yeni gereksinimlere cevap verip, veremeyeceği incelenir. Büyük bir ihtimalle mevcut yapı yeni kullanıcı hikayelerinin implementasyonu için yeterli olmayacaktır. Bu durumda programcı ekip refactoring yöntemleriyle programın yapısını yeni gereksinimleri kabul edecek şekilde modifike eder. Bu esnada tasarım yapısal değişikliğe uğrar. Bu gerekli bir işlemdir ve yapılmak zorundadır, aksi taktirde bir sonraki iterasyon beraberinde getirdiği değişikliklerle programcı ekibini yazılım esnasında zorlayacaktır.

Çevik süreç gelecekte olabilecek değişikleri göz önünde bulundurmadığına göre, programı bu değişikliklerin olumsuz yan etkilerine karşı nasıl koruyabiliriz? Bunun yolu her zaman olduğu gibi yazılım esnasında uygun tasarım prensiplerini uygulamaktan geçmektedir. Eğer çevik süreç bize gelecekte olabilecek değişikliklere karşı destek sağlamıyorsa, bizimde tedbir alarak çevik süreci, çevik prensiplere ters düşmeden takviye etmemiz gerekiyor. Çevik süreci, çevik tasarım prensiplerini kullanarak istediğimiz bir yapıda, bakımı ve geliştirilmesi kolay program yazılımına destek verecek şekilde takviye edebiliriz.

Tekrar bölüm başında sorduğum soruya dönelim ve sorunun cevabını bulmaya çalışalım. Şu şekilde bir soru sormuştum: “Değişiklik kaçınılmaz olduğuna göre, bir programı gelecekte yapılması gereken tüm değişiklikleri göz önünde bulundurarak, şimdiden buna hazır bir şekilde geliştirmek mümkün müdür?” Bu mümkün değil demiştik. Lakin esnek bir tasarım oluşturarak önü açık ve gelecek korkusu olmayan bir program yapısı oluşturabiliriz. Bunu gerçekleştirmek için kullanabileceğimiz prensiplerin başında Open Closed – Açık Kapalı prensibi (OCP) gelmektedir. Bertrand Meyer tarafından geliştirilen bu prensip kısaca şöyle açıklanabilir:

“Programlar geliştirilmeye açık ama değiştirilmeye kapalı olmalıdır.”

Programı geliştirmek, programa yeni bir davranış biçimi eklemek anlamına gelmektedir. OCP ye göre programlar geliştirmeye açık olmalıdır, yani programı oluşturan modüller yeni davranış biçimlerini sergileyecek şekilde genişletilebilmelidirler. Bir modüle yeni bir davranış biçimi kazandırılarak düşünülen değişiklik sağlanır. Bu yeni kod yazılarak gerçekleştirilir ( bu yüzden bu işleme değiştirme değil, genişletme denir), mevcut kodu değiştirerek değil! Eğer kendinizi bir müşteri gereksinimini mevcut kod üzerinde değişiklik yaparken bulursanız, biliniz ki OCP prensibine ters düşüyorsunuz. Kod üzerinde yapılan değişiklik, bir sonraki gereksinimlerinde ayni şekilde implemente edilmesini zorunlu kılacaktır. Bu durum, kodun zaman içinde içinden çıkılmaz ve çok karmaşık bir yapıya dönüşmesini çabuklaştırır.

OCP prensibinin nasıl uygulanabileceğini bir önceki bölümde yer alan RemoteControl – TV örneği üzerinde inceleyelim.

Resim 1 de görüldüğü gibi RemoteControl sınıfı TV sınıfını kullanarak işlevini yerine getirmektedir. Eğer RemoteControl sınıfını TV haricinde başka bir aleti kontrol etmek için kullanmak istersek, örneğin CDPlayer Resim 2 deki gibi değişiklik yapmamız gerekebilir. Bu noktada esnek bağ prensibini unutarak, resim 2 de yer alan çözümün bizim için yeterli olduğunu düşünelim.

[source language=’java’]
package org.cevikjava.design.ocp;

/**
* TV ve CDPlayer sınıflarından
* nesneleri kontrol eder.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{

/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on(Object obj)
{
if(obj instanceof TV)
{
((TV)obj).tvOn();
}
else if(obj instanceof CDPlayer)
{
((CDPlayer)obj).cdOn();
}
}

/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off(Object obj)
{
if(obj instanceof TV)
{
((TV)obj).tvOff();
}
else if(obj instanceof CDPlayer)
{
((CDPlayer)obj).cdOff();
}

}
}
[/source]

Bu şekilde oluşturulan bir tasarım OCP prensibine ters düşmektedir, çünkü her yeni eklenen alet için on() ve off() metotlarında değişiklik yapmamız gerekmektedir. OCP böyle bir modifikasyonu kabul etmez. OCP ye göre mevcut çalışır kod kesinlikle değiştirilmemelidir. Onlarca aletin bulunduğu bir sistemde on() ve off() metotlarının ne kadar kontrol edilemez ve bakımı zor bir yapıya bürüneceği çok net olarak bu örnekte görülmektedir.

Bunun yanı sıra RemoteControl sınıfı TV ve CDPlayer gibi sınıflara bağımlı kalacak ve başka bir alanda kullanılması mümkün olmayacaktır. TV ve CDPlayer sınıflar üzerinde yapılan tüm değişiklikler RemoteControl sınıfını doğrudan etkileyecek ve yapısal değişikliğe sebep olacaktır.

Resim 3 de yer alan çözüm OCP ye uygun yapıdadır, çünkü kod üzerinde değişiklik yapmadan programa yeni davranışlar eklemek mümkündür. OCP prensibi, esnek bağ prensibi kullanılarak uygulanabilir. OCP ye uygun RemoteControl sınıfının yapısı şu şekilde olmalıdır:

[source language=’java’]
package org.cevikjava.design.loosecoupling.design;

/**
* RemotControlInterface sınıfını
* implemente eden sınıfları
* kontrol edebilen sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Delegasyon islemi için RemoteControlInterface
* tipinde bir sınıf degiskeni tanimliyoruz.
* Tüm islemler bu nesnenin metodlarina
* delege edilir.
*/
private RemoteControlInterface remote;

/**
* Sinif konstuktörü. Bir nesne oluşturma islemi
* esnasında kullanilacak RemoteControlInterface
* implementasyonu parametre olarak verilir.
*
* @param _remote RemoteControlInterface
*/
public RemoteControl(RemoteControlInterface _remote)
{
this.remote = _remote;
}

/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on()
{
remote.on();
}

/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off()
{
remote.off();
}

}

[/source]

on() ve off() metotları sadece RemoteControlInterface tipinde olan bir sınıf değişkeni üzerinde işlem yapmaktadır. Bu sayede if/else yapısı kullanmadan RemoteControlInterface sınıfını implemente etmiş herhangi bir alet üzerinde gerekli işlem yapılabilmektedir.

Bu örnekte on() ve off() metotları değişikliğe kapalı ve tüm program geliştirmeye açıktır, çünkü RemoteControlInterface interface sınıfını implemente ederek sisteme yeni aletleri eklemek mümkündür. Sisteme eklediğimiz her alet için on() ve off() metotları üzerinde değişiklik yapmak zorunluluğu ortadan kalkmaktadır. Uygulanan OCP ve esnek bağ prensibi ile RemoteControl başka bir alanda her tip aleti kontrol edebilecek şekilde kullanılır hale gelmiştir.

Stratejik Kapama (Strategic Closure)

Ne yazık ki bir yazılım sistemini OCP prensibini uygulayarak %100 değişikliklere karşı korumamız imkansızdır. OCP ye uygun olan bir metot müşterinin yeni istekleri doğrultusunda OCP’ye uygun olmayan bir hale gelebilir. Programcı metodu ilk implemente ettiği zaman, gelecekte olabilecek değişiklikler hakkında fikir sahibi olmayabilir. Metot OCP uyumlu implemente edilmiş olsa bile, bu metodun her zaman OCP uyumlu kalabileceği anlamına gelmez.

Eğer kapama tam sağlanamıyorsa, kapamanın stratejik olarak implemente edilmesi gerekir.
Programcı implementasyon öncesi meydana gelebilecek değişiklikleri kestirerek, implemente ettiği metotların kapalılık oranını yükseltmelidir. Bu tecrübe gerektiren stratejik bir karardır.

Programcı her zaman ne gibi değişikliklerin olabileceğini kestiremeyebilir. Bu durumda konu hakkında araştırma yaparak, oluşabilecek değişiklikleri tespit edebilir. Eğer olabilecek değişikliklerin tespiti mümkün değilse, beklenen değişiklikler meydana gelene kadar beklenir ve implementasyon yeni değişiklikleri de yansıtacak şekilde OCP uyumlu hale getirilir.

Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.

  Open Closed Principle (OCP) (66,2 KiB, 6.201 yükleme)


EOF (End of Fun)
Özcan Acar

Loose Coupling (LC) – Esnek Bağ Tasarım Prensibi

Bir program bünyesinde, tanımlanan görevlerin yerine getirilebilmesi için birden fazla nesne görev alır. Bu nesneler birbirlerinin sundukları hizmetlerden faydalanarak kendi görevlerini yerine getirirler. Bu durumda nesneler arası bağımlılıklar oluşur. Bir nesne kullandığı diğer bir nesne hakkında ne kadar fazla detay bilgiye sahip ise, o nesneye olan bağımlılığı o oranda artar. Oluşan her bağımlılık bir sınıf için dolaylı olarak yapısal değiştirilme rizikosunu artırır, çünkü bağımlı olduğu sınıf üzerinde yapılan her değişiklik kendi yapısında değişikliğe neden olacaktır. Bu durum programın genel olarak kırılgan bir hale gelmesini kolaylaştıracaktır.

Buradan “Eğer bağımlılık varsa, sorun var, bu yüzden bağımlılıkların ortadan kaldırılması gerekmektedir” sonucunu çıkartabiliriz. Nesneye yönelik tarzda tasarlanmış edilmiş bir program içinde bağımlılıkları ortadan kaldırmak imkansızdır, çünkü nesnelerin olduğu yerde interaksiyon ve bağımlılık olmak zorundadır. Bağımlılıkları ortadan kaldıramıyorsak, o zaman onları kontrol altına almamız işimizi kolaylaştıracaktır. Eğer bana soracak olursanız, yazılım disiplininin özü de burada saklıdır: Yazılımcı olarak kullandığımız tüm metotların temelinde bağımlılıkların kontrolü ve yönetilmesi yatmaktadır. İyi bir tasarım oluşturmak için sarf ettiğimiz efor, bağımlılıklara hükmetmek isteyişimizden kaynaklanmaktadır, yani iyi bir tasarım , kontrol edilebilir bağımlılıkları beraberinde getirdiği için iyidir, kendimizi programcı olarak iyi hissetmemizi sağladığı için değil! Biliyoruz ki kötü bir tasarım nesneler arası yüksek derecede bağımlılığa sebep vereceği için programcı olarak hayatımızı zorlaştıracaktır. Bu yüzden sahip olduğumuz tüm teknik yeteneklerimizle bağımlılığa karşı bir savaş veririz. Onu yenmemiz mümkün olmasa da, bize zarar vermeyecek şekilde kontrol altına almamız mümkündür.

Anladığımız kadarıyla bağımlılıkları mantıklı bir çerçevede ortadan kaldırmamız imkansız! Peki bağımlılıkları nasıl kontrol altına alabiliriz? Esnek bağımlılıklar oluşturarak! Esnek bağımlılık oluşturmak demek, nesneler arası bağların oluşmasına izin vermek, ama sınıflar üzerinde yapılan yapısal değişikliklerin bağımlı sınıflar üzerinde yapısal değişikliğe sebep vermesini engellemek demektir. Bunu bir örnek vererek açıklayalım.

RemoteControl (tv uzaktan kumanda aleti) ve TV (televizyon) sınıflarının arasında tek yönlü (uni directional) bir bağ oluşmuştur, çünkü bir RemoteControl nesnesi görevini yerine getirebilmek için bir TV nesnesine ihtiyaç duymaktadır. Nesneler arası bağımlılıkların yönü vardır. Resim 1 de bu bağımlılığın yönünün RemoteControl sınıfından TV sınıfına doğru olduğunu görmekteyiz. Bu tek yönlü bir bağdır ve RemoteControl sınıfı bünyesinde TV tipinde bir sınıf değişkeni barındırarak bağı oluşturur. Sınıflar arası bağlar karşılıklı da (bi directional) olabilir. İki taraflı bağlarda sınıflar, bir sınıf değişkeni aracılığıyla karşılıklı olarak birbirlerine işaret ederler. RemoteControl nesnesi bir TV nesnesi olmadığı sürece işe yaramaz. Bu yüzden bu iki sınıf arasında direk bağlantı oluşturulmuştur. Böyle bir bağımlılık aşağıdaki sorunların oluşmasına sebep vermektedir:

  • RemoteControl nesnesi, bir TV nesnesi olmadığı sürece kendi görevini yerine getiremez, bu yüzden tek başına bir işe yaramaz. Mutlaka ve mutlaka, var olabilmesi için bir TV nesnesine ihtiyaç duymaktadır. Bu durumda RemoteControl sınıfını başka bir alanda kullanmak istediğimizde TV sınıfını da yanına koymak zorundayız. Böyle bir bağımlılık RemoteControl sınıfının tek başına başka bir alanda tekrar kullanılmasını engellemektedir. Buradan söyle bir sonucu çıkartıyoruz: Program parçalarının (sınıflar) tekrar kullanılabilir olabilmeleri için diğer sınıflara olan bağımlılıklarının düşük seviyede olması gerekmektedir. Esnek bağımlılık olmadan bir kere yazılan kodun tekrar kullanımı çok güçtür.
  • TV sınıfı bünyesinde meydana gelen yapısal değişiklikler RemoteControl sınıfını doğrudan etkileyecektir. Böyle bir bağımlılık RemoteControl sınıfının yapısal değişikliğe uğrama rizikosunu artırır.
  • RemoteControl nesnesi sadece bir TV nesnesini (yani bir televizyonu) kontrol edebilir. Oysaki bir evde uzaktan kontrol edilebilecek başka aletler de olabilir. Böyle bir bağımlılık RemoteControl nesnesini sadece bir TV nesnesiyle beraber çalışmaya mahkum eder. Uzaktan kontrol edilebilen aletler için başka RemoteControl sınıflarının oluşturulması gerekmektedir, örneğin bir CD çalıcıyı kontrol etmek için CDPlayerRemoteControl isminde bir sınıfı oluşturmak zorundayız.

[source language=’java’]
package org.cevikjava.design.loosecoupling;

/**
* Bir televizyonu uzaktan kuman etme
* aletini simule eden sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Kontrol edilen televizyon
*/
private TV tv = new TV();

/**
* Televizyonu acmak
* için kullanilan metot.
*
*/
public void tvOn()
{
tv.on();
}

/**
* Televizyonu kapatmak
* için kullanilan metot.
*/
public void tvOff()
{
tv.off();
}
}

[/source]

RemoteControl sınıfı bünyesinde TV tipinde bir sınıf değişkeni (tv) barındırdığı için kendisini TV sınıfına bağımlı kılar.

[source language=’java’]
package org.cevikjava.design.loosecoupling;

/**
* Bir televizyonu simule eden sınıf.
*
* @author Oezcan Acar
*
*/
public class TV
{
/**
* Televizyonu acmak için
* kullanilan metot.
*
*/
public void on()
{
System.out.println(“TV acildi.”);
}

/**
* Televizyonu kapatmak için
* kullanilan metot.
*
*/
public void off()
{
System.out.println(“TV kapandi”);
}
}

[/source]

Nesneler arası kuvvetli bağların, programın bakımı, geliştirilmesi ve kodun tekrar kullanımını negatif yönde etkilediğini gördük. Bu sorunu ortadan kaldırabilmek için sınıfların ilişkileri üzerinde yapısal değişikliğe gitmemiz gerekiyor. Esnek bağ oluşturabilmek için Abstract (soyut) ya da Interface sınıflarından faydalanabiliriz. Resim 2 de görüldüğü gibi RemoteControlInterface ismini taşıyan bir Interface sınıf ile RemoteControl ve bu sınıfın kontrol etmek istediği aletler arasında esnek bir bağ oluşturuyoruz. Böyle bir yapının esnekliği nereden gelmektedir, bunu yakından inceleyelim.

[source language=’java’]
package org.cevikjava.design.loosecoupling.design;

/**
* RemoteControlInterface sınıfı
*
* @author Oezcan Acar
*
*/
public interface RemoteControlInterface
{
/**
* Bu sınıfı implement eden
* bir aleti acmak için
* kullanilan metot.
*
*/
void on();

/**
* Bu sınıfı implement eden
* bir aleti kapatmak için
* kullanilan metot.
*
*/
void off();

}

[/source]

Interface sınıflar bünyesinde sadece metot gövdeleri tanımlanır. Alt sınıflar kullanılarak interface sınıfında tanımlanmış olan metotlar implemente edilir. Bu sayede interface sınıfında tanımlanmış metotlar için değişik sınıflarda değişik tarzda implementasyonlar yapmak mümkündür. Herhangi bir sınıf implements direktifini kullanarak bir interface sınıfını implemente edebilir. Interface sınıfında tanımlanmış olan tüm metotların alt sınıflarca implemente edilmesi gerekmektedir.

Resim 2 de görüldüğü gibi RemoteControl sınıfı direk TV sınıfı kullanmak yerine, RemoteControlInterface ismini taşıyan interface sınıfı ile beraber çalışmaktadır. Böyle bir yapılanma ile RemoteControl ve bu sınıfın kullanmak istediği somut sınıflar (örneğin TV) arasına bir set çekmiş olduk. RemoteControl sınıfı bu setin arkasında yer alan sınıfları, bunlar RemoteControlInterface sınıfını implemente eden sınıflardır, tanımamaktadır ve tanımak zorunda değildir. RemoteControl sınıfının tanıması gereken tek sınıf RemoteControlInterface sınıfı ve bu interface sınıfının dış dünyaya sunduğu metotlardır. RemoteControl sınıfı böylece dolaylı olarak, RemoteControlInterface sınıfını implemente eden her sınıfı kullanabilir hale gelmektedir. Böylece RemoteControl sınıfının somut sınıflara olan bağımlılığı bir interface sınıf kullanılarak ortadan kaldırılmıştır.

[source language=’java’]
package org.cevikjava.design.loosecoupling.design;

/**
* RemotControlInterface sınıfını
* implemente eden sınıfları
* kontrol edebilen sınıf.
*
* @author Oezcan Acar
*
*/
public class RemoteControl
{
/**
* Delegasyon islemi için RemoteControlInterface
* tipinde bir sınıf degiskeni tanimliyoruz.
* Tüm islemler bu nesnenin metodlarina
* delege edilir.
*/
private RemoteControlInterface remote;

/**
* Sinif konstuktörü. Bir nesne oluşturma islemi
* esnasında kullanilacak RemoteControlInterface
* implementasyonu parametre olarak verilir.
*
* @param _remote RemoteControlInterface
*/
public RemoteControl(RemoteControlInterface _remote)
{
this.remote = _remote;
}

/**
* Aleti acmak
* için kullanilan metot.
*
*/
public void on()
{
remote.on();
}

/**
* Aleti kapatmak
* için kullanilan metot.
*/
public void off()
{
remote.off();
}

}

[/source]

RemoteControl sınıfını RemoteControlInterface sınıfını kullanacak şekilde değiştiriyoruz. Bu amaçla RemoteControl sınıfında RemoteControlInterface tipinde bir sınıf değişkeni (remote) tanımlıyoruz. Sınıf konstruktörü RemoteControlInterface tipinde bir parametre kabul etmektedir. Böylece RemoteControl sınıfından bir nesne oluştururken, istediğimiz tipte bir RemoteControlInterface implementasyon sınıfı kullanabiliriz.

RemoteControl sınıfı on() ve off() isminde iki metot tanımlamaktadır. Bu metotlar RemoteControlInterface sınıfında yer alan on() ve off() metotları ile karıştırılmamalıdır. RemoteControl sınıfı sahip olduğu metotlara ac() ve kapat() isimlerini de verebilirdi. Bu metotlar içinde delegasyon yöntemiyle sınıf değişkeni olan remote nesnesi kullanılmaktadır. Bu sayede kullanılan RemoteControlInterface implementasyon sınıfının (örneğin TV) on() ve off() metotları devreye girecektir.

[source language=’java’]
package org.cevikjava.design.loosecoupling.design;

/**
* Bir televizyonu simule eden sınıf.
* RemoteControlInterface sınıfını
* implemente ederek bir RemoteControlInterface
* haline gelir.
*
* @author Oezcan Acar
*
*/
public class TV implements RemoteControlInterface
{

/**
* Televizyonu acmak için
* kullanilan metot.
*
*/
public void on()
{
System.out.println(“TV acildi.”);
}

/**
* Televizyonu kapatmak için
* kullanilan metot.
*
*/
public void off()
{
System.out.println(“TV kapandi”);
}
}

[/source]

TV sınıfı RemoteControlInterface sınıfını implemente etmektedir. Bu sebepten dolayı RemoteControlInterface sınıfında tanımlanmış olan on() ve off() metotlarına sahiptir. TV sınıfı RemoteControlInterface sınıfını implemente etmediği sürece RemoteControl sınıfı tarafından kullanılamaz.

[source language=’java’]
package org.cevikjava.design.loosecoupling.design;

/**
* Test sınıfı
*
* @author Oezcan Acar
*
*/
public class Test
{

public static void main(String[] args)
{
RemoteControlInterface rci = new TV();
RemoteControl control = new RemoteControl(rci);
control.on();
control.off();
}
}

[/source]

Test.main() bünyesinde ilk önce kullanmak istediğimiz alet nesnesini, (TV) ve akabinde RemoteControl nesnesini oluşturuyoruz. RemoteControl konstruktör parametresi olarak bir satır önce oluşturduğumuz TV nesnesini almaktadır. RemoteControl bünyesinde yer alan on() ve off() metotları ile televizyonu açıp, kapatabiliriz. Ekran çıktısı şu şekilde olacaktır:


TV acildi.
TV kapandi

RemoteControl sınıfının konstruktörü RemoteControlInterface sınıfını implemente eden her sınıfı kabul ettiği için istediğimiz herhangi bir aleti RemoteControl sınıfı ile kontrol edebilir hale geliyoruz.

Oluşturduğumuz yeni tasarımın bize sağladığı avantajlar şöyledir:

  • Bir interface sınıf (RemoteControlInterface) kullanarak, RemoteControl ve kontrol etmek istediği aletler (TV, CDPlayer) arasında bir bariyer oluşturduk. RemoteControl sınıfı kontrol etmek istediği aleti tanımak zorunda olmadığı için bu sınıf ve diğerleri arasındaki sıkı bağı çözmüş ve interface sınıf kullanarak daha esnek bir hale getirmiş oluyoruz.
  • Bu tarz bir tasarım ile programı gelecekte oluşacak değişiklikleri taşıyabilecek hale getirdik. RemoteControl sınıfı, RemoteControlInterface sınıfını implemente eden her sınıfı kontrol edebilir. RemoteControlInterface sınıfını implemente ederek, sisteme uzaktan kumanda edilebilen yeni aletler ekleyebiliriz.
  • RemoteControl sınıfı, RemoteControlInterface sınıfının implemente edildiği başka bir ekosistemde tekrar kullanılabilir hale geldi. Esnek bağımlılık oluşturmak, kodun tekrar kullanımını kolaylaştırmaktadır.

İncelediğimiz örneklerde nesneler arası bağın ortadan kaldırılamayacağını ama esnek bağ oluşturma prensibini uygulayarak kontrol edilebilir bir hale getirilebileceklerini gördük. Esnek bağlar oluşturabilmek için Interface yada soyut sınıflardan yararlanabiliriz.

Usta yazılımcılar tasarım prensiplerine ve tasarım şablonlarına (design pattern) hakim olup,onları doğru yerde kullanmasını bilirler. Eğer şimdiye kadar tasarım prensipleri hakkında bir çalışmanız olmadıysa, sizin için yazılım disiplininde bir üst boyutun kapısını aralamış olduk. Tasarım prensiplerini uygulayarak ve tasarım şablonlarını kullanarak konseptüel daha yüksek seviyede çalışabilirsiniz. Bu bölümde yer alan tasarım prensipleri yazılım sürecine olan bakış açınızı tamamen değiştirecek niteliktedir.

Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.


EOF (End of Fun)
Özcan Acar

Single Responsibility Principle (SRP) – Tek Sorumluk Tasarım Prensibi

Resim 1 deki gibi bir sınıfa daha önce bir yerlerde rastlamışsınızdır. Bu sınıf kendisini bilgibankasına ekleme, silme, müşteriye email gönderme, login yapma (shop sistemi olabilir) ve sipariş oluşturma işlemlerini yapabilmektedir.


Resim 1

Büyük bir ihtimalle bu sınıfı programlayan programcı kendisi ile gurur duyuyor olmalıdır. Aslında böyle bir sınıfın ve programcının küçümsenecek hiçbir tarafı yok. Bu sınıf büyük emek harcanarak oluşturulmuş olabilir, çünkü ihtiva ettiği metotlar basit değildir. Lakin bu sınıf saatli bir bombadır, çünkü içinde bulunduğu ortamdaki her değişiklik, sınıfın yapısal değişikliğe uğramasına sebep olabilir. Bunun yanı sıra Customer sınıfında implemente edilen kod tekrar kullanılmak istendiğinde, Customer sınıfının bağımlı olduğu sınıf ve API’lerin bu sınıfla beraber kullanılma zorunluluğu vardır, yani Customer sınıfı gittiği yere beraberinde kullandığı diğer sınıflar ve API’leri götürür. Bu kodun tekrar kullanımını sağlamak için istenmeyen bir durumdur.

Customer sınıfını test edilebilir ve bakımı kolay bir hale getirmek için, sahip olduğu sorumlulukların başka sınıflara yüklenmesi gerekmektedir. Bunun bir örneğini resim 2 de görmekteyiz.


Resim 2

Resim 2 de yer alan Customer sınıfı sahip olduğu sorumlulukların başka sınıflara yüklenmesiyle hafiflemiş ve değişikliklere karşı daha dayanıklı bir hale gelmiştir. Bunun yanı sıra bu sınıfın test edilebilirliği yükselmiştir. Bu ve diğer sınıfların değişmek için artık sadece bir sebebi vardır. Bunun sebebi sadece bir sorumluluk sahibi olmalarında yatmaktadır.

Bir sınıfın sorumluluğunu sadece bir sebepten dolayı değişebilir olması olarak tanımlayabiliriz. Eğer bir sınıfın değişikliğe uğraması için birden fazla sebep varsa, bu sınıf birden fazla sorumluluk taşıyor demektir. Bu durumda sınıfta sadece bir sorumluluk kalacak şekilde, sorumlukların diğer sınıflarla paylaşılması yoluna gidilmelidir.

Bu yazıyı PDF dosyası olarak aşağıdaki linkten edinebilirsiniz.


EOF (End of Fun)
Özcan Acar

Chain of Responsibility Tasarım Şablonu

Chain of responsibility sorumluluk zinciri anlamına gelmektedir. Sisteme gönderilen bir istediğin (komut) hangi nesne tarafından cevaplanması gerektiğini bilmediğimiz durumlarda ya da isteği yapan nesne ve servis sağlayan nesne arasında sıkı bir bağ oluşmasını engellememiz gerektiğinde Chain of Responsibility tasarım şablonu kullanılır. Okumaya devam et →

Flyweight (Sinek Siklet) Tasarım Şablonu

Java dilinde yazılan programlar içinde sınıflar ve bu sınıflardan oluşturulan nesneler kullanır. Bazen aynı sınıftan yüzlerce, belki binlerce nesne oluşturup, kullanıyor olabiliriz. Bu gibi durumlarda çok nesne oluşturulduğu için sistem performansı kötüye gidebilir. Flyweight tasarım şablonu kullanılarak, kullanılan nesne adedini aşağıya çekebiliriz.

Bu satırlar oluşurken, büyük bir ihtimalle kullandığım editör flyweight tasarım şablonunu kullanıyor olabilir. Yazdığım her cümle kelimelerden, her kelime birden fazla harften oluşmaktadır. Kullandığım editörün Java dilinde yazıldığını ve her harf için bir nesne kullandığını farzedersek, bir satırlık doküman için 80 ila 100 arası harf nesnesi oluşturması gerekir. 100 satırlık bir doküman için bu 10.000 civarı harf nesnesinin oluşturulması anlamına gelir.

Bu yazıyı PDF olarak edinebilirsiniz.

  Flyweight (Sinek Siklet) Tasarım Şablonu (52,4 KiB, 9.191 yükleme)

Konuyla İlgili Kitaplar


EOF (End of Fun)
Özcan Acar

Proxy (Vekil) Tasarım Şablonu

Oluşturulmaları zaman alıcı ve sistem kaynaklarını zorlayan nesnelere vekalet eden nesnelere proxy nesneleri adı verilir. Bu nesneler vekil oldukları nesnelerin tüm metodlarına sahiptirler ve kullanıcı sınıf ile vekil olunan nesne arasında aracılık yaparlar. Vekil olan nesne, kullanıcı sınıfa, vekil olunan nesne gibi davranır ve kullanıcı sınıftan gelen tüm istekleri vekil olunan nesneye iletir. Böyle bir yapının kullanılmasının sebebi, gerek olmadığı sürece vekil olunan nesnenin oluşturulmasını engellemektir ya da vekil olunan nesneyi gizlemektir. Okumaya devam et →

Facade (Cephe) Tasarım Şablonu

Profesyonel yazılım sistemleri birçok komponentin birleşiminden oluşur. Yazılım esnasında bir çok ekip birbirinden bağımsız, sistemin bütününü oluşturan değişik komponentler üzerinde çalışırlar. Bir komponent, belirli bir işlevi yerine getirmek için hazırlanmış bir ya da birden fazla Java sınıfından oluşmaktadır.

Okumaya devam et →

Embedded Jetty

Üzerinde çalıştığım açık kaynaklı JStorage projesinde Tomcat yerine, bir Java sınıfından koşturulabilecek Jetty containeri kullandım. Jetty, Tomcat gibi bir JSP/Servlet containeri. Aşağıdaki örnekte yer aldığı gibi Jetty bir Java (main()) program tarafından çalıştırilabilir.

[source language=’java’]
package org.jstorage.tracker.impl.restfull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jstorage.tracker.TrackerException;
import org.jstorage.tracker.impl.AbstractTracker;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.servlet.DispatcherServlet;

@Component(“RestfullTracker”)
@Scope(“singleton”)
public class RestfulTrackerImpl extends AbstractTracker
{

private final Log logger = LogFactory.getLog(RestfulTrackerImpl.class);

private Server server;
ApplicationContext applicationContext;
private int port;
private String ip;

public RestfulTrackerImpl()
{

}

protected void finalize() throws Throwable
{
super.finalize(); // not necessary if extending Object.
logger.info(“Stopping storage node with jetty”);
server.stop();
}

@Override
public void runInProductionMode()
{
try
{
logger.info(“Starting storage node with jetty in PRODUCTION MODE”);
server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });

ServletHandler handler = new ServletHandler();
server.setHandler(handler);

handler.addServletWithMapping(“org.jstorage.tracker.impl.http.TrackerServlet”,”/”);

server.start();

server.join();
}
catch (Exception e)
{
throw new TrackerException(e);
}
}

@Override
public void runInTestMode()
{
try
{
Server server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });

Context context = new Context(server, “/”, Context.SESSIONS);

ServletHolder servletHolder = new ServletHolder(new DispatcherServlet());
servletHolder.setInitParameter(“contextConfigLocation”,”classpath:spring-restfulltracker.xml”);
servletHolder.setInitOrder(1);
context.addServlet(servletHolder, “/*”);

// Add the Spring context listener
context.getInitParams().put(“contextConfigLocation”,”classpath:spring-restfulltracker.xml”);
ContextLoaderListener listener = new ContextLoaderListener();
context.addEventListener(listener);

server.start();
context.getServletHandler().initialize();

}
catch (Exception e)
{
throw new TrackerException(e);
}
}

public ApplicationContext getApplicationContext()
{
if(applicationContext == null)
{
applicationContext = new ClassPathXmlApplicationContext(“spring-restfulltracker.xml”);
}
return this.applicationContext;
}

public int getPort()
{
int p = this.port;

try
{
p = Integer.parseInt(System.getProperty(“port”));
}
catch (Exception e)
{
//ignore
}

return p;
}

public void setPort(int port) {
this.port = port;
}

public String getIp()
{
String ip = “127.0.0.1”;

try
{
ip = (String)System.getProperty(“ip”);
}
catch (Exception e)
{
//ignore
}
return ip;
}

public void setIp(String ip)
{
this.ip = ip;
}
}

[/source]

59. satırda org.jstorage.tracker.impl.http.TrackerServlet isimli Servlet sınıfını http://127.0.0.1/ adresinde erişilecek şekilde sisteme ekliyorum. Bu mapping işlemini genelde web aplikasyonlarında web.xml bünyesinde aşağıdaki şekilde gerçekleştiririz.

[source language=’java’]

ForwardedServlet
tests.Filter.ForwardedServlet


ForwardedServlet
/ForwardedServlet

[/source]

Yukarda yer alan programı çalıştırdıktan sonra Web tarayıcısı üzerinden http://127.0.0.1/ adresine bağlandığımız taktirde TrackerServlet.doGet() metodu işlem görecektir. -Dport=8080 şeklinde bir sistem değişkeni kullanarak (118. satır), Jetty’nin çalıştırılacağı portu değiştirebiliriz.


EOF (End of Fun)
Özcan Acar

JAXB ile Java-XML Mapping

JAXB (Java Xml Binding – https://jaxb.dev.java.net/) frameworkü kullanılarak Xml–>Java, Java–>Xml mapping işlemlerini gerçekleştirmek mümkündür. Bu yazılımcıya, Xml dosyalarının ihtiva ettiği verileri Java sınıflarıyla modelleme imkanı sunmakta ve verilerın işlenmesini kolaylaştırmaktadır. JAXB’nın nasıl kullanılabileceğini bir örnek üzerinde sizlere aktarmak istiyorum.

Aşağıda bir arabayı modelleyen bir DTD dosyası yer almaktadır.

[source language=’java’]







[/source]

Bu DTD dosyasını kullanarak JAXB yardımı ile Java mapping sınıfları oluşturabiliriz. Bu işlemi Ant ile yapmak mümkündür.

[source language=’java’]






[/source]

jaxb.home isminde bir değişken ile JAXB paketinin ihtiva ettiği Jar dosyalarının yerini tanımlıyoruz. Xml den türetilen Java sınıflarını oluşturabilmek için com.sun.tools.xjc.XJCTask sınıfını kullanıyoruz.

DTD dosyasından Java sınıflarını oluşturabilmemiz için tasit.jaxb isminde bir konfigürasyon dosyası oluşturmamız gerekmektedir. Bu dosyanın içeriği aşağıdaki şekildedir:

[source language=’java’]




[/source]

Java sınıflarını oluşturmak için Ant dosyasında tanımladığımız tasit isimli hedefi (target) koşturuyoruz. tasit hedefinde belirttiğimiz gibi Java sınıfları xjc tarafından oluşturulduktan sonra gen-src dizine yerleştirileceklerdir.

JAXB tarafından oluşturulan sınıflar aşağıda yer almaktadır.

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“model”,
“motor”
})
@XmlRootElement(name = “araba”)
public class Araba {

@XmlElement(required = true)
protected String model;
@XmlElement(required = true)
protected Motor motor;

/**
* Gets the value of the model property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getModel() {
return model;
}

/**
* Sets the value of the model property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setModel(String value) {
this.model = value;
}

/**
* Gets the value of the motor property.
*
* @return
* possible object is
* {@link Motor }
*
*/
public Motor getMotor() {
return motor;
}

/**
* Sets the value of the motor property.
*
* @param value
* allowed object is
* {@link Motor }
*
*/
public void setMotor(Motor value) {
this.motor = value;
}

}

[/source]

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“beygirgucu”,
“tipi”
})
@XmlRootElement(name = “motor”)
public class Motor {

@XmlElement(required = true)
protected String beygirgucu;
@XmlElement(required = true)
protected String tipi;

/**
* Gets the value of the beygirgucu property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getBeygirgucu() {
return beygirgucu;
}

/**
* Sets the value of the beygirgucu property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setBeygirgucu(String value) {
this.beygirgucu = value;
}

/**
* Gets the value of the tipi property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTipi() {
return tipi;
}

/**
* Sets the value of the tipi property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTipi(String value) {
this.tipi = value;
}

}
[/source]

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlRegistry;

/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.tasit.mapping package.
*

An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {

public ObjectFactory() {
}

/**
* Create an instance of {@link Motor }
*
*/
public Motor createMotor() {
return new Motor();
}

/**
* Create an instance of {@link Araba }
*
*/
public Araba createAraba() {
return new Araba();
}

/**
* Create an instance of {@link Tasit }
*
*/
public Tasit createTasit() {
return new Tasit();
}

}

[/source]

[source language=’java’]
package com.tasit.mapping;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“araba”
})
@XmlRootElement(name = “Tasit”)
public class Tasit {

protected List araba;

/**
* Gets the value of the araba property.
*
*

* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a set method for the araba property.
*
*

* For example, to add a new item, do as follows:
*

* getAraba().add(newItem);
* 

*
*
*

* Objects of the following type(s) are allowed in the list
* {@link Araba }
*
*
*/
public List getAraba() {
if (araba == null) {
araba = new ArrayList();
}
return this.araba;
}

}

[/source]

Şimdi Tasit.dtd den türetilmiş bir Xml dosyasında yer alan verileri xjc tarafından oluşturulan Java sınıflarına nasıl yükleyebileceğimizi yakından inceleyelim. Tasit.dtd ye uyumlu bir Xml örneği aşağıda yer almaktadır:

[source language=’java’]



Ford Fiesta

75
benzinli


Opel Agila

70
benzinli



[/source]

Aşağıda yer alan program yardımı ile Xml dosyasında yer alan verileri elde edebiliriz:

[source language=’java’]

package com.tasit;

import java.io.File;
import java.io.FileReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.EventFilter;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import com.tasit.mapping.Araba;

public class Main {

/**
* @param args
*/
public static void main(String[] args) throws Exception
{
XMLInputFactory xmlif = XMLInputFactory.newInstance();
FileReader fr = new FileReader(new File(“c:/temp/araba.xml”));
XMLEventReader xmler = xmlif.createXMLEventReader(fr);
EventFilter filter = new EventFilter() {
public boolean accept(XMLEvent event) {
return event.isStartElement();
}
};

XMLEventReader xmlfer = xmlif.createFilteredReader(xmler, filter);
StartElement e = (StartElement) xmlfer.nextEvent();

JAXBContext ctx = JAXBContext.newInstance(“com.tasit.mapping”);
Unmarshaller um = ctx.createUnmarshaller();

while (xmlfer.peek() != null)
{
Object o = um.unmarshal(xmler);
if (o instanceof Araba)
{
Araba araba = (Araba) o;
System.out.println(araba.getModel());
System.out.println(araba.getMotor().getBeygirgucu());
System.out.println(araba.getMotor().getTipi());
System.out.println(“———————————–“);
}
}
fr.close();

}

}
[/source]

Ekran çıktısı şu şekilde olacaktır:

[source language=’java’]
Ford Fiesta
75
benzinli
———————————–
Opel Agila
70
benzinli
———————————–
[/source]

Xml–>Java mapping işlemlerinin nasıl yapıldığını gördük. Aşağıdaki Java örneği Java–>Xml mapping işleminin nasıl yapıldığını göstermektedir.

[source language=’java’]

package com.tasit;

import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import com.tasit.mapping.Araba;
import com.tasit.mapping.Motor;

public class Main {

/**
* @param args
*/
public static void main(String[] args) throws Exception
{

Araba araba = new Araba();
Motor motor = new Motor();
motor.setBeygirgucu(“300”);
motor.setTipi(“dizel”);
araba.setMotor(motor);
araba.setModel(“Crossfire”);

Marshaller marshaller = null;

marshaller = JAXBContext.newInstance(“com.tasit.mapping”).createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
StringWriter writer = new StringWriter();

marshaller.marshal(araba, writer);

System.out.println(writer.getBuffer().toString());

}

}
[/source]

Programı koşturduğumuzda ekran çıktısı şu şekilde olacaktır:

[source language=’java’]


Crossfire

300
dizel

[/source]


EOF (End Of Fun)
Özcan Acar

Hibernate Session Problemi: Illegal attempt to associate a collection with two open sessions

BTSoru.com projesini modellerken aşağıdaki hata ile karşılaştım:

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at bt.btsoru.application.manager.question.facade.QuestionManagerImpl.addQuestion(QuestionManagerImpl.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

Hatanın oluşmasını sağlayan kod bölümü şu şekilde:

[source language=’java’]
// Soruyu ekleme bölümü
BTUser user = new BTUser();
user.setId(WebSession.get().getUser().getId());
getQuestion().setUser(WebSession.get().getUser());

QuestionManagerResult result = qManager.addQuestion(getQuestion());

[/source]
Kod 1.1

Birinci satırda yer alan BTUser bir Hibernate nesnesi. BTUser ile bir kullanıcıyı modelliyorum. UML diagramı aşağıda yer almaktadır:

Sistem bünyesinde her kullanıcının sıfır ya da birden fazla sorusu olabilir. Sorulan her soru sadece bir kullanıcıya aittir. BTUser gibi BTQuestion sınıfı da bir Hibernate nesnesi, yani ihtiva ettiği veriler bilgilbankasında saklanabilmekte.

Kod 1.1 de yer alan kodun yedinci satırında qManager.addQuestion() metodu ile yeni bir soruyu (BTQuestion nesnesini) bilgibankasına ekliyorum. BTQuestion nesnesinin ihtiva ettiği verileri bir form üzerinden kullanıcı daha önce girdi. Gerekli validasyon işlemlerini daha önce gerçekleştirerek, BTQuestion nesnesini, yani kullanıcının sisteme eklemek istediği soruyu bilgibankasına ekliyorum.

BTUser ile BTQuestion arasındaki ilişkiyi sağlamak için aşağıdaki şekilde BTQuestion bünyesinde bir ManyToOne ilişkisini oluşturuyorum. Bu şekilde BTQuestion nesnesi bir kullanıcıya atanmış olacaktır.

[source language=’java’]

@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(
name=”btsoru_user_questions”,
joinColumns = { @JoinColumn( name=”question_id”, unique = false) },
inverseJoinColumns = @JoinColumn( name=”user_id”, unique = false)
)
private BTUser user;
[/source]
Kod 1.2

@JoinTable anotasyonu ile kullanıcı ile sahip olduğu soruları ilişkilendirmek için yeni bir bilgibankası tablosu oluşturuyorum (btsoru_user_question). Bu tablo bünyesinde kullanıcının sahip olduğu tekil numara (user_id) ve soru nesnesinin tekil numarası (question_id) yer almaktadır. Bu şekilde kullanıcı ve soruyu ilişkilendirmiş oluyorum.

Bu konfigurasyonda kod 1.1 altıncı satırda yer alan qManager.addQuestion() metodunu kullandığım taktirde, yukarda bahsettiğim Hibernate problemi oluşuyor. Bunun sebebi şudur: Kod 1.1, üçüncü satırda Hibernate tarafından kontrol edilen bir sınıfdan (BTUser) bir nesne oluşturarak, bu nesneye id değerini atıyorum. Buradaki amacım, bir BTQuestion nesnesini bilgibankasına eklemeden önce bir BTUser nesnesi ile ilişkilendirmek. Bunu getQuestion().setUser(WebSession.get().getUser()); ile gerçekleştiriyorum. Bunu yapmadığın taktirde, bilgibankasına herhangi kullanıcıya ait olmayan bir soru yer alacaktır ve bu istenmeyen bir durumdur. Dördüncü satırda WebSession.get().getUser().getId()); ile kullanıcı id sini session içinde bulunan BTUser nesnesinden ediniyorum. Id değişkenini kullanıcının kimlik numarası olarak düşünebilirsiniz. Bu işlemi gerçekleştirebilmem için kullanıcının soruyu sisteme eklemeden önce isim ve şifresi ile login yapmış olması gerekmektedir. Login işlemi ardından oluşturduğum BTUser nesnesini session içine yerleştiriyorum ve gerekli durumlarda WebSession.get().getUser()); ile bu nesneye, yani login yapmış kullanıcının bilgilerine ulaşıyorum. Sorun da ne yazik ki burada oluşmaktadır. Login işleminin ardından belirli bir id ye sahip bir BTUser nesnesi sistemde (session içinde) mevcuttur. Aynı id ye sahip ikinci bir nesne oluşturulduğu taktirde, Hibernate buna Illegal attempt to associate a collection with two open sessions hata mesaji ile karşı çıkacaktır. Böyle bir konstellasyonda Hibernate kontrolünde bulunan (Hibernate Cache) bir nesneye rakip olacak aynı id yi taşıyan ikinci bir nesne oluşturup kullanmamız mümkün değildir.

Bu sorunu aşmak için BTQuestion bünyesinde bulunan BTUser değişkeninin mapping parametrelerini kod 1.3 deki gibi değiştirmemiz yeterli olacaktır.

[source language=’java’]

@ManyToOne(fetch=FetchType.LAZY)
@JoinTable(
name=”btsoru_user_questions”,
joinColumns = { @JoinColumn( name=”question_id”, unique = false) },
inverseJoinColumns = @JoinColumn( name=”user_id”, unique = false)
)
private BTUser user;
[/source]
Kod 1.3

@ManyToOne anotasyonundaki cascade=CascadeType.ALL elementini kaldırmamız yeterlidir.


EOF (End of Fun)
Özcan Acar

BizimAlem.com – Java İle Yüksek Performanslı Web Platformu

11-12 Nisan 2009 tarihlerinde CETURK tarafından Kıbrıs Doğu Akdeniz Üniversitesinde düzenlenen Java Etkinlikleri Günü’nde “Java ile yüksek performanslı web platformlarının oluşturulması” başlıklı bir sunum yaptım.

Özcan Acar

Okumaya devam et →

Command (Komut) Tasarım Şablonu

Command tasarım şablonunu açıklamak için televizyonu uzaktan kumanda etme aletini metafer (mecazi örneklendirme) olarak kullanmak istiyorum. Kanalları değiştirmek için kumanda aleti üzerinde belirli bir tuşa basarız. Tusa basıldığı anda kumanda aleti televizyona bir komut göndererek, kanalın değişmesini sağlar. Aynı şekilde televizyonu açıp, kapatmak ve ses ve renk ayarlarını yapmak için değişik tuşlar kullanılır. Okumaya devam et →

Yazılım Mimarisi Tasarımı Günü

CETURK tarafından 21.3.2009 tarihinde Elazığ Fırat Üniversitesinde düzenlenen  Yazılım Mimarisi Tasarımı Günü‘ne konuşmacı olarak katıldım ve Tasarım Prensipleri ve Spring MVC konulu iki sunum yapım. Sunum dosyalarını aşağıdaki linkler üzerinden edinebilirsiniz.

Düzenlediğimiz diğer seminerleri Kurumsal Java Akademisi sayfalarından takip edebilirsiniz.

  Tasarım Prensipleri (1,0 MiB, 7.489 yükleme)

  Spring MVC (524,1 KiB, 11.347 yükleme)

Spring MVC Eclipse Projesi

Tasarım Prensipleri sunumu
.

Spring MVC sunumu

.





















EOF (End Of Fun)
Özcan Acar

Adapter Tasarım Şablonu

Adapter tasarım şablonu yardımı ile, sistemde mevcut bulunan bir sınıfın sunduğu interface (sınıf metodları) başka bir sınıf tarafından kullanilabilir şekilde değiştirilir (adapte edilir). Bu adapter yardımı ile birbiriyle beraber çalışamıyacak durumda olan sınıflar, birlikte çalışabilir hale getirilir.

Adapter tasarım şablonu kendi içinde sınıf ve nesne adapteri olarak ikiye ayrılır. Önce sınıf adapterini inceliyelim.

 

 

Bu yazıyı PDF olarak edinebilirsiniz.

  Adapter Tasarım Şablonu (2,0 MiB, 18.627 yükleme)

Konuyla İlgili Kitaplar