Effective Java Madde 13: Sınıfların ve Üyelerinin Erişilebilirliğini Kısıtlayın

Cuma, 12 Ağu 2016 yorum yok

İyi tasarlanmış bir yazılım modülünü kötü tasarlanmış bir modülden ayıran en önemli faktör, içindeki verileri ve gerçekleştirim (implementation) detaylarını diğer modüllerden ne kadar iyi saklayabildiğidir. İyi tasarlanmış bir modül, gerçekleştirim detaylarını net bir biçimde diğer modüllerden gizler. Modüller birbirleriyle tanımladıkları API (Uygulama Programlama Arabirimi, UPA) üzerinden konuşurlar ve birbirlerinin iç dünyasından habersiz olmaları gerekir. Bu konsept, çok temel bir yazılım tasarım ilkesidir ve bilgi saklama yada kapsülleme (encapsulation) olarak bilinir.

Bilgi saklama konsepti birçok açıdan önemlidir. En önemli faydası ise yazılım modüllerini birbirinden ayrıştırarak (decoupling) birbirinden bağımsız bir şekilde geliştirilebilmesini, test edilebilmesini ve kullanılabilmesini sağlamaktır. Bu durum, bir sistemin geliştirilmesini hızlandırır çünkü farklı modüller paralel bir biçimde aynı anda geliştirilebilir. Ayrıca bakım yapmayı ve modülün anlaşılmasını kolaylaştırır, bir problem olduğunda diğer modülleri bozma riski olmadan hata araştırılıp, çeşitli çözümler denenebilir. Bilgi saklama kendi başına bir sistemin yüksek performansla çalışmasını sağlamasa da, birbirinden bağımsız modüllerin diğerlerini etkilemeden optimize edilebilmesini sağlar. Dolayısıyla sistemi test ederek hangi modülün yavaş çalıştığını tespit edebilir, sistemin diğer parçalarına zarar vermeden yavaş çalışan modülü optimize edebilirsiniz. Birbirinden bağımsız modüller aynı zamanda tekrar kullanılabilirliği (reusability) yüksek olan modüllerdir. Bilgi saklama tekniği büyük sistemler geliştirmenin riskini önemli ölçüde azaltır, çünkü sistem bir bütün olarak doğru çalışmasa da bağımsız modüllerin doğru çalıştığı kanıtlanabilir.
Devamını Oku…

FacebookTwitterLinkedInPocketEmailDiggDeliciousTumblrPaylaş

Effective Java Madde 12: Comparable Arayüzünü Gerektiğinde Uygulayın

Pazar, 31 Tem 2016 yorum yok

Bu bölümde gördüğümüz diğer metotların aksine, compareTo() metodu Object içerisinde tanımlanmış değildir. Bunun yerine Comparable arayüzünün tek metodu olarak karşımıza çıkmaktadır. Karakter olarak Object sınıfındaki equals() metoduna benzer ancak eşitlik karşılaştırması yanında sıralama da yapabilir ve üreyseldir (generic). Comparable arayüzünü uygulayarak, sınıfınızın nesneleri arasında sıralama yapılabileceğini belirtmiş olursunuz. Bu arayüzü uygulayan nesnelerden oluşan bir diziyi (array) sıralamak aşağıdaki gibi son derece kolaydır:

Arrays.sort(a);

Benzer şekilde, Comparable arayüzü uygulandığında arama yapma, uç değerleri bulma, kendiliğinden sıralı veri yapılarını yönetme gibi problemler de çok kolaylaşır. Örneğin, aşağıdaki program komut satırından girilen parametreleri alfabetik sırada yazdırmakta ve tekrar edilen değerleri elemektedir. Bu kodun doğru çalışması String sınıfının Comparable arayüzünü uygulamasıyla mümkün olmaktadır.

public class WordList {
        public static void main(String[] args) {
        Set<String> s = new TreeSet<String>();
        Collections.addAll(s, args);
        System.out.println(s);
    }
}

