Nesneye Yönelimli Programlamaya Giriş: Bir Örnekle Temel Kavramlar I

Sınıf, Nesne, Constructor, Destructor, Abstraction ve Encapsulation, Property

1960’lı yıllarının sonuna doğru kodlama karmaşıklığı ve kod uzunluğu sürekli artmaktaydı. Bu durum kodların iyileştirilmesini, hata ayıklamayı ve takım çalışmasını zorlaştırmaktaydı. Yazılım ortamı geliştiricileri bu zorlukları ortadan kaldırmak için gerçek hayatı göz önüne alıp modellenmesine olanak sağlayan Nesneye Yönelimli Yaklaşımını önermişlerdir. Günümüzdeki Java, C++ ve C# gibi başlıca programlama dillerinin hepsi bu yaklaşım üzerine inşa edilmiştir. Bu yazımda gerçek dünyadan bir modellemenin nasıl yapıldığını temel özellikler üzerinden anlatmayı ve bu modellemenin kod tarafına aktarılmasını anlatacağım.  

Sınıf vs. Nesne (Class vs. Object)

Sınıf deyince öğrencilerimin bazıları eğitim yılları, bazıları Darwin’in canlı sınıfları, bazılarının siyasi tarafı gelmektedir. Öncelikle belirtmeliyim ki biz sınıflara bölmekle uğraşan kişiler değiliz. Eğer sınıflara bölen kişiler olsaydık bizim yan bir dünyamız olan elektronik dünyası halen mevcut transistörleri sınıflama çalışırdı. Oysa ki elektronik dünyası mevcut durumları geliştirip yeni transistör yapabilmiştir. (Kaynaklar bölümündeki makaleyi öneririm.) Bizim bu dersteki amacımız kendi sınıflarımızı en iyi şekilde modellemek olacaktır. Bu günkü örneğin çocukluk yıllarımın rüyası olan futbol konusu olacak ve bir futbol oyununun temelini atacağız.

Bir sınıfta modele ait değişken ve metotlar (fonksiyon veya yordam) bulunduran temel bileşenimizdir. Bu bileşen üzerinden nesnelerimizi yaratıp, nesneler üzerinden değişken ve fonksiyonlarımıza ulaşabiliriz. Kısacası, Futbolcu bir sınıf iken Messi, Quresma, Ronaldo nesnelerimiz olacaktır. Öncelikle, her nesnenin ortak özelliklerini belirleyelim.

Sınıfımız bir ad değişkenin yanı sıra çalım, hız, şut, yetenek, defans, kafa, pas vb. bir çok özelliğimiz olabilir. Ad değişkeni nesne oluşturduğumuzda nesnenin diğer nesnelerden ayırt edilmesini sağlar. Diğer özellikler futbolcunun özelliklerini belirleyen değişkenlerdir. Bu değişkenler hesaplanabilirlik kavramına (Alan Turing ve Turing tezini araştırabilirsiniz) uygun olması için sayısal değerler olabilir.  Şimdi bu modelimizi koda dökelim.

4 adet değişken ve iki adet yordamdan oluşan bir sınıfımız var. public, nesnemizi oluşturduktan sonra bu değişken ve yordamlara sınıf dışından ulaşabileceğimiz anlamına gelir. (Daha geniş bilgi için erişim belirteçlerine bakın.) Şimdi bir nesne oluşturalım. Nesneyi bir sınıftaki yordam veya main bölümünün içine yazabilirsiniz. 
 

Yukarıdaki örnekte Futbolcu sınıfından iki futbolcu nesnesi oluşturuldu. Bu iki nesnenin kendine ait özellikleri girildi. Özellik sayısı çok olduğunda teker teker girilmesi çok zor olacak. Şimdi bu basit sorunu nasıl çözüleceğine gelelim.
Constructor (Yapıcı)
Bir canlı dünyaya gelirken daha en başta bazı özellikleri belirlenir. NYP’da, sınıftan bir nesne oluşturduğumuzda özellikleri daha nesne oluşurken belirleme şansımız vardır. Yapıcı sınıf ile aynı isimde olur. Bir yapıcı, parametresiz veya dışarıdan parametre alabilir. Ayrıca aklımızda bulunsun aynı isme ama farklı parametre sayısına sahip birden fazla yapıcı tanımlanabilir. (Çok şekillilik konusunda göreceğiz.)   

