Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan...

14
EFFECTIVE JAVA – Madde 2 JOSHUA BLOCH BY İBRAHİM KÜRCE, @IBRAHIMKURCE

Transcript of Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan...

Page 1: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

EFFECTIVE JAVA – Madde 2JOSHUA BLOCHBY İBRAHİM KÜRCE, @IBRAHIMKURCE

Page 2: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

BÖLÜM 2 : NESNELERİ OLUŞTURMA VE YOKETME

Bu bölüm nesneleri oluşturma ve yok etme ile ilgilidir. Bu bölümde, nesneler ne zaman ve nasıl oluşturulur, ne zaman ve nasıl oluşturulmaması gerekir, zaman kısıtlı durumlarda yok edilmesi nasıl sağlanır ve yok edilmeden önce nesnelere ait temizleme işlemleri nasıl yönetilir gibi sorulara cevap aranır.

Page 3: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Statik fabrikalar ve yapılandırıcılar ortak bir kısıtı paylaşır: birçok isteğe bağlı parametreye karşı kendilerini iyi ölçekleyemezler. Paketli gıdaların üzerinde bulunan Beslenme Unsurları(Nutrition Facts) sınıfının gösterilmesi ele alalım. Bu sınıfın birkaç tane zorunlu alanı vardır: porsiyon boyutu(serving size), konteyner başına porsiyon(servings per container) ve porsiyon başına kalorileri(calories per serving). Ayrıca birçok isteğe bağlı alanı vardır: toplam yağ(total fat), doymuş yağ(saturated fat), kolesterol, sodyum vb. Birçok ürün için bu alanların değerleri sıfırdan farklıdır.

Bu sınıf için ne tür yapılandırıcı veya statik fabrika metodu yazmalıyız? Geleneksel olarak, programcılar iç içe geçmiş yapılandırıcı modelini(telescoping constructor pattern) kullanır. Bu modelde zorunlu parametreleri kapsayan bir yapılandırıcı tanımlanır, sonra zorunlu olanlar ve bir tane isteğe bağlı parametreli yapılandırıcı tanımlanır, daha sonra zorunlu olanlar ve iki tane isteğe bağlı yapılandırıcı tanımlanır ve ta ki bütün isteğe bağlı parametreleri tanımlayana kadar böyle devam eder. Pratikte nasıl göründüğüne bakalım. Kısa olsun diye, sadece 4 adet isteğe bağlı alan gösterilmiştir:

Page 4: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Page 5: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Sınıfın örneğini oluşturmak istediğinde, set etmek istediğin alanları kapsayan en kısa parametreli yapılandırıcıyı kullanırsın.

Bu şekilde yapılandırıcı kullanımı set etmek istemediğiniz birçok parametreyi eklemenizi gerektirir, kullanmasanız bile değeri set etmek zorundasınızdır. Bu durumda yağ alanı için 0 değerini yapılandırıcıya geçeriz. Bu durum altı parametreli bir sınıfta o kadar da kötü durmuyor ama parametre sayısındaki artışla beraber durum çabucak kontrolden çıkar.

Kısaca, iç içe yapılandırıcı modeli çalışır ama birçok parametre olduğunda kullanıcı tarafı için kod yazmak çok zorlaşır ve okumakta daha zordur. Okuyucu hangi değerin ne olduğunu anlamaya zorlanır ve dikkatlice parametreleri saymak zorundadır. Aynı tipteki parametrelerin uzun serisi ince hatalara yol açar. Kullanıcı aynı tipteki iki parametreyi yanlışlıkla ters çevirirse, derleyici bundan şikayet etmez ama program çalışma zamanında istediğiniz sonucu üretmez(Madde 40).

Page 6: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Çoklu parametresi olan yapılandırıcı ile karşılaşıldığında kullanılabilecek ikinci alternatif JavaBeans modelidir. Bu durumda nesnesi oluşturmak için parametresiz yapılandırıcı çağrılır ve sonra zorunlu alanlar ve isteğe bağlı alanlar için set edici(setter) metotları çağırarak değerlerini set edersiniz.

Page 7: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Bu modelde, iç içe geçmiş yapılandırıcı modelinin sahip olduğu dezavantajlar yoktur. Örnek oluşturması kolaydır ve oluşan kodu okumakta kolaydır.

Maalesef, JavaBeans modelinin çok ciddi dezavantajları vardır. Oluşturma işleminin birçok farklı çağrılara bölünmesi, JavaBean nesnesinin oluşturma aşamasında kararsız bir durumda olmasına neden olur. Bu sınıfın yapılandırıcısının parametreleri geçerli mi diyerek sınıfı kararlı bir durumda tutabilme ihtimali yoktur. Kararsız durumda nesneye erişmeye çalışmak, kodun hatalı durumundan çok daha karmaşık hatalara yok açar ve hata ayıklaması da zordur. İlgili bir dezavantajı da JavaBeans modeli sınıfı değişmez(immutable) yapabilme ihtimalini engeller(Madde 15) ve çok kanallı programlamanın güvenliğini sağlamak için programcıya fazladan çaba harcatır.

Page 8: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Manuel müdahale ile nesneyi dondurmak ve nesnenin oluşturulması bitene kadar değişimine izin vermeyerek dezavantajı azaltılabilir ama pratikte nadiren kullanılan bir yoldur. Dahası çalışma zamanında hatalara yol açar, derleyici programcıya nesne üzerinde onu kullanmadan önce dondurucu metodu sağlamaz.

Neyse ki, üçüncü bir alternatif var bu yöntem ile iç içe geçmiş yapılandırıcı modelinin güvenirliliğiyle JavaBeans modelinin okunulabilirliğini birleştirir. Buna Kurucu(Builder) modeli denir. İstenilen nesneyi direk istemek yerine, kullanıcı zorunlu parametrelerden oluşan yapılandırıcı veya statik fabrika metodunu çağırır ve kurucu nesnesi elde eder. Bundan sonra kullanıcı isteğe bağlı parametrelerin set edici metodlarını çağırır. En son olarak, kullanıcı parametresiz olarak build metodunu çağırır ve değişmez nesnesini oluşturur. Kurucu sınıfı, nesnesini oluşturacağı sınıfın, statik üye sınıfıdır. Örneğin:

Page 9: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Page 10: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

NutritionFacts sınıfı değişmez(immutable) sınıftır ve tüm varsayılan parametre değerleri aynı bölgededir. Kurucunun set edici metodu kurucunun kendisini döner böylece zincirleme olarak çağrılabilir. Kullanıcının kodu şöyle gözükür:

Bu kullanıcı kodu yazması ve okuması kolay bir koddur. Kurucu modeli Ada ve Python dillerinde olan isimli isteğe bağlı parametrelere(named optional parameters) benzer.

Yapılandırıcı gibi, bir kurucu parametrelerinde değişmeyenlere(invariant) yer verebilir. Build metodu bu değişmeyenleri kontrol edebilir. Parametrelerin kurucudan asıl nesneye kopyalanmasından sonra kontrol edilmesi önemlidir ve asıl nesne üzerinde kontrol edilmelidir. Bu değişmeyenler ihlal edilirse, build metodu IllegalStateException hatası fırlatmalıdır(Madde 60). Hatanın detayında hangi değişmeyen alanı ihlal edildiği belirtilmelidir(Madde 63).

Page 11: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Değişmeyen alanları yönetebilmenin diğer bir yolu da değişmeyen alanları gruplayarak da set edici metot ile çağrılmalıdır. Değişmeyen alan ihlal edilirse, set edici metot IllegalArgumentException hatası fırlatır. Bu yöntemin avantajı değişmeyen alandaki ihlal değer set edilince fark edilir, build metodunun çağrılması beklenmez.

Kurucu yönteminin yapılandırıcı üzerine ufak bir avantajı ise, kurucular birçok değişken argümanlı(varargs) parametreye sahip olabilir. Yapılandırıcılar ise sadece bir tane değişken argümanlı parametreye sahip olabilir. Kurucular her parametre için ayrı metod kullanabildikleri için, istedikleri kadar değişken argümanlı parametreye sahip olabilir, her bir set edici metod için.

Kurucu modeli esnektir. Tek bir kurucu birçok nesneyi oluşturabilir. Kurucunun parametreleri nesne oluşturma ile nesneyi değiştirme arasında iyileştirmeler yapabilir. Mesela kurucu bazı alanları otomatik doldurabilir ya da seri numarası gibi bir alanı her nesne oluşturulduğunda otomatik artırılabilir.

Page 12: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Set edilen kurucu parametreleri güzel bir Abstract Factory oluşturur. Diğer bir deyişle, kullanıcı bu kurucuyu bir metoda parametre olarak geçebilir ve bu kurucu da kullanıcı için bir ya da daha fazla nesne oluşturabilir. Bu özelliği etkinleştirmek için, kurucuyu belirtmek için tip sağlamanız gerekir. Java 1.5 sürümü ya da sonrakini kullanıyorsanız, tek bir genel(generic) ifade bütün kurucuları için yeterli gelebilir, nesne tipinin bir önemi olmaz.

NutritionFacts.Builder sınıfı Builder<NutritionFacts> i gerçekleyecek şekilde tanımlanmalıdır.

Page 13: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Kurucu örneğini alan metodlar genelde kurucuların tipine soru işareti karakteriyle kısıtlama getirirler. Örneğin, ağaç yapısı oluşturmak için kullanıcı tarafından tanımlı bir düğüm örneği tanımı:

Java’da geleneksel Abstract Fabrika gerçeklemesi sınıfın nesnesi olur ve newInstance metodu build metodunun parçası olarak çalışır. Bu kullanım problemlerle doludur. newInstance metodu her zaman sınıfın parametresiz yapılandırıcısı çağırır, hatta böyle bir yapılandırıcı olmamasına rağmen. Parametresiz yapılandırıcı yoksa derleme zamanında hata üretilmez. Bunun yerine çalışma zamanında InstantiationException veya IllegalAccessException hatası alınır, bu da çirkin ve uygunsuz bir durumdur. Ayrıca, newInstance metodu, throws ifadesi fırlatmamasına rağmen, parametresiz yapılandırıcı tarafından fırlatılan hatayı yayar. Diğer bir değişle, Class.newInstance metodu derleme zamanındaki hata fırlatma kontrolünü bozar. Kurucu arayüzü, bu eksiklikleri doğrular.

Page 14: Effective Java - Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Madde 2: Birçok parametreli yapılandırıcıyla karşılaşırsan kurucuları(builder) düşün.

Kurucu modelinin bazı dezavantajları vardır. Nesne oluşturmak için önce onun kurucusunu oluşturmanız gerekiyor. Kurucu nesnesi oluşturmanın maliyeti genelde az olsa da, performans kritik durumlarda problem olabilir. Ayrıca, kurucu modeli iç içe geçmiş yapılandırıcı modeline göre daha kalabalıktır, o yüzden yeterli parametre varsa kullanılmalıdır. 4 veya daha fazla parametreli durumlarda. Şunu da unutmamak gerekir ki, ilerde parametre eklemek isteyebilirsiniz. Eğer yapılandırıcılar veya statik fabrikalar ile başlarsanız ve sonradan kurucu modeli eklemek isterseniz bu eski yapılandırıcılar veya statik fabrika metodları sizi canınızdan bezdirilebilir. Bu nedenle, kurucu ile başlamak daha iyi olabilir.

Özet olarak, yapılandırıcıların veya statik fabrikaların bir avuçtan fazla parametresi olduğu durumlarda, sınıfları kurucu modeli ile tasarlamak iyi bir seçenektir. Özellikle parametreler isteğe bağlı olduğu durumlarda. Kullanıcı kodunun okuması ve yazması daha kolay olur. JavaBeans yönteminden daha güvenli olur.