Devamını Oku…

Effective Java Madde 10: toString() Metodunu Her Zaman Geçersiz Kılın

Cumartesi, 28 May 2016 yorum yok

Her ne kadar java.lang.Object bir toString() gerçekleştirimi sunsa da, geri döndürdüğü karakter dizisi genellikle sizin sınıfınızı kullanmak isteyen bir yazılımcının görmek istediği şey değildir. Bu değer sınıfın adı, ardından gelen ‘@’ karakteri ve nesnenin hash kodunun onaltılık sistemde (hexadecimal) ifadesini içermektedir, örneğin “PhoneNumber@163b91”. toString sözleşmesi, bu metottan döndürülen değerin “kolay okunup anlaşılabilen, kısa ama bilgi verici” olması gerektiğini söyler. “PhoneNumber@163b91” değerinin kısa ve okuması kolay olduğunu savunabilirsiniz ancak “(707) 867-5309” ile karşılaştırıldığında bilgi verici olmadığı açıktır. toString sözleşmesi bunların haricinde “Bu metodu bütün alt sınıfların geçersiz kılması tavsiye edilir” demektedir. Kesinlikle uyulması gereken bir tavsiye!

Her ne kadar equals (Madde 8) ve hashCode (Madde 9) sözleşmeleri kadar kritik olmasa da, güzel bir toString gerçekleştirimi sağlamak sınıfınızı çok daha kolay kullanılabilir hale getirir. toString metodu, bir nesne printf, println gibi metotlara parametre olarak geçildiğinde, karakter dizilerine eklendiğinde veya assert ile birlikte kullanıldığında otomatik olarak işletilir.

Devamını Oku…

Effective Java Madde 9: equals() ile Birlikte Mutlaka hashCode() Metodunu da Geçersiz Kılın

Perşembe, 26 May 2016 yorum yok

Önemli Not: Hash tabanlı veri yapılarının Java’da nasıl çalıştığını bilmiyorsanız bu yazıyı okumadan önce araştırmanızı şiddetle tavsiye ederim.

Object sınıfından gelen hashCode metodunu gerektiği yerde geçersiz kılmamak (override) birçok hatanın kaynağını oluşturur. equals metodunu geçersiz kıldığınız her sınıfta hashCode metodunu da geçersiz kılmanız gerekir. Bunu yapmamak Object.hashCode metodunun sözleşmesini ihlal etmek demektir, bu da sınıfınızın hash tabanlı HashMap, HashSet, HashTable gibi veri yapılarıyla birlikte kullandığında yanlış çalışmasına yol açar. Bunun sebebi ise Object sınıfından gelen hashCode metodunun hash kodunu hesaplarken nesnenin o anda bulunduğu bellek adresini kullanmasıdır. Her nesnenin bellek adresi farklı olacağı için hesaplanan hash kodu da farklı olacaktır. Object sınıfı belirtiminde tarif edildiği üzere sözleşme genel olarak şu şekildedir:

  • equals karşılaştırmasında kullanılan alanlar sabit kaldığı sürece, hashCode metodu aynı uygulama içerisinde üst üste çağrıldığında her zaman aynı sonucu üretmelidir.
  • Eğer iki nesne equals metoduna göre birbirine eşitse, bu iki nesnenin hashCode metotları da aynı integer değerini üretmelidir.
  • Eğer iki nesne equals metoduna göre eşit değilse, hashCode metodu bu iki nesne için farklı integer sonuçları üretmek zorunda değildir. Ancak yazılımcı bilmelidir ki eşit olmayan nesneler için farklı hash kodları üretmek hash table performansını artırabilir.

Devamını Oku…

Java’da Enum Türleri

Perşembe, 23 Tem 2015 1 yorum

Merhabalar, Java dilinde Enum türlerini detaylarıyla anlatan Türkçe bir kaynak bulamadığım için bu yazıyı yazmaya karar verdim, umarım bu alandaki eksiği bir nebze olsun kapatır.

