14 Temmuz 2011 Perşembe

Paketler ve Sınıf Yolu

Okumakta olduğu üniversitenin kütüphanesinde kitap arayan hevesli bir öğrenciyi düşünün. Bu öğrenci, belli bir düzene göre düzenlenmiş kütüphaneyi bu düzene uygun biçimde dolaşarak arayışını kolaylaştırabilir. Örneğin, kitapların fakülte, dal ve konu sıradüzenine (hiyerarşi) göre gruplanması durumunda, programlama dersine dair bir kitabı arayan öğrenci, arayışını önce mühendislik kitaplarına, sonra mühendislik kitapları içindeki bilgisayar mühendisliği kitaplarına ve nihayet bilgisayar mühendisliği kitapları içindeki programlama kitaplarına kısıtlayacaktır. Son adımda ise, konuya dair kitaplar içinde yapılan aramayı takiben, istenen kitap ya bulunacak ya da bulunamayacaktır. Kütüphanedeki kitap sayısı düşünüldüğünde, bu yöntemin kütüphanedeki tüm kitapları baştan sona sırasal bir biçimde tarayarak aramaktan çok daha verimli olacağı kesindir.

Java platformundaki türler de (sınıf ve arayüzler) kütüphanedeki kitapları andıran bir sıradüzenine konulur. Aynı konudaki kitapların kütüphanede aynı bölgeye yerleştirilmesinde olduğu gibi, ilişkili türler aynı paket içine konulur. Gerektiği takdirde, birbirine yakın kavramların gruplandığı paketler bir üstpaket içine yerleştirilebilir. Paket içine paketlerin ve/veya türlerin konulması ile oluşturulan bu sıradüzeninin amacı, platformdaki türlerin algılanmasını ve ilişkin sınıf dosyalarının ihtiyaç duyan paydaşlarca bulunmasını kolaylaştırmaktır.

Paket oluşturmanın bir diğer sebebi, farklı kişiler tarafından hazırlanmış türlerin ad çakışması olmadan kullanılmasını olanaklı kılmaktır. Ne kastettiğimizi, bir sergideki tabloların envanterini tutmak için yazılmış olan (Prog.java dosyasındaki) Java programının resmedildiği aşağıdaki şekil üzerinden görelim.1


abc şirketi tarafından hazırlanan ve nesne kabı olarak hizmet veren Tablo sınıfı ile, def şirketince gerçekleştirilen ve sanat eseri olan tablo kavramını soyutlayan aynı adlı sınıftan yararlanılan Prog.java içindeki tür tanımlarının başarılı bir biçimde derlenebilmesi için aynı ada sahip her iki sınıfın birbirinden ayırt edilmesini mümkün kılacak bilginin sağlanması veya sınıf adlarından birisinin ya da her ikisinin de değiştirilmesi gerekir. Kaynak kodun elimizde olmayabileceğini düşünürsek, ad değiştirmenin çoğu zaman pek de uygulanabilir bir seçenek olmadığı görülecektir. Yapılması gereken, her iki şirketin de olası kullanım senaryolarını göz önünde bulundurarak tür tanımlarını diğer şirketlerinki ile çakışma ihtimali olmayan bir paketin içine koymasıdır. Bu bağlamda önerilen yöntem, şirketin İnternet üzerindeki varlığını temsil eden ve diğer şirketlerinki ile çakışması olanaksız olan alan adının kullanılmasıdır. Ek olarak, şirket içindeki yazılım envanterini tutmayı kolaylaştırmak adına, bazı ek bilgiler kullanılabilir. Buna göre, abc şirketinin ürettiği Tablo.java dosyasındaki Tablo sınıfının aşağıdaki gibi yazıldığı düşünülebilir: Türkiye'deki bir ticari şirket olan abc'nin yazılım mimarlarınca belirlenen kurallara bağlı olarak, Tablo sınıfı bakımını kolaylaştırmak adına diğer veri yapıları ile birlikte bulunabileceği vy paketine koyulmakta.

Tablo.java
package tr.com.abc.vy;

...

public class Tablo {
  ...
} // Tablo sınıfının sonu

Bu noktada, hemen ilk uyarımızı yapalım: dosya içinde tanımlanan türlerin ait olduğu paketi ilan eden package bildirimi, kaynak kodun yorum satırları dışındaki ilk öğesi olmalıdır. Ayrıca, aynı dosyada sadece bir paket bildirimi bulunabilir.

Yapılan tanımların paketlere yerleştirilmesi, söz konusu türlerin kullanımı aşamasında bazı şeylere dikkat edilmesini gerektirir. Öncelikle, atıfta bulunurken artık türün yerleştirildiği paket de hesaba katılmalıdır; türün adı kaynak dosyada sağlanan ilişkin tanımdaki ile aynı değildir. Örneğin, tr.com.abc.vy paketindeki Tablo sınıfı, tr.com.abc.vy.Tablo adına sahiptir, Tablo değil. Dolayısıyla, bu sınıfın nesnesi aşağıdaki gibi yaratılmalıdır.

...

tr.com.abc.vy.Tablo tablo =
  new tr.com.abc.vy.Tablo(...);

Yukarıdaki gibi uzun adların kullanılmasının doğuracağı olası yazım hataları iki şekilde azaltılabilir. Öncelikle, programlama dilinden bağımsız bir şekilde kullanılan geliştirme ortamlarındaki ad tamamlama özelliğinden yararlanılabilir. Java programcısı olarak bizi ilgilendiren ikinci seçenek ise, package bildiriminin kullanıcı tarafındaki tamamlayıcısı olarak görülebilecek import bildirimidir. Buna göre, Tablo sınıfı aşağıdaki gibi de kullanılabilir.

...
import tr.com.abc.vy.Tablo;
...
Tablo tablo = new Tablo(...);

import bildirim(ler)inin package bildiriminin ardından gelmesi gerektiği unutulmamalıdır. Ayrıca, java.lang paketinin içindeki türlerin özel bir şekilde ele alındığı ve Java derleyicisi tarafından otomatikman program içine getirildiği hatırda tutulmalıdır.

Program içinde yararlanılan tür sayısının artmasıyla birlikte import bildiriminin jokerli uyarlamasının kullanımı düşünülebilir. Bu bildirim, söz konusu paket içindeki tüm türlere kısa adlarıyla atıfta bulunmayı olanaklı kılar.

...
import tr.com.abc.vy.*;
...
Tablo tablo = new Tablo(...);

Bu noktada bir uyarının daha yapılması yerinde olacaktır: bir paketin jokerli import bildirimi ile görünür kılınması, paketin "içindeki" diğer paketlerin görünürlüğünü etkilemez; tüm paketlerin ayrı ayrı görünür kılınması gerekir. Çünkü, paketler sadece türler için aduzayı görevi görürler, diğer paketler için değil. Mesela, java.util paketinin görünür kılınması java.util.regex paketinin içindeki sınıfları görünür hale getirmeyecektir.

Değinilmesi gereken bir diğer bildirim, J2SE 5 ile birlikte Java'ya eklenen static import bildirimidir. Bu bildirim, bir sınıfın—paketin değil!—static bir öğesinin (veya tüm static öğelerinin) sınıf adı ile nitelenmeksizin kullanılabilmesini sağlar. Buna göre, aşağıdaki kodda java.lang.System sınıfının tüm static öğeleri sınıf adıyla nitelenmeksizin kullanılabilirken, java.lang.Math sınıfının yalnızca PI adlı özelliği benzer biçimde kullanılabilecektir.

...
import static java.lang.Math.PI;
import static java.lang.System.*;
...
double alan = PI * Math.pow(yarıçap, 2);
out.println("Alan: " + alan);

Gerçekleştirimci tür tanımını package bildirimi ile bir pakete koyar, kullanıcı söz konusu türü kısa adı ile kullanmak isteyecek olursa import bildiriminden yararlanır demekle işimizin bittiğini zannediyorsanız, üzülerek yanıldığınızı söylemeyelim. Kullanıcı, program içinde yararlanılan türlere ait gerçekleştirimleri içeren sınıf dosyalarının nerede aranması gerektiğine dair bilgiyi derleyiciye sağlamalıdır. Kütüphanede bıraktığımız hevesli öğrenciye dönecek olursak, öğrencimizin bir şekilde kütüphanenin bulunduğu yeri bilmesi gerekir. Aslına bakılırsa, öğrencimizin biraz daha azimli davranıp, aranan kitabın bulunmaması halinde aramaya devam edeceği diğer kütüphanelerin adresini öğrenmesinde yarar olacaktır. Buna göre, kitap arama aşağıdaki adımlar izlenerek yapılır diyebiliriz.

  1. Adres listesinden bir sonraki kütüphanenin adresini bul. Böyle bir adresin olmaması durumunda, boynunu bükerek evine dön.
  2. Adreste belirtilen kütüphanede aramaya başla.
  3. Aramanın başarıyla sona ermesi durumunda kitabı çantaya koy. Aksi takdirde, 1. adıma git.
  4. Kitabın gerekli bölümlerini oku.

Algoritmamızı Java derleyicisinin2 sınıf dosyası arama algoritmasına uyarlayacak olursak...

  1. Sınıf yolu listesinden bir sonraki sınıf yolu dizininin konumunu bul. Böyle bir konumun olmaması durumunda, kullanılmak istenen türe dair tanımın bulunmadığını ilan et ve derlemeyi başarısızlıkla bitir.
  2. Sınıf dosyasını sınıf yolu dizininden başlayarak ara. Bunun için, türün uzun adındaki '.' karakterlerini yol ayırıcı karakteri—Unix temelli sistemlerde '/', MS Windows temelli sistemlerde '\'—ile değiştir. Buna göre, tr.com.abc.vy.Tablo sınıfı sınıf yolu dizininin içindeki tr/com/abc/vy altdizininde aranacaktır.
  3. Aramanın başarıyla sona ermesi durumunda sınıf dosyasını belleğe yükle.. Aksi takdirde—yani, Tablo.class dosyasının bulunmaması durumunda— 1. adıma git.
  4. Türün kullanımı noktasında, kullanımın doğruluğunun denetimi için belleğe yüklenen dosyadaki bilgilere başvur.

Benzetmeyi tamamlamak için yapmamız gereken, sınıf yolu listesini Java derleyicisine aktarmak. Bu, sınıf dosyalarının aranmaya başlanacağı dizinlerin konumlarını tutan CLASSPATH ortam değişkenine PATH ortam değişkenine benzer bir şekilde değer sağlanması yoluyla olur. Bir diğer yöntem, Java derleyicisine -cp opsiyonu ile uygulama bazında bu bilginin verilmesini içerir. Aşağıda her iki yöntemin birlikte kullanımına dair bir örnek verilmektedir.3

javac -cp ".:/Java Paketleri/sergi:$CLASSPATH" Prog.java

Prog.java dosyasındaki Java programlama dili tanım(lar)ını derleyen bu komut, kullanım doğruluğunu denetlemek için ihtiyaç duyulan türlerin karşılığındaki sınıf dosyalarını önce derlenmekte olan kaynak dosya ile aynı dizinde, sonra /Java Paketleri/sergi ve nihayetinde CLASSPATH ortam değişkeni ile belirtilen dizinler içinde arayacaktır. Arama sırasında sınıf dosyasından daha sonra güncellenmiş aynı adlı bir .java dosyasına rastlanması durumunda, arama başarıyla bitmiş kabul edilecek, derleyici kaynak dosyayı derleyerek denetimlerinde bu derlenmiş dosyadan yararlanacaktır.

Son olarak, bu günceyi daha önce ziyaret edip de kod örneklerine göz atmış olanlardan bazılarınızın, package bildirimi olmaksızın yazılan türlere ne olduğunu sorduklarını duyar gibiyim. Bu haklı sorunun yanıtını vererek yazımızı bitirelim: söz konusu türler, varsayılan paket dediğimiz ve kaynak kodun bulunduğu dizinle ilişkilendirilen paketin içinde konuluyordu.



  1. Şekli bir yerden hatırladıklarını düşünenler belleklerinin oynadığı bir oyuna kurban edilmiyorlar; benzer bir şekil, Java adıyla birlikte anılan kavram ve kısaltmalara değindiğimiz yazıda🔎 da vardı. Dolayısıyla, bu yazıda değindiğimiz kavramların diğer JSM dillerinden yararlanılarak üretilmiş sınıf dosyaları için de geçerli olduğu unutulmamalı.
  2. Sınıf dosyası arama Java derleyicisine sınırlı bir işlem değil. Sınıf dosyaları ile işi olan tüm sistem yazılımlarının bu algoritmayı kullanması söz konusu olacaktır ki, buna en güzel örnek sınıf dosyalarını işleten JSM'dir.
  3. MS Windows temelli işletim dizgelerinde çalışanların ':' yerine ';', $CLASSPATH yerine %CLASSPATH% kullanmaları gerekecektir.

Hiç yorum yok:

Yorum Gönder

Not: Yalnızca bu blogun üyesi yorum gönderebilir.