HELLO, I’M SERHAT AND THIS IS MY FANCY TITLE.

Ruby günlüğü - gün 3

Öncelikle Ruby pure-object-oriented bir dil olduğu için, nesne tabanlı programlama hakkında bir kaç noktaya değinmek gerekir, aksi halde ilerleyen konularda nesne, sınıf, fonksiyon gibi kavramlar karışacaktır. Ben daha önce çok derinlemesine olmasa bile bir Python tecrübem olduğu için ve bu sorunları Python çalışırken yaşamış olduğum için, bi kez tekrarlamakta fayda görüyorum. OOP kullanmak zorunlu değildir, ama bilinmesi gerekir ve kullanılsa iyi olur. Neden mi? Yazının devamında bunu da açıklamaya çalışacağım.

Nesne ve Sınıflarla Birlikte Ruby

Ruby'nin bir OOP dili olduğunu biliyoruz ve bunu en kısa şekilde, Ruby programlarının gerçek hayat konseptlerini tanımlayabilmesi ve yönetebilmesi olarak özetleyebiliriz. Herhangi bir Ruby programı; insanlar, kutular, biletler, haritalar gibi çalışmak istediğimiz birçok konsepti içerebilir. Ruby bir OOP dili olması sebebi ile, oluşturduğumuz bu konsepler arasında ilişki kurabilir. Bu konseptlere birazdan, sınıf ismini vereceğiz.

Örnek vermek gerekirse, spor müsabakaları için bir bilet sistemi programı kodlamayı düşünüyoruz. Elimizde temel ve ortak olarak insan, bilet, yer, etkinlik gibi konseptler var. Bu konseptleri iki farklı etkinlik için de kullanabilmeliyiz. Örneğin şampiyonlar ligi finali içinde bunları kullanmamız gerekli, üniversiteler arası basketbol turnuvası içinde. Ruby'de OOP'ye sağdık kalarak ve ilişkilendirmeler kurarak farklı etkinlikler için belirli konseptleri tek bir sınıf altında toplayabiliriz. Daha önceki yazıda örnek olarak bir insan oluşturmuş ve ilişkilere göz atmıştık, şimdi OOP bilgisi ile o yazıyı okursanız daha net olacaktır.

Hatırlamak gerekirse:

class Insan
  attr_accessor :isim, :yas, :cinsiyet
end

Yukarıdaki örnekte ilk olarak Class Insan ile bir insan konsepti tanımladık. attrnin attribute (özellik) manasına geldiğini daha önce söylemiştim. accessor ise, Ruby'ye tanımlanan özelliklerin istenilen zamanda atanabilmesini ve değiştirilebilmesini sağlayabilecek şekilde tanımla demek oluyor.

Sınıfları temel olarak, gerçek hayatta ki konseptler için kullanabileceğimizi söyledik, nesneler ise sınıflar üzerinde ki birer elemandır. Örneğin "insan" bir sınıf ise "ahmet" bir nesnedir gibi. Veya "araba" bir sınıftır, "yarış arabası" ve "jip" ise bir nesnedir, yarış arabasının mavi renk olmasını ise onun bir özelliği (attribute) olarak örnekleyebiliriz.

Insan.new = insan1

ile "Insan" sınıfına ait yeni bir nesne oluşturduk. insan1 nesnesi burada bir yer tutucudur, yani diğer bir tabirle değişkendir. Bellek içerisinde insan1'i tutar ve istediğimiz zaman kullanabilmemizi sağlar.

insan1.isim = "Serhat Dundar"
# insan1'in isim özelliğini "Serhat Dundar" olarak tanımladık.

insan1.yas = 21
# insan1'in yaş özelliğini 21 olarak tanımladık.

insan1.cinsiyet = "Erkek"
# insan1'in cinsiyet özelliğini "Erkek" olarak tanımladık.

Değişkenler mantığını en kolay anlayabildiğimiz terimlerden. Özellikle matematik ile biraz olsun uğraşmış biri iseniz değişkenleri çok kolay anlayabilirsiniz. Bir matematik sorusunda x = 5 ile aslında x değişkenini 5'e eşitlemiş oluyoruz. Yani yaptığımız şey x isminde bir değişken oluşturmak ve bunu 5 değerine eşitlemek.

Bu örneklemeyi matematik-programlama anlamında ayırmak için genellikle x = x + 1 örneği kullanılır.

Matematiksel olarak bu örneğe yaklaşıldığında 0 = 1 sonucu çıkmakta ve tanımsızlık durumu doğmaktadır. Ancak programlama için konuşursak bu ifade tanımsız bir durumu temsil etmez. Programın başında x değişkenini x = 5 ile 5'e eşitlemiş olalım, bir sonraki satırda ise x = x+1 ile x'e yeniden atama yaptığımızı ifade ediyoruz. Dolayısıyla x önceden 5'ti, artık 1 ekledik ve şuan 6 oldu. Adı üstünde "değişken" yani her an değişebilir oluşu buradan geliyor.

Değişkenler, sınıflar gibi kalıcı bilgi tutmaz, daha geçicidirler. Program içerisinde sürekli değiştireceğimiz ve oynayacağımız bir veriyi değişken olarak tanımlamak bize esneklik kazandırır.

Şimdi 3 ayrı sınıf tanımlayalım. Bunlar; "Kedi", "Köpek" ve "Yılan" olsun.

class Kedi
  attr_accessor :isim, :yas, :cinsiyet, :renk
end
class Kopek
  attr_accessor :isim, :yas, :cinsiyet, :renk
end
class Yılan
  attr_accessor :isim, :yas, :cinsiyet, :renk
end

Bu şekilde 3 ayrı hayvan için sınıf tanımlamak herhangi bir sorun doğurmaz, kodumuz hatasız işler, bu sınıflara ait hayvanlar oluşturabilir, bu hayvanlara tanımladığımız özellikleri atayabiliriz.

Ancak bu şekilde sınıf tanımlaması yaptığımız taktirde OOP nimetlerinden faydalanmıyoruz demektir. OOP'in temel yapı taşlarından biri olan "miras" sistemini bu sınıf tanımlamasında hiç kullanmadık. Oysa ki 3 ayrı hayvan var ve belirttiğimiz özellikler de tamamen aynı. Boşu boşuna tekrar tekrar isim, yaş, cinsiyet ve renk özelliklerini tanımlamış olduk.

3 sınıfımızda daha genel, ortak bir özelliğe sahip. Üç sınıfımızda birer hayvan. O halde "Hayvanlar" isminde daha genel bir sınıf tanımlayıp, buna ait özellikleri kedi, köpek ve yılan için kullanabiliriz.

class Hayvanlar
  attr_accessor :isim, :yas, :cinsiyet, :renk
end
class Kedi < Hayvanlar
end
class Kopek < Hayvanlar
end
class Yılan < Hayvanlar
end

Kedi, Köpek ve Yılan'ın "Hayvanlar" sınıfın özelliklerini miras alma durumunu < işareti ile sağladık. Bu sayede her seferinde isim, yaş, cinsiyet ve renk özelliklerini belirtme zorunluluğundan kurtulduk.

Peki her bir hayvanın, sadece kendine has özelliklerini, bu yapıyı bozmadan tanımlamak istesek? Örneğin tüm bunların dışında, kedi için birde kedinin cinsini tanımlayabilir miyiz? Van kedisi gibi?

Elbette, zaten bunu yapamasaydık OOP'nin ne anlamı kalırdı? Şimdi bu nasıl yapılıyor onu görelim örnek üzerinde:

class Hayvanlar
  attr_accessor :isim, :yas, :cinsiyet, :renk
end
class Kedi < Hayvanlar
  attr_accessor :kedicinsi
end
class Kopek < Hayvanlar
end
class Yılan < Hayvanlar
end

Gördüğünüz gibi kedi sınıfının altında birde kedicinsi özelliği tanımladık. Bu özellik sadece kediler için geçerli, yılan ve köpek hala diğer özellikleri "hayvanlar" sınıfından miras alıyor ve kedi de aynı şekilde ancak, kedinin artı olarak birde kedicinsi özelliği var.

Ruby ve Metodlar

Şimdi, oluşturduğumuz bu hayvanlara (siz birer tane oluşturdunuz ve denediniz sayıyorum) bazı işler yaptıralım. Bir nesneye yaptırdığımız işleri method veya fonksiyon olarak ifade edebiliriz. Örneğin köpeğimize bir havlama işi yaptırtabiliriz.

class Kopek < Hayvanlar
  def havlamasesi
    puts "Havv havv"
  end
end

def yani define bloğunu yine end ile kapattığımıza dikkat etmişsinizdir. Blokları daima end ile kapatmaktayız, yoksa programımız bizim bloğu sürdürmek mi yoksa bitirmek mi istediğimizi anlayamaz.

kopek1 = Kopek.new
# Kopek sınıfına ait bir köpek oluşturduk.

puts kopek1.havlamasesi
# Hazırladığımız metodu çağırdık ve havlama sesi ekrana yazıldı.

Bütün hayvanlar için ortak olan bir eylem yok mu peki? Eğer varsa bunu nasıl tanımlarız? Örneğin bütün hayvanlar için ortak bir eylem düşünelim. Aklıma daha iyi bir örnek gelmediği için bütün bu hayvanlara "saldırma" eylemi yaptırtmak istesek? Biz bunları programda çağırınca bize kızıp saldırsalar?

O halde bu kod parçamızı ana sınıf içerisine yazmalıyız, yani class Hayvanlar ile tanımladığımız blok içerisine.

Tüm örnekleri tek bir uygulama içerisinde görelim o halde :

class Evcilhayvanlar
  attr_accessor :isim, :yas, :cins, :renk
  def saldirma
    puts "Saldirdim!"
  end
end

class Kopek < Evcilhayvanlar
  def ses
    puts "Havvv havv"
  end
end

class Kedi < Evcilhayvanlar
  attr_accessor :kuyruk_uzunlugu
  def ses
    puts "miyavvvv"
  end
end

class Yilan < Evcilhayvanlar
  attr_accessor :uzunluk
end

Şimdi tanımladığımız tüm özellikleri ve saldırma işlevini test edebilirsiniz:

piton = Yilan.new

piton.isim = "Deneme"
piton.uzunluk = 100
piton.renk = "Sari"

puts piton.isim
puts piton.uzunluk
puts piton.renk

tekir = Kedi.new

tekir.isim = "Tekir"
tekir.kuyruk_uzunlugu = "100 cm"

puts tekir.isim
puts tekir.kuyruk_uzunlugu
puts tekir.ses
puts tekir.saldirma
puts kopek.saldirma

Bu günlük bu kadar. Başarılar.


Share this post!


Blog Comments powered by Disqus.