Java’da Enum türleri önceden tanımlanmış sabit değerleri ifade etmek için kullanılır. Peki bununla neyi kastediyoruz? Mesela en klasik örnek haftanın günleri. Bir haftanın kaç gün olduğu ve hangi günlerden oluştuğu bilindiğine göre bunu aşağıdaki gibi bir Enum türüyle ifade edebiliriz.

public enum Gun {
    PAZARTESI,
    SALI,
    CARSAMBA,
    PERSEMBE,
    CUMA,
    CUMARTESI,
    PAZAR
}

Devamını Oku…

Effective Java Madde 8: equals() Metodunu Geçersiz Kılarken Sözleşmeye Uyun

Pazartesi, 09 Şub 2015 yorum yok

Java dilinde bütün sınıfların atası olan Object içerisinde, eşitlik kontrolü amacıyla kullanılan equals() metodunu geçersiz kılmak (override) çok zor bir iş gibi görünmese de bunu yanlış yapmak aslında çok daha kolay. Yanlış şekilde geçersiz kılınmış bir equals() metodu uygulamada ciddi sorunlar çıkmasına yol açabilir. Bu tarz problemlerden kaçınmanın en kolay yolu bu metodu geçersiz kılmamaktır. Bu durumda her nesne sadece kendisine eşit olacaktır. Object içerisindeki equals() metodu çok basit bir şekilde aşağıdaki gibi tanımlanmıştır:

public boolean equals(Object obj) {
    return (this == obj);
}

Gördüğünüz gibi Object sınıfında tanımlanan equals() metoduna göre her nesne sadece kendisine eşittir. Aşağıdaki durumlardan birisi söz konusuysa bu metoda dokunmamak en doğrusudur:
Devamını Oku…

Categories: Java Tags: ,

Effective Java Madde 7: Finalizer (Sonlandırıcı) Kullanmaktan Kaçının

Salı, 27 Oca 2015 1 yorum

Java’da sonlandırıcılar (finalizer) çoğu durumda gereksiz, tehlikeli ve tutarsız davranışlar sergileyen metotlardır. Kullandığınız taktirde anlam veremediğiniz hatalı davranışlar, kötü performans ve taşınabilirlik (portability) sorunlarıyla karşılaşabilirsiniz. Sonlandırıcı kullanmak sadece bir iki durumda işe yarayabilir, onları da yazıda göreceğiz. Ancak genel bir kural olarak sonlandırıcı kullanmaktan sakınmalıyız.

Öncelikle sonlandırıcı derken tam olarak neyi kastettiğimizi söyleyelim. Java’da bütün sınıfların atası olan Object içerisinde aslında hiçbir iş yapmayan finalize() metodu vardır. Eğer yazılımcı nesne bellekten temizlenmeden önce bir takım kaynakları serbest bırakmak istiyorsa, bu metodu override edebilir. Teorik olarak çöp toplayıcı, bir nesne tamamen erişilemez duruma geldiğinde bu metodu çağıracak ve sistemden silinmeden önce finalize() metodunun içeriği işletilecektir. Ancak pratikte bu yaklaşım çalışmamakta ve uygulamada ciddi sorunlara yol açmaktadır.

Devamını Oku…

Effective Java Madde 6: Erişilmeyen Nesnelerin Referanslarından Kurtulun

Çarşamba, 14 Oca 2015 1 yorum

C veya C++ gibi bellek yönetimini yazılımcının yaptığı dilleri kullandıktan sonra Java gibi çöp toplayıcı (garbage collector) mekanizmasına sahip bir dile geçiş yaptığınız zaman, yazılımcı olarak işinizin ne kadar kolaylaştığını farkedersiniz çünkü çöp toplayıcı sizin için bellekte kalmış kullanılmayan nesneleri temizleyecektir. Bu durum size Java ile kodlama yaparken bellek yönetimini düşünmek zorunda olmadığınız izlenimini verebilir ancak bu doğru değildir!