Artık nesnemizi oluşturduğumuzda yukarıdaki 4 özelliğin girilmesi şarttır. Main’de tekrar düzenleme yaparsak:

Görüldüğü gibi ilk yaratma anında özelliklerimizi belirledik.  Kod kısmı sınıf içine taşındı ve main kısmındaki kod azaldı. Ayrıca main kısmında özellik unutulması durumunda IDE’miz bizi uyaracaktır. Bu tür hataları da önlemiş olacağız.

Destructor (Yıkıcı)

Nesne doğar, yaşar ve kaçınılmaz son ölüm. Nesnenin başlangıç özellikleri yapıcı ile belirledik. Nesnemiz, yaşaması sırasında yordam/lar ile bazı işlemleri yerine getirebilir. Ama ölüm günü gelecektir. Yıkıcı, ölümden önce sınıfın son işlemlerinin yapıldığı yordamdır. Yıkıcı da yapıcı da olduğu gibi sınıf ile aynı isme sahip olur ancak ~ karakteri ile başlar.

Destructor metodu illa yazmamıza gerek yok. Ama nesne bellekten çıkarılırken bazı işlemler yapmamız gerekebilir. Sınıfın son durumunu kaydetme, log tutma, başka sınıfları tetikleme vb. birçok işlem bu esnada yapılabilir. 
C++ gibi programlama dillerinde bir nesnenin bellekten alınması manuel bir süreç olarak yönetiliyordu. Bu bellekten alma işlemi için destroy gibi metotlar kullanılması gerekiyordu.  Java ve C#’ta bu işleme sadece gerektiğinde kullanılmasına imkan tanıyor. Başka bir deyişle destroy gibi bir işlem yapmamıza gerek yok. Garbage Collection (çöp toplama) mekanizması sayesinde işlemi biten nesneler bellekten siliniyor. GC’ı, Azrail’e benzetebiliriz. Destruct bölümü ise Azrail’in son isteklerimizi sorduğu bölüm ve son istekler alındıktan sonra ölen nesneler bellekten tamamen atılıyor.  
 

Abstraction (Soyutlama) ve Encapsulation (Saklama, Paketleme)

“Soyutlama” önemli özelliklere odaklanabilmek için ayrıntıları göz ardı etme sürecidir. Bir yazılım birçok sınıftan oluşabilir. Her sınıfın kendine ait özellikleri vardır. Soyutlamanın amacı her sınıfın özelliklerin iyi bir şekilde belirlenmesidir. Örneğin futbol oyunu futbolcu, futbolTopu, stadyum, fizik, forma, krampon gibi bir çok sınıftan oluşabilir. Her sınıfın kendine ait özelliklerinin belirlenmesi işlemine soyutlama diyoruz. 
Saklama, soyutlamayı desteklemek ya da güçlendirmek için bir sınıfın iç yapısının gizlenmesidir. Saklama sayesinde özelliklerinize ve yordamlarınıza ulaşılmayı engelleyebilirsiniz. Sınıf içindeki özellik ve yordamları yukarıda olduğu gibi public (genel) veya private (özel) olarak tanımlanabilir. Public, nesne oluşturulduktan sonra o özellik veya yordama ulaşabildiğimiz anlamına gelir. Private bir özellik veya yordama ise sadece sınıf içinden ulaşabiliriz.

Public olan özelliklerimize Private yaptık. Bundan sonra Main’den bu özelliklere ulaşamayız:

Sadece yapıcı üzerinden bu özelliklere ulaşabiliriz. Ama, nesne oluşturulduktan sonra ulaşma şansımız kalmamaktadır. Bu sayede sınıfımızdaki özellik ve yordamları saklama yapabilme özelliği kazandırdık. Soyutlamamıza bir aşama daha iyi hale getirdik. Fakat hala sorunlarla karşılaşabiliriz. Örneğin, hız özelliği 1-100 arasında olsun diyelim. 150 girersek ne olacak. Soyutlamamızda halen sorunlar var demek ki!!! 150 girilmesini nasıl engelleriz!!!
 Cevap:

Bir kaç çözüm şansınız var.
Birinci çözüm yönteminde yapıcı içine if şartımızı yazabiliriz.

Sadece hıza odaklandık ve diğer satırları yazmadık. (Bu kodu kopyala yapıştır yapmayın, çalışmaz!)
İkinci yöntem bir yordam üzerinden bu özelliği kontrol etmektir. Bunun için set_hiz adımda private bir yordam olsun.

void bir yordam değer döndürmez. set_hiz yordamı sadece yukarıdaki hız değişkenin içeriğine kontrollü bir şekilde ulaşılmasını sağlar.

Birinci sorunun cevabının sonunda set_hiz yordamını private yapmıştık. Eğer main bölümünden bu yordama ulaşmak istersek ne yapmalıyız.
Cevap:

Çok basit bir soru oldu. Cevap tabii ki Public yapmak olacak.

Hız değişkenini kontrollü biçimde değiştirebiliyoruz. Fakat, nesne oluşturulduktan sonra bu değişkenin içindeki değere sınıf dışından ulaşamıyoruz. Nasıl ulaşabiliriz.
Cevap:

Tabiki sınıf içindeki hiz değişkenini public yapıp değiştirebiliriz. Fakat bu durumda yazılan set_hiz yordamının bir işlevi kalmaz. Yine sınıf dışından 150 girilirse sınıf buna izin vermiş olur. Bu durumda bu değişkeni dışarıdan erişime açmak saçma olacaktır. bunun yerine hiz değişkeni türünden değer döndüren bir yordam yazarız. Bu yordamında ismi get_hiz olsun.

Hem set_hiz sayesinden kontrollü değiştirme şansına ulaştık, hem de get_hiz sayesinde hiz değişkenine girilen değeri kontrol altına aldık. Bu bölümü çok iyi anlamadan Property konusuna geçmeyin.

Property

Oluşturulan bu private değişkenlere kontrollü bir şekilde erişim sağlanmak için Property tanımlanmaktadır. Property (Özellik – bu özelliği yukarıda modellemede bahsedilen özellikle karıştırmayın. Karıştırmamak için Property demeyi tercih ettim.) sayesinde bu değişkenleri sadece get edebilir, sadece set edebilir ya da  aynı anda hem get hem de set edebilirsiniz. Son sorumuzda bir fonksiyonla bu işlemi yapabildiğimizi görmüştük. Ancak, kodlamada bu işlem çok standart hale geldiği için yazılım geliştiricileri set ve get olaylarını fonksiyon olarak yazmak yerine Property olarak yazmayı tercih etmeye başlamışlardır. 

İki fonksiyon için bir yordam içine set ve get bölümleri yazdık. P_HIZ yordamı bir property’dir.  Bu property bir yordamdaki gibi parametre almaz. set bölümü value değeri alır. value anlamı =’den sonra alınacak değer anlamına gelir. Bu değerin türü property’nin tanım kısmında belirtilir. Bu örnekte int olarak alınmıştır. get işlemi return edilecek değeri döndürür. Bu değerde property tanımlamasındaki değer türü ile aynı olmalıdır. Yapıcı bölümünde işlem property’nin set bölümününe ulaşır ve kontrollü bir şekilde değişikliği yapar. Özellikle bir değişkeni kontrol altına almayı düşündüğümüzde fonksiyon yerine property kavramını kullanabiliriz.
Bu yazımda basit bir tek sınıf üzerinde durduk. Sınıf tamamen size ait bir dünyadır. Tasarımı istediğiniz gibi yapabilirsiniz, fazlasıyla özgürsünüz. Ancak  tek sınıftan oluşmaz. Sınıflardan ve sınıflar arasındaki ilişkilerden oluşur. Kalıtım ve çok şekillilik gibi kavramlar üzerinde ayrıca duracağız.  
Yukarıdaki örnekte olduğu gibi kendi sınıflarınızı yaratın ve sınıfı daha kontrollü hale getirmeye çalışın. Bol kodlu günler…
 
KAYNAKLAR