Press "Enter" to skip to content

Effective Java Madde 57: Yerel Değişkenlerin Etki Alanını Kısıtlayın

Bu madde ile Madde 15: Sınıfların ve Üyelerinin Erişilebilirliğini Kısıtlayın benzer tavsiyelerde bulunmaktadır. Yerel değişkenlerin etki alanını (scope) kısıtlayarak hem kodunuzun okunabilirliğini ve bakım yapılmasını kolaylaştırırsınız, hem de hata yapılmasının önüne geçmiş olursunuz.

C gibi eski programlama dillerinde, yerel değişkenlerin blokların başında tanımlanması zorunluydu ve bazı programcılar da bunu alışkanlık haline getirdiler. Ancak bu terketmeye değer bir alışkanlıktır! Java değişkenlerin tanımlanabileceği yerler açısından oldukça esnektir.

Yerel değişkenlerin etki alanının kısıtlanması konusunda uygulanabilecek en iyi yöntem, değişkeni ilk defa kullanıldığı yerde tanımlamaktır. Bir değişken kullanılmadan önce tanımlanmışsa, kodu dağınık göstermekten başka bir işe yaramayacaktır. Kodu okuyup anlamaya çalışan kişilerin dikkatini bozacaktır. Okuyucu, değişkenin kullanıldığı satıra geldiğinde belki de değişkenin tipini ve değerini hatırlamayacaktır.

Bir değişkeni gereğinden önce tanımlamak etki alanının (scope) sadece erken başlamasına değil aynı zamanda geç bitmesine sebebiyet verebilir. Etki alanı değişkenin tanımlandığı anda başlar ve içinde bulunduğu blok sonlanana kadar devam eder. Eğer değişken kullanıldığı bloğun dışında tanımlanmışsa, değişkene ihtiyaç kalmadığı durumda bile erişilebilir olacaktır. Bu durum değişkenin yanlış bir noktada yanlışlıkla kullanılması ihtimalini doğurur, bu da kötü sonuçlara sebebiyet verebilir.

Hemen hemen bütün yerel değişken tanımlamaları, bir ilk değer ataması (initialization) içermelidir. Eğer değişkeni tanımlarken hangi değeri atamanız gerektiğini bilemiyorsanız, değişken tanımlamasını ertelemelisiniz. Bunun bir istisnası try-catch bloklarıdır. Eğer ilk değer ataması için çalıştırılan kod bir denetimli aykırı durum (checked exception) fırlatıyorsa, ilk değer atamasını try bloğu içerisinde yapmanız gerekir. (tabi eğer içinde bulunduğunuz metot aykırı durumu tekrar fırlatmıyorsa) Ve bu değer try bloğunun dışında kullanılacaksa, değişkene ilk değer ataması yapmadan try bloğu dışında tanımlamanız gerekir.

Döngüler değişkenlerin etki alanının sınırlandırılması için bize güzel bir fırsat sunarlar. Hem geleneksel for döngüsü hem de for-each döngüsü, bize etki alanları sınırlandırılmış döngü değişkenleri tanımlama şansı verir. Döngü değişkenlerinin etki alanı, tanımlandığı parantezin içerisi ve döngü kodunu oluşturan bloktan ibarettir. Bu sebeple, eğer döngü değişkenleri döngüden çıktıktan sonra kullanılmayacaksa, while yerine for döngüleri kullanın.

Örneğin, bir koleksiyon üzerinde tarama yapan aşağıdaki döngüye bakalım:

 // Bir koleksiyon veya diziyi taramanın tercih edilen yolu
for (Element e : c) {
    ... // e değişkeniyle istediğimizi yapabiliriz
}

Eğer yineleyiciye (iterator) erişmek istiyorsanız (mesela remove metodunu çağırmak için bu gereklidir), bu sefer geleneksel for döngüsü kullanabilirsiniz:

// Yineleyici gerektiğinde tarama yapan kod
for (Iterator<Element> i = c.iterator(); i.hasNext(); ) { 
    Element e = i.next();
    ... // i ile istediğimizi yapabiliriz
}

Neden while yerine for döngüsü önerdiğimizi daha iyi anlayabilmek için şimdi aşağıdaki örneğe bakalım:

Iterator<Element> i = c.iterator(); 
while (i.hasNext()) {
    doSomething(i.next()); 
}
....
Iterator<Element> i2 = c2.iterator(); 
while (i.hasNext()) {        // HATA!!!
    doSomethingElse(i2.next()); 
}

İkinci döngüde programcı kopyala-yapıştır hatası yapmıştır. c2 koleksiyonunu taramak için yeni bir i2 iteratorü tanımlamıştır ancak while döngüsü içinde eski i değişkenini kullanmaktadır. Maalesef i değişkeninin etki alanı hala devam ettiği için kod sorunsuz bir şekilde derlenmekte ve bir aykırı durum fırlatmadan çalışmaktadır. Ancak sorun şu ki yanlış işlem yapmaktadır! İlk döngüde i iteratörü üzerinden bütün elemanlar ziyaret edildiği için, ikinci döngüye hiç girilmiyor. Bu da c2 koleksiyonu boşmuş gibi çalışmasına sebep oluyor. Bu sessiz bir hata olduğu için bulunması uzun zaman mümkün olmayabilir!

Eğer aynı kopyala-yapıştır hatasını for döngülerinden herhangi birisini kullanırken yapsaydık kod derlenmezdi ve hatayı hemen görmüş olurduk. Çünkü birinci döngüde tanımlanan i değişkeni ikinci döngü başladığında etki alanını kaybetmiş olacaktır. Şimdi koda bakalım:

for (Iterator<Element> i = c.iterator(); i.hasNext(); ) { 
    Element e = i.next();
    ... // e ve i ile istediğimizi yapabiliriz
} 
...
// Derleme hatası! - cannot find symbol i
for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {
    Element e2 = i2.next();
    ... // e2 ve i2 ile istediğimizi yapabiliriz
}

Dahası, for döngüleri kullanırken bu tarz hatalar yapma şansınız zaten düşüktür çünkü farklı değişken isimleri kullanmak için bir sebep yoktur. Döngüler birbirlerinden tamamen bağımsız olduğu için, değişken isimlerinin tekrar tekrar kullanılmasında bir sakınca yoktur.

For döngülerinin while döngülerine göre bir diğer avantajı da daha kısa ve anlaşılır olmalarıdır. Aşağıdaki döngü de yine yerel değişkenlerin etki alanlarının sınırlandırılmasına güzel bir örnektir:

for (int i = 0, n = expensiveComputation(); i < n; i++) { 
    ... // i ile istediğimizi yapabiliriz;
}

Bu koddaki önemli nokta iki tane döngü değişkeni tanımlanmış olmasıdır. i ve n döngü değişkenlerinin etki alanı aynıdır. n değişkeninin değeri, i‘nin limitini daha tanımlandığı anda belirlemektedir. Böylece her iterasyonda yapılacak maliyetli bir hesaplamanın önüne geçilmiştir. Eğer her iterasyonda aynı sonucu üreten metot çağrılarınız varsa, bunları döngü değişkeni olarak tanımlamak daha doğru olacaktır.

Yerel değişkenlerin etki alanını kısıtlamak için kullanılabilecek bir diğer yöntem de metotları kısa tutmak ve aynı metotta birden fazla iş yapmamaktır. Eğer bir metotta iki veya daha fazla iş yapmaya kalkarsanız, bunlardan birini yaparken tanımladığınız değişkenlerin etki alanı diğer işlemleri yaparken kullandığınız değişkenlerle karışabilir. Bunu önlemek için bu işlemleri farklı metotlarda yapabilirsiniz.

Share

Leave a Reply

%d bloggers like this: