Sanıyorumki tüm nesne yönelimli programlama
dillerinde en önemli kavramlardan birisi, oluşturulan nesnelerin birbirleri ile
ilişkilerinin ne ölçüde olacağına karar verilmesidir. Bu kararı vermede, erişim
belirleyicilerinin gerçekten çok önemli bir yeri var. Özellikle C# dili ile
program yazarken, public, private gibi erişim belirleyicilerinin kullanımına
özen gösteririm. Nitekim bunları doğru yerlerde doğru amaçlar için ve doğru
yönlendirmeler ile kullanmak isterim. Diğer yandan, karmaşık ve kod satırlarının
sayısı 10 binler ile ifade edilebilecek projelerde, kodu kitap okur gibi
okuyabilmek, dilin temelinde yatan ancak çoğu zaman dikkat etmeden geçtiğimiz
unsurların iyi bilinmesini gerektirir. İşte benim inancıma göre bu temellerden
biriside erişim belirleyicileridir. Aslında sözü bu kadar uzatmanın anlamı yok.
Hemen harekete geçmeli ve icraatlar yapmalıyım diye düşünüyorum.
Her nesne yönelimli programlama dili
uygulayıcısının mutlaka ve mutlaka erişim belirleyicilerine aşinalığı vardır.
Hatta %100 olduğundan eminim. Benim aşinalığım ise C# programlama dilinden
geliyor. Bu hafta boyunca Java dilini öğrenmek için kendime ayırdığım
vakitlerde, erişim belirleyicileri ile ilgili olarak kaynakları inceledim.
Gerçekten Java dilinde'de çok büyük öneme sahip olduklarını ve iyi bilinmeleri
gerektiğini keşfetmiş olduğumu büyük bir gururla söylemek isterim :) Java
dilinde erişim belirleyicileri 4'e ayrılmaktadır. Bunları daha iyi
anlayabilmek için özel bir renklendirme stratejisi uyguladığım aşağıdaki tabloyu
oluşturdum.

Bu şekilde Java dilindeki erişim belirleyicilerini
değişik renkler ile göstermeyi tercih ettim. Neden bu tarz bir renklendirme
kullandığımı ise birazdan hep birlikte anlayacağız. Erişim belirleyicilerini
sınıflara, metodlar ve alanlara uygulayabilmekteyiz. Elbette erişim
belirleyicisinin tipine göre bir takım farklılıklarında olacağı söyleniyor.
Anlaşılan bu farklılıkları örnekleri yazdıkça daha iyi kavrayacağım. Artık işe
koyulmanın tam sırası. Sıcak bir fincan kahvemi yanıma alıyorum ve erişim
belirleyicilerini tek tek incemeleye başlıyorum.
Öncelikle private erişim belirleyicisinden
bahsedelim. Private isim anlamı ilede özel bir kullanıma sahiptir. Bu erişim
belirleyicisi sadece metodlara ve alanlara uygulanabilir. Onu özel yapan, sadece
tanımlanmış olduğu sınıf içerisinde kullanıma izin vermesidir. Bu, başka bir
paketten, varsayılan paketten hatta bu private üyeleri içeren sınıfın bulunduğu
aynı paketteki diğer sınıflardan bile erişimin gerçekleştirilemiyeceğini anlatır. Anlatır
anlatmasına ama, bunu örnekler ile incelemeden anlamak şu an için zor olabilir.
Bu nedenle hemen bir örnek geliştirmeye karar verdim. Her zaman olduğu gibi
kodları işleyebilmek ve erişim belirleyicilerinin paketler arasındaki
etkileşimini daha iyi anlayabilmek için örnek bir paket hazırladım.

Bu kez com.bsenyurt.Ers isimli bir paketi
kullanacağım. İlk örneğimde, private alanları ve metodları içeren bir sınıfım
var.
package com.bsenyurt.Ers;
public class PrivateD
{
private int Deger;
private void Metod()
{
Deger=10;
}
} |
Bu paket içerisinde tanımladığım PrivateD sınıfı
içinde Deger isimli bir private alan ve Metod isimli private bir yordam
tanımladım. Private erişim belirleyicisinin uygulattığı sınırlamaya göre bu alan
ve metoda sadece bu sınıf içerisinden erişebilirim. Nitekim, Deger alanının
değerini Metod yordamı içinden değiştirebilmekteyim. Fakat private
belirleyicisinin tanımı gereği öncelikle bu paket içindeki sınıfa ati private
üyelere, bu paketi uygulayan başka bir sınıf içinden erişememem gerekiyor. Bunu
ispat etmek zorunluluğunu elbette hissediyorum. İspat matematik bilmindede çok
önemli bir yere sahip. Hatırlıyorumda üniversite yıllarında sayısız ispat
yapardık. Sonuçta hipotezleri teoriye dönüştürür ve geçerliliği kanıtlanırdı.
Aynı şey bence bir programlama dilini öğrenirkende geçerli. Verilen
tanımlamaların ispatını yapmayı başarbilirsek o zaman dilde uzmanlaşmamız daha
kolay ve güçlü olur. Sözü uzatmadan private alanların bu tanımı gereği ilk
ispatı yapmak üzere bilgisayarımın herhangibir klasöründe bu paketi kullanacak
bir sınıf daha yazdım.
import com.bsenyurt.Ers.*;
public class Sinif
{
void Yaz()
{
PrivateD n=new PrivateD();
System.out.println("PrivateD nesnesi
oluşturuldu");
System.out.println("Deger="+n.Deger);
n.Metod();
System.out.println("Deger="+n.Deger);
}
} |
Bu sınıf içinde, com.bsenyurt.Ers paketi içindeki
PrivateD sınıfınından bir nesne örneği oluşturmaya ve, PrivateD sınıfı içindeki
Deger alanı ile Metod yordamlarını çağırmaya çalıştım. Sonuç olarak, sınıfı
derlemeye çalıştığımda aşağıdaki hataları aldım.

İspatın sadece ilk kısmı bitti ama. Sırada
com.bsenyurt.Ers paketi içinde yer alan başka bir sınıf içinden, PrivateD sınıfı
içindeki private üyelere erişebilip erişemiyeceğimi kontrol etmem gerekiyor.
Tanım gereği bu şekildede private üyelere erişememem gerekli. Bu amaçla,
com.bsenyurt.Ers paketi içinde başka bir sınıf tanımlıyorum.
package com.bsenyurt.Ers;
public class PrivateD
{
private int Deger;
private void Metod()
{
Deger=10;
}
}
class PSinif
{
void Deneme()
{
PrivateD pd=new PrivateD();
pd.Deger=50;
pd.Metod();
}
} |
Paketimin içinde PSinif isimli yeni bir sınıf
tanımladım ve bu sınıf içindeki Deneme metodundan PrivateD sınıfındaki özel
üyelere erişmeye çalıştım. Sonuç beklediğim ve umduğum gibi oldu.

Anlaşılan şu ki private üyelere, başka bir
paketten yada aynı paket içinden erişemiyoruz. Bu da private üyelerin sadece
tanımlandıkları sınıf içinden erişilebildikleri anlamına geliyor. Privatizim
adını vereceğim bir dogma burada kendini gösteriyor. Privatizim, aslında nesne
yönelimli programlama dillerinin, kapsülleme kavramının temelinde yatan
uygulanabilirliğin bir göstergesi. C# dilinde sınıf içindeki alanları dış
ortamlardan soyutlamak istediğimizde bunları private tanımlar ve bu alanlara
erişmek için özellikleri kullanırız. Aynı şekilde sadece sınıf içindeki özel
alanlar ile ilgili işlemleri yapacak özel sınıflarda tanımlar ve bunlarıda dış
ortamdan soyutlayarak kapsüllemeyi gerçekleştiririz. Aynı düşüncelere java dili
içinde geçerli.
Private kullanımına ilişkin kaynak araştırmalarımı
incelerken güzel bir örnek yakaladım. Bu örnekte, bir sınıfa ait yapıcı metod
private olarak tanımlanmıştı. Bunun doğal sonuçlarını oturup bir kaç saniye
düşündüğümde, bu yapıcının tanımlandığı sınıfa ait bir nesne örneğinin başka
paketler içindeki sınıflar içinden yada aynı paketteki başka sınıflar içinden
tanımlanamıyacağını farkettim. O halde bu tarz bir nesne örneğini nasıl
oluşturabilir ve alanlarına erişebilirdik. Herşeyden öte neden böyle bir
gereklilik olsun ki. Sebebi gayet basit. Bazen yapmış olduğumuz bir sınıfın
kesinlikle başka sınıflar içinden örneklenememesini isteyebiliriz. Sınıfımız
gerçekten başlı başına özeldir ve bu nedenle nesne örneğinin oluşturulabilmesi
yasaklanmıştır. Ancak bu sınıf içine koyacağımız static bir metod bizim
belirleyeceğimiz kurallar çerçevesinde, bu nesne örneğinin oluşturulmasına ve
kullanılmasına imkan sağlıyabilir.
Ne demek istediğimi son paragrafı tekrar
okuduğumda bende tam olarak kestiremedim. İşte keskinliği arttırmanın yolu.
Olayı gerçekçi bir örnek üzerinde düşünmek. Bu amaçla com.bsenyurt.Ers
paketindeki PrivateD sınıfının tanımını biraz değiştirdim. Öncelikle
private yapıcı metodu tanımladım.
package com.bsenyurt.Ers;
public class PrivateD
{
private int Deger;
private PrivateD()
{
}
private void Metod()
{
Deger=10;
}
}
class PSinif
{
void Deneme()
{
PrivateD pd=new PrivateD();
}
} |
Öncelikle görmek istediğim, varsayılan yapıcının
private tanımlanmasının, nesne örneğini oluşturmaya olan etkisi idi. Etki
aşağıdaki gibi ekrana yansıyıverdi.

Peki o halde böyle bir sınıfı nasıl
örnekleyebilirdim. Cevap biraz düşününce ortaya çıkıverdi. Static ve dışarıdan
erişilebilir bir metod içinde bu nesneyi tanımlayabilir ve sınıf ile ilgili
gerekli, istediğim düzenlemeleri bizzat sınıfı yazan kişi olarak ben
yaptırtabilirdim. Böylece bu sınıfımın başkaları tarafından ancak benim
belirlediğim kurallar çerçevesinde oluşturulabilmesini sağlardım. Bu düşüncemin
sonucunda aşağıdaki örnek ortaya çıkıverdi.
package com.bsenyurt.Ers;
public class PrivateD
{
private int Deger;
private PrivateD()
{
}
private void Yaz()
{
System.out.println(Deger);
}
public static void Kullan()
{
PrivateD pd=new PrivateD();
pd.Deger=10;
pd.Yaz();
}
} |
Şimdi bu paketi başka bir sınıfta kullanmayı
denemem gerekiyor. Bu amaçlada aşağıdaki örneği geliştirdim.
import com.bsenyurt.Ers.*;
public class Sinif
{
public static void main(String[] args)
{
PrivateD.Kullan();
}
} |
Tabi burada göze çarpan benim public erişim
belirleyicisini daha anlatmadan kullanmış olmam. Aslında public erişim
belirleyicisini diğerlerini olduğu gibi C# dilinden biliyorum. Bu nedenle static
olarak tanımladığım bu metoda dışarıdaki bir sınıftan erişebilmem için yani
paket dışından halka açık şekilde tanımlamam gerekliydi. Ama yazının ilerleyen
kısımlarında bu erişim belirleyicisinide detaylı bir şekilde inceleyeceğim. Ne
de olsa gözden bir şey kaçırmamak lazım. Uygulamayı başarılı bir şekilde
derledikten sonra çalıştırdım ve aşağıdaki ekran görüntüsünü elde ettim.

Private ile ilgili olarak söylenecek tek bir şey
kaldı. Aslında söylemekten ziyade şekil olarak private erişim belirleyicisinin
nasıl işlediğini daha iyi görselleyebilmek. Bu amaçla önce kara kalemimi aldım
ve kağıt üzerinde bir çizim yaptım. Kendimle mutabakata vardıktan sonra ise, bu
örneği dijital ortama geçirdim. Sonuçta aşağıdaki grafik ortaya çıktı. Ha bu
arada ilk şekilde private erişim belirleyicisini neden kırmızı renk ile
gösterdiğimi sanırımı anlamışsınızdır. Bu renk genelde erişimi kısıtlanmış olan
durumlar için kullanılır. Ancak kastettiğim elbetteki kırmızı trafik lambasıda
değil.

Şimdi sıra geldi friendly erişim belirleyicisini
incelemeye. Private erişim belirleyicisinden ziyade, friendly erişim
belirleyicisi Java dilinde varsayılan erişim belirleyicisidir. Yani bir üye için
erişim belirleyicisi belirtilmesse bu üye dost canlısı olarak kabul edilir.
Bununla birlikte friendly erişim belirleyicisi, alanlara, metodlara ve sınıflara
uygulanabilir. Özelliği ise şudur. Bu tipteki üyelere aynı paket içinde
erişilebilirken farklı paketlerden erişilememektedir. Elbette bu tanımı ispat
etmem gerekiyor. Haydi bakalım kolları sıvamanın zamanı geldi. Bir yudum kahve
dopingi ve ardından işte aşağıdaki örnek.
package com.bsenyurt.Ers;
class KardesSinif
{
int Deger;
void Degistir(int yeniDeger)
{
Deger=yeniDeger;
}
} |
Örnek sınıfı yine com.bsenyurt.Ers paketi içinde
oluşturdum. Burada sınıf için, int tipteki Deger değişkeni için ve Degistir
isimli metod için herhangibir erişim belirleyicisini kullanmadım. Böylece bu
üyelerin friendly erişim belirleyicisinine sahip olmasını sağlamış oluyoruz.
Tabi aklıma saf ve yeni bir java programcısı olarak, bu üyelerin başına friendly
anahtar kelimesini eklemekte gelmedi değil. Bunu yaptığım zaman yani örneği
aşağıdaki şekilde değiştirdiğimde, java derleyicisi bana " Ben herşeyin
farkındayım, zaten bir şey yazmasan friendly olduklarını anlarım, beni boşu
boşuna bir de bu yazdıklarını denetlemek zorunda bırakmanı anlamıyorum"
gibisinden bir hata mesajı verdi.
friendly package com.bsenyurt.Ers;
class KardesSinif
{
friendly int Deger;
friendly void Degistir(int yeniDeger)
{
Deger=yeniDeger;
}
} |
Ve java derleyicisinin karşılık olarak verdiği
tarihi cevap .

Boyumun ölçüsünü aldıktan sonra örneği eski haline
getirdim. Daha sonra sistemin her hangibir klasörü içinde com.bsenyurt.Ers
paketindeki KardesSinif sınıfını kullanacak bir örnek oluşturdum. Teori eğer
doğru ise, bu yeni örnek sınıfı içinden, KardesSinifi üyelerine erişememem
gerekiyor. Çünkü yeni örnek sistemin herhangibir yerinde varsayılan paket
özellikleri ile oluşturulacak. Bu amaçla aşağıdaki örneği geliştirdim.
import com.bsenyurt.Ers.*;
class Kardes
{
public static void main(String[] args)
{
KardesSinif ks=new KardesSinif();
ks.Degistir(15);
}
} |
Örnek elbette derlenmedi. Gerçektende,
com.bsenyurt.Ers paketi içindeki KardesSinifi sınıfını friendly olması nedeni
ile kullanamamıştım.

Anlaşılan friendly erişim belirleyicisine sahip
üyeler aile dışına çıkmayı pek sevmiyorlar. Aileden kastım elbette bu üyelerin
bulunduğu paket. Dolayısıyla Teorinin ikinci kısmına bakmak gerekiyor. Buna
göre, aynı paket içinden friendly üyelere erişilebiliyor. Bu amaçla,
com.bsenyurt.Ers paketi içinde aşağıdaki sınıfı oluşturdum. Bu sınıf içinden
açıkça, KardesSinif sınıfına erişmeye çalışılıyor. KardesSinif sınıfından ks
isminde bir nesne örneği yaratılıyor ve bu nesne üzerinden Degistir isimli metod
çağırılıyor.
package com.bsenyurt.Ers;
public class Kardes
{
public void Kullan()
{
KardesSinif ks=new KardesSinif();
ks.Degistir(15);
}
} |
Bu kez derleme işlemi başarılı bir şekilde
gerçekleştirildi. Burada herhalde el alışkanlığından olsa gerek, Kardes isimli
sınıf tanımının başına ve Kullan isimli metodun başına public erişim
belirleyicisini ekledim. Evet aslında public ile ilgili teorileri C# 'tan
bilmeme rağmen, Java'da nasıl olduğunu merak ediyorum. Ancak herşeyden önce,
friendly ile ilgili son bir şey yapmam gerekiyor. Friendly erişim
belirleyicisinin tanımını şekillendirmek.

Private ve friendly erişim belirleyicilerinin
kısıtlamalarından sonra şöyle kendimi özgür hissettirecek bir erişim
belirleyicisine ihtiyacım olduğunu düşünmeye başladım. Bu arzumun karşılığı
sanıyorumki public erişim belirleyicisi. Public erişim belirleyicisine sahip
sınıflar, metodlar ve alanlara, herhangibir paketten, aynı paketten, varsayılan
paketten kısaca heryerden erişilebilir. Bu kulağa hoş gelen bir tanım olmakla
birlikte, bazen programcıların ağır bedeller ödemesinede neden olmuştur. Bir
üyeyi herkese açmak güzel bir fikir gibi görünebilir. Ancak burada durum, Linux
gibi açık kaynaklı bir işletim sisteminin gelişiminden çok daha farklıdır.
Çoğu zaman iş uygulamalarında sınıfların belli
başlı üyelerini hatta sınıfların kendilerini dış dünyaya kapatmak ve denetimi,
kontrolü tamamen ele almak isteriz. Bazen sadece belli başlı üyelerin herkese
açık olmasını isteriz. Örneğin bir bankacılık uygulaması için, banka
müşterilerinin birer sınıf örneği olarak temsil edildiklerini düşündüğümüzde,
sadece yetkili personelin bu kişiye ait bilgileri görmesi gerekecektir. Hatta
müşterinin kendisi bile sınıf tanımı içindeki, hesap no yada bakiye gibi
bilgilere ulaşmak için, şifre ve daha fazlasına ödemek zorundadır. İşte bu gibi
bir iş uygulaması üyelerin son derece iyi planlanmasını gerektirir. Bir müşteri
sınıfını herkese kapatabilirsin. Ancak, bu sınıf içindelki özel bilgileri
private yapmak, bu sınıfın yer aldığı paketler üzerinde çalışan programcıların
kullanabilmesi için friendly üyeler açmak ve şifre sorgulama yada kullanıcı
doğrulama gibi işlemleri herkesin kullanımına açan public metodlar uygulatmak
daha mantıklıdır.
O nedenle public hem iyidir hemde iyi değildir. Bu
nedenle her zaman inandığım bir şey vardır. Bir uygulama nesne yönelimli
programlama dilleri üzerinde koşacaksa, onu koşturmadan önce gerçekten iyi
tasarlanmalıdır. Yapılan herşey, belkide 3 kez kendimize sorulup, 3 kez program
ekibine sorulup ve üç kezde genel müdüre sorulup ondan sonra yürürülüğe
girmelidir. Evet 3 kez sormak biraz abartı gelebilir ancak 3 güvenlik
görevlisinin, genel müdürün, program ekibinin ve hatta sayısız 3'ün bir araya
gelerek oluşturduğu kalabalık bir müşteri grubunun bizi kovalamasından iyidir.
Neyse bu kadar abartı senaryolar gözümüzü korkutmamalı değil mi? Sonuç olarak
Java'yı bu kahve molasında ilerletmek zorunluluğunu hissediyorum. O halde
beklemenin anlamı yok. Sırada public erişim belirleyicisi var.
Aslında public erişim belirleyicisi ile ilgili
söylenecek fazla bir şey yok. Bu erişim belirleyicisini incelemek için
com.bsenyurt.Ers paketi içine aşağıdaki sınıfı ekledim.
package com.bsenyurt.Ers;
public class Topla
{
public int Toplam(int d1, int d2)
{
return d1+d2;
}
public int Eleman=10;
} |
Şimdi ilk denemek istediğim bu sınıf içindeki public üyelere aynı
paket içinden erişip erişemiyeceğim. Bu amaçla, com.bsenyurt.Ers paketi içinde
yer alan bir sınıfı aşağıdaki şekilde düzenledim.
package com.bsenyurt.Ers;
public class PSinif
{
public void ToplamAl()
{
Topla t=new Topla();
int t1=t.Toplam(10,11);
int t2=t.Toplam(12,12);
int t3=t.eleman;
t.eleman=1;
}
} |
PSinif sınıfı içinden, aynı pakette yer alan,
Topla isimli sınıfın, public üyelerine erişmeye çalıştım. Bu sınıfı derlediğimde
herhangibir hata mesajı almayışım, bu erişimin geçerli olduğunu göstermekteydi.
Sırada, bu public üyelere başka bir paket içerisinden erişip erişemiyeceğimin
ispatı var. Bunun için, com.bsenyurt.Yazi paketi içindeki Temel sınıfını
kullandım. Bu sınıfın kodlarına, com.bsenyurt.Ers.PSinif sınıfındaki ToplamAl
metodunun aynısını ekledim.
package com.bsenyurt.yazi;
import com.bsenyurt.Ers.Topla;
public class Temel
{
public void Uzunluk(String metin)
{
System.out.println(metin.length());
}
public void ToplamAl()
{
Topla t=new Topla();
int t1=t.Toplam(10,11);
int t2=t.Toplam(12,12);
int t3=t.eleman;
t.eleman=1;
}
} |
Sonuçta, bu sınıfta başarılı bir şekilde derlendi.
Yani, public üyelere, başka bir paket içindende erişebilmiştik. Aşağıdaki şekil
sanıyorumki Public erişim belirleyicisinin davranışını çok daha net açıklıyor.

Erişim belirleyicilerinde son olarak protected
erişim belirleyicisi kaldı. Bu erişim belirleyicisi aslında kalıtım kavramı ile
ilişkili. Hazır kalıtım demişken bir sonraki kahve molasında bu konuyu işlemeye
çalışacağım. İşte o zaman, protected erişim belirleyicisinin işlevini daha iyi
anlayabileceğimi sanıyorum.
Kahvemden son bir yudum aldığımda aklıma, erişim
belirleyicilerinin etki alanlarını gösteren bir tablo yapmak geldi. Bunun için
bir süre uğraşmam gerekti ama sonunda başardım. Bu tablo erişim
belirleyicilerinin etki alanlarını tam olarak açıklayabiliyor. Bu tablo aynı
zamanda, buraya kadar işlediklerimin özetlenmesindede bana oldukça yardımcı
oldu.
| |
Aynı Paket |
Farklı
Paket |
Tür. Paket |
|
Sınıf |
public |
|
|
|
|
friendly |
|
|
|
|
Metod |
public |
|
|
|
|
private |
|
|
|
|
friendly |
|
|
|
|
protected |
|
|
|
|
Alan |
public |
|
|
|
|
private |
|
|
|
|
friendly |
|
|
|
|
protected |
|
|
|
Bu tablo erişim belirleyicilerinin etki alanlarını
kolayca anlamama yaradı. Kırmızılar erişimin olmadığını, yeşiller ise erişim
olduğunu gösteriyor elbette. Tablonun okunması ise son derece kolay. Örneğin,
Sınıfları ele alalım. Sınıflarda dikkat edilmesi gereken nokta, protected yada
private sınıfların tanımlanamadığı. Diğer yandan public bir sınıfa her yerden
erişebiliyoruz. Aynı paket içinden, farklı bir paket içinden, hatta türetilmiş
bir paket içindende. Lakin friendly olarak yani dost canlısı bir sınıf
bildirdiğimizde bu sınıfa sadece aynı paket içinden erişilebilmekte. Bu okuma
şekli alanlar ve metodlar içinde geçerli.
Son yudumuda bitirdim. Bir kahve molam daha sona
erdi. Önümüzdeki hafta gözüm, kulağım, aklım, kalıtım konusunda olucak.
Kalıtımın nesneye dayalı programlama modelinde önemli bir yeri var. O kahve
molama kadar çoook çalışmam lazım. Çooooookkk.
Burak Selim ŞENYURT
selim@bsenyurt.com