Hemen aşağıdaki yığıt (stack) kodunu inceleyelim:

// Bellek sızıntısını bulabilir misiniz?
public class Stack {

    // Yığıt yazarken generic türler kullanmak daha mantıklı olacaktır
    // ama buradaki amacımız farklı olduğu için üzerinde durmuyoruz
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
   
    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }
    
    /**
    * Dizide boşluk kalmamışsa, bir sonraki elemanın eklenebilmesini
    * sağlamak için diziyi genişletiyoruz
    */
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

Devamını Oku…

Effective Java Madde 5: Gereksiz Nesne Yaratmaktan Kaçının

Salı, 13 Oca 2015 2 yorum

Yazılım geliştirirken var olan bir nesneyi kullanmak, işlevsel olarak aynı işi yapan yeni bir nesne yaratmaktan genellikle daha faydalıdır. Nesnelerin yeniden kullanımı hem uygulamayı hızlandıracak hem de daha okunabilir bir kod yazmanızı sağlayacaktır. Eğer bir nesne değiştirilemez (immutable) ise, o nesne her zaman yeniden kullanılabilir.

Aşağıdaki kod gereksiz nesne yaratılmasına bir örnektir:

String s = new String("merhaba")   // YANLIS KULLANIM!!

Yukarıdaki kod her çalıştırıldığında, içeriği aynı olan yeni bir String nesnesi yaratılacaktır. Tırnak içerisindeki “merhaba”, Java’da zaten bir String nesnesini ifade eder ve gereksiz yere yapıcı metot (constructor) kullanarak yarattığımız bütün nesnelerle işlevsel olarak aynıdır. Dolayısıyla yukarıdaki kodun bir döngüde çalıştırıldığını düşünürseniz çok fazla sayıda gereksiz String nesnesi yaratılmış olur. Bu durumu engellemek için aynı kodu aşağıdaki gibi yazabiliriz:

String s = "merhaba"   // DOGRU KULLANIM!!

Devamını Oku…

Effective Java Madde 2: Çok Sayıda Parametreyle Karşılaştığınızda Builder Kullanın

Cuma, 12 Ara 2014 2 yorum

Daha önce birinci maddesini işlediğimiz Effective Java kitabının ikinci maddesiyle devam ediyoruz.

Sınıf yapıcılar (constructor) ve statik fabrika metotlarının (static factory methods) paylaştığı ortak bir kısıt vardır: çok sayıda parametre geçmeniz gerektiği zaman kullanışsız bir duruma gelirler. Örneğin, paket gıdaların üzerinde yer alan ve besin değerlerini gösteren bir etiketi Java’da bir sınıf kullanarak ifade etmeye çalıştığımızı düşünelim. Bu etiketin üzerinde kalori, kolesterol, protein, karbonhidrat, yağ, doğmamış yağ, kalsiyum, demir ve daha birçok besin türü görürüz. Ancak besinlerin bir çoğu için bu değerlerin büyük bir kısmı opsiyonel olacaktır, yani bu alanlara hiçbir atama yapmamıza gerek olmayacaktır.

Böyle bir sınıf yazmamız gerektiği zaman nasıl bir sınıf yapıcı veya statik fabrika metodu kullanırız? Genelde yazılımcıların aklına gelen ilk yöntem iç içe geçmiş yapıcı metotlar (telescobing constructors) kullanmak olur, yani sadece zorunlu parametreleri içeren bir yapıcı metot, ardından zorunlu parametreleri ve sadece bir opsiyonel parametre içeren başka bir yapıcı metot, ardından iki opsiyonel parametreli üçüncü bir yapıcı metot ve bu şekilde devam eder. Şimdi örnek kodu inceleyelim, kolaylık olması açısından sadece 2 tane zorunlu 4 tane de opsiyonel değişkenimiz olsun.

Devamını Oku…

%d blogcu bunu beğendi: