Ruby/Söz Dizimi/Sınıflar

←Metod Çağrıları | Öntanımlı Sınıflar→

Sınıflar, oluşum nesnelerinin üretildiği temel şablonlardır. Bir sınıf dahili durumu temsil eden değişkenler topluluğu ve bu durumlardaki davranışları belirleyen metodları barındırır.


Sınıf Tanımlamak

düzenle

Ruby'de sınıflar class ifadesini takip eden bir isimle tanımlanır. İsim büyük harfle başlamalıdır ve genel görüş olarak bir'den fazla kelimeden oluşan isimlerde her kelimenin ilk harfi büyük ve bitişik olarak yazılır, kelimeler arası ayırıcı karakter konmaz (CamelCase). Sınıf tanımlama bloğu içinde metodlar, sınıf değişkenleri, ve oluşum değişkenleri için tanımlamalar olabilir, bir de attr_accessor gibi sınıf içine dışarıdan erişimler için kısa yol tanımları olabilir. Sınıf tanımlamaları end ifadesi ile biter.

Örnek :

class MyClass
    def bir_metod
    end
end


Oluşum Değişkenleri

düzenle

Oluşum değişkenleri, sınıftan oluşturulan her nesne için üretilir ve o nesne içinde kullanılabilir. Kendilerine @ operatörü ile erişilir. Sınıf tanımlama bloğu dışında bu oluşum değişkenlerine, sadece oluşumun metodlarına kodlar ekleyerek erişilebilir.

Örnek :

class Sınıf
    @one = 1
    def birşey_yap
        @one = 2
    end
    def çıktı
        p @one
    end
end
oluşum = Sınıf.new
oluşum.çıktı
oluşum.birşey_yap
oluşum.çıktı

Bu kodun çıktısı, süpriz bir şekilde :

 nil
 2

olur. İlk çıktıda nil değeri var, sebebi ise @one değişkeni class Sınıf altında tanımlanmış bu tanım olarak bir oluşum değişkeni gibi görünmesine karşılık, kapsam olarak sınıf değişkeni gibi duruyor (ama @@one şeklinde sınıf değişkeni ismi yok). Buna karşılık birşey_yap metodu içinde tanımlanan @one ise bir oluşum değişkeni ve sınıftan üretilmiş olan nesneye ait. Bunlar aslında iki farklı değişken ve ilkine sadece bir sınıf metodu içinden erişilebilir.


Erişim Metodları

düzenle

Daha önce söylediğimiz gibi bir oluşum değişkenine sadece nesne metodlarının içinde erişebilirsiniz. Eğer dışarıdan oluşum değişkenine erişmek isterseniz , bu amaçla hazırlanmış erişim metodları (Accessor Methods) tanımlamalısınız. Örneğin :

class Sınıf1
    def initialize
        @foo = 28
    end

    def foo
        return @foo
    end

    def foo=(value)
        @foo = value
    end
end

oluşum = Sınıf1.new
p oluşum.foo
oluşum.foo = 496
p oluşum.foo

Dikkat ettiyseniz Ruby değişkene direk olarak erişip değiştirmek için enteresan kabiliyetlere sahip

a = oluşum.foo
oluşum.foo = b

satırları sırasıyla foo ve foo= metodlarını çağırır.

a = oluşum.foo()
oluşum.foo=(b)

olması doğru gösterim gibi , ancak Ruby yukarıdaki daha temiz görünümü mümkün kılmış.

Bu metodlara çok fazla gereksinim olacağı düşünülerek bu okuma ve yazma metodlarını üreten bir kısa yol eklenmiş:

class Sınıf1
    attr_accessor :foo

    def initialize
        @foo = 28
    end
end

oluşum = Sınıf1.new
p oluşum.foo
oluşum.foo = 496
p oluşum.foo

önceki programla aynı işi yapar. attr_accessor metodu Ruby sınıftan nesne üretirken çalışır ve foo ve foo= metodlarını üretir.

Bununla beraber erişim metodları sadece oluşum değişkenlerine erişebilmek için gerekmez. Örneğin foo değişkenine değeri yazmadan evvel bir yuvarlama yapmak isteyebiliriz:

class Sınıf1
    def initialize
        @foo = 28
    end

    def foo
        return @foo
    end

    def foo=(value)
        @foo = value.round
    end
end

oluşum = Sınıf1.new
p oluşum.foo
oluşum.foo = 496.2
p oluşum.foo

bu durumda metodlarımızı kendimiz yazmamız daha doğru olacaktır. Yine de okuyucu metodumuz hala standart, bunu kısaltmanın da bir yolu var.

class Sınıf1
    attr_reader :foo
    def initialize
        @foo = 28
    end

    def foo=(value)
        @foo = value.round
    end
end

Ruby attr_reader ve attr_writer kısa yolları okuma ya da yazma işlerinden sadece birini standart yapıda kullanmak durumunda işimizi görecektir.


Sınıf Değişkenleri

düzenle

Sınıf değişkenlerine @@ operatörü ile erişilir. Bu değişkenler, sınıftan üretilen bir nesne yerine sınıf hiyerarşisine iliştirilirler ve tüm oluşum nesneleri tarafından aynı değeri paylaşırlar (bu Java veya C++ dilleindeki "static" değişkenlere benzer).

Örnek :

class Sınıf2
    @@value = 1
    def bir_ekle
        @@value = @@value + 1
    end
    
    def value
        @@value
    end
end
oluşum1 = Sınıf2.new
oluşum2 = Sınıf2.new
p oluşum1.value
oluşum1.bir_ekle
p oluşum1.value
p oluşum2.value

çıktısı :

 1
 2
 2


Sınıf Oluşum Değişkenleri

düzenle

Yukarıda örneğini verdiğimiz sınıf değişkenleri, sınıftan kalıtım yoluyla üretilmiş, hiyerarşideki tüm sınıflarda paylaşılır. Ama sınıflar kalıtım zincirinde diğer sınıflarla paylaşılmayan değişkenlere de sahip olabilir.

class Çalışan
    class << self; attr_accessor :oluşumlar; end
    def kaydet
        self.class.oluşumlar ||= []
        self.class.oluşumlar << self
    end
    def initialize isim
        @isim = isim
    end
end
class Yönetici < Çalışan; end
class Programcı < Çalışan; end
Yönetici.new('Martin').kaydet
Yönetici.new('Roy').kaydet
Programcı.new('Erik').kaydet
p Yönetici.oluşumlar.size    # => 2
p Programcı.oluşumlar.size  # => 1

Daha fazla detay için, bakınız MF Bliki: ClassInstanceVariables


Sınıf Metodları

düzenle

Sınıf metodları, normal metodlar gibi tanımlanır, tek farkı isminin başına self. ibaresi eklenir veya sınıf adını takip eden nokta konur. These methods are executed at the Class level and may be called without an object instance. They cannot access instance variables but do have access to class variables.

Example:

class Sınıfım
    def self.bir_metod
        p 'birşey'
    end
end
Sınıfım.bir_metod

ya da

class Sınıfım
    def Sınıfım.bir_metod
        p 'birşey'
    end
end
Sınıfım.bir_metod


Çıktısı :

 "birşey"


Oluşumlar

düzenle

Bir sınıftan bir nesne üretme işlemine instantiation (örnekleme) denir. Ruby'de bu işlemi sınıfın new öntanımlı metodu ile yaparız.

Örnek :

birNesne = BirSınıf.new(parametreler)

Bu fonksiyon nesneyi hafızaya yerleştir ve kontrolü (eğer mevcutsa) sınıfın initialize metoduna aktarır. new fonksiyonuna girilen parametreler de initialize metoduna aktarılır.

class BirSınıf
    def initialize(parametreler)
    end
end


Görünürlüğü Belirleme

düzenle

Default olarak Ruby sınıflarındaki tüm metodlar public'tir — herkes tarafından erişilebilir. Yine de bu kuralın iki istisnası var : Object sınıfı altında tanımlanmış global metodlar ve herhangi bir sınıfın initialize metodu dolaylı olarak özeldir (private).

Eğer istenirse nesne metodları public, private, protected olarak kısıtlanabilir.

İlginçtir burada kullanacağımız ifadeler bir deyim değildir, aslında sınıfta çalışan metodlardır, metodların görünürlüğünü dinamik olarak değiştirirler, ve sonuç olarak bu ifadeleri kullandıktan sonra sınıf bloğu sonuna kadar ya da yeni bir kısıtlama bildirilene kadar tanımlanacak tüm metodlar bu kısıtlamaya tabi tutulur.


Private

düzenle

Basit bir örnek :

class Example
    def methodA
    end
    
    private # sınf içinde bu satırı takip eden tüm metodlar özeldir (private) nesne dışından erişilemez
    
    def methodP
    end
end

x = Example.new
x.methodA
x.methodP # error: private method `methodP' called

Eğer private ifadesi argüman olmadan verilirse ondan sonra gelen tüm metodları kasteder. Ayrıca isim verilerek de kullanılabilir.

İsim ile private metod örneği :

class Example
    def methodA
    end
    
    def methodP
    end
    
    private :methodP
end

Burada private bir argüman ile çağrılır, ve methodP görünürlüğünü özele çekiyor.

Not olarak — def SınıfAdı.metod_adı şeklinde tanımlanan sınıf metodları private_class_method ifadesi ile özel yapılır.

Bu private_class_method ifadesinin genelde kullanımı üretici metod olan new metodunu erişilemez yapmaktır, başka bir şekilde nesne üretmeye zorlamak için mesela. Tipik bir örnek :

class Örnek
    private_class_method :new
    
    def Örnek.üret(*args, &block)
        @@inst ||= new(*args, &block) 
    end
  end
x = Örnek.new     # hata verir
x = Örnek.üret    # çalışır


Her ne kadar C++'ta private "bu sınıfa özel" anlamına da gelse, Ruby'de bu oluşum nesnesine ait demektir.

Gerçekşu ki private metodlar aşağıdaki temsili şekilde dışarıdan diğer metodlar yardımıyla çağrılabilir.

class AccessPrivate
    def a
    end
    private :a          # a private bir metod
        
    def accessing_private
        a              # çalışır! 
        self.a         # çalışır!
        diğer_nesne.a  # çalışmaz! a private bir metod
    end
end

Burada, diğer_nesne deki a metodu private olduğu için erişilemez. Ama eğer "protected" olsa metoda yine dışarıdan direk erişilemezdi ancak accessing_private metodu içinden erişilebilirdi.


Bir metod için default görünürlük "public" dir. Bunu kendiniz kontrol etmek için de public ifadesini kullanabilirsiniz.

Neden bu var kesin emin değilim ama bir metodu private yaptıktan sonra dinamik olarak tekrar public yapmak için kullanılabilir.

Ruby'de görünürlük tamamen dinamiktir ve çalışma zamanında bile görünürlük değiştirilebilir!


Protected

düzenle

Şimdi, “protected” daha fazla açıklamayı hak ediyor. Java ya da C++'dan gelenler için, eğer bir metod "private" ise görünürlüğü tanımlandığı sınıf içinde kısıtlanır. Eğer bir metod “protected” ise bu sınıfın çocuklarından da (bu sınıftan kalıtımla üretilen sınıflar) veya aynı paketteki diğer sınıflardan da erişilebilir.

Ruby'de "private" görünürlük, Java'daki "protected" a benzer. Ruby'de "private" metodlara sınıfın çocuklarından da erişilebilir. Tam bir private metod Ruby'de yapılamaz, bir metodu komple gizleyemezsiniz.

Protected ve private arasındaki fark hemen göze çarpmaz. Eğer bir metod protected ise tanımlandığı sınıf ya da onun alt sınıflarında kullanılabilir. Eğer bir metod private ise sadece çağıran nesnenin kapsamında erişilebilir — başka bir oluşumun private metoduna direk olarak erişilemez, nesne aynı sınıftan üretilmiş olsa bile. Protected metodlara ise aynı sınıftan üretilmiş nesneler içinden ulaşılabilir(veya çocukları).

class Sınıf
    def initialize(yaş)
        @yaş = yaş
    end
    def yaş
        return @yaş
    end
    protected :yaş         
        
    def <=>(diğeri)
        self.yaş <=> diğeri.yaş
    end
end
kişi1 = Sınıf.new(12)
kişi2 = Sınıf.new(18)

p kişi1 <=> kişi2

Ne güzel , yaş adında bir parametre, @yaş adında bir oluşum değişkeni ve yine yaş adında bir metod var. Burada yaş metodu protected olduğu için aynı sınıftan üretilen diğer nesnenin yaş metodu çağrılabiliyor ve kişilerin yaşları karşılaştırılıyor. Eğer yaş metodu private olsaydı diğer nesne içinden çağrılamayacak ve hata verecekti. Ama protected de olsa kişi1.yaş çağrısı erişim hatası verecektir.


Oluşum Değişkenleri

düzenle

Dikkat ederseniz nesne oluşum değişkenleri private değildir, sadece onları göremezsiniz. Bir oluşum değişkenine erişmek için okuma ve yazma metodları oluşturursunuz.

Aşağıdaki örneğe bakın :

class GotAccessor
    def initialize(size)
        @size = size
    end
    
    def size
        @size
    end
    def size=(val)
        @size = val
    end
end
  
# @size değişkenine şöyle ulaşılır:
# a = GotAccessor.new(5)
# x = a.size 
# a.size = y

Ne iyi ki bunu kısa yoldan yapacak özel deyimlere sahibiz : attr_accessor, attr_reader, attr_writer. attr_accessor size hem okuma hem yazma işlevlerini kazandırır, attr_reader sadece okuma işlevi ve attr_writer sadece yazma işlevi kazandırır.

Şimdi kodumuzu kısaltalım:

class GotAccessor
    def initialize(size)
        @size = size
    end
    
    attr_accessor :size
end
  
# attr_accessor @size değişkenine erişim metodlarını otomatik 
# olarak üretir:
# a = GotAccessor.new(5)
# x = a.size 
# a.size = y


Kalıtım

düzenle
 
super deyimi sadece direk ebeveyn sınıfın metoduna ulaşmak için kullanılır. Yine de geçici bir çözüm var.

Bir sınıf işlevlerini ve değişkenlerini bir superclass'dan miras yoluyla alabilir, bu üst sınıfa bazen ebeveyn sınıf ya da temel sınıf dendiği de olur. Ruby çoklu mirasçılığı desteklemez, bu yüzden Ruby'de sadece birtek ebeveyn sınıf olabilir. Deyim yapısı şöyledir :

class EbeveynSınıf
    def bir_metod
        puts 'b'
    end
end
  
class BirSınıf < EbeveynSınıf  # < anlamı mirasçı (inherit veya "extends" eğer Java biliyorsanız)
    def diğer_metod
        puts 'a'
    end
end
  
oluşum = BirSınıf.new
oluşum.diğer_metod
oluşum.bir_metod

Çıktısı :

  a
  b


Ebeveyn sınıftaki tüm private olmayan değişken ve metodlar, kalıtım yoluyla çocuk sınıfta aynen geçerlidir.

Eğer mirasçı sınıfta ebeveyn sınıfın bir metodunun üzerine yazarsanız bile ebeveyn sınıfın metoduna "super" kelimesi kullanarak erişebilirsiniz.

class EbeveynSınıf
    def bir_metod
        puts 'b'
    end
end
  
class BirSınıf < EbeveynSınıf  
    def bir_metod
        super
        puts 'a'
    end
end
  
oluşum = BirSınıf.new
oluşum.bir_metod

Çıktısı :

  b
  a

(çünkü bir_metod ebeveyn sınıftaki orjinali de çağırıyot).

Eğer daha derin bir kalıtım hattınız varsa (mirasçının mirasçısı gibi) ve hala yukarılardaki üst sınıfların metodlarına ulaşmak isterseniz, bunu yapamazsınız. Ancak bunu çözmek için bir yöntem vardır, aşağıya geçirmek istediğiniz metoda alias ile değişik bir takma isim vererek. Sonra aşağıdaki sınıftan yukarıdaki sınıfa bu takma isimle erişebilirsiniz.

class X
    def foo
        "hello"
    end
end
  
class Y < X
    alias xFoo foo
    def foo
        xFoo + "y"
    end
end

class Z < Y
    def foo
        xFoo + "z"
    end
end
  
puts X.new.foo
puts Y.new.foo
puts Z.new.foo

Çıktısı :

 hello
 helloy
 helloz


Modüllerde Karıştırma

düzenle

Modülleri öncelikle Ruby modules'de okumanız iyi olur. Modüller bir kısım değişkenleri, fonksiyonları ve sınıfları bir grupta toplama yoludur, ama daha çok diğer dillerdeki namespace(ad alanmları)'lere benzerler. Bir modül, bir sınıf değildir, bir modülden kalıtımla mirasçı yapamazsınız ve bir modül self ile kendisini referans etmez. Modüllerin, modüle metodları olabilir (sınıfların sınıf metodları olması gibi) ve oluşum metodları da.

Bir modülü bir sınıfa dahil edebilirsiniz, yani içine karıştırabilirsiniz.

module A
    def a1
        puts 'a1 çağrıldı'
    end
end

module B
    def b1
        puts 'b1 çağrıldı'
    end
end

module C
    def c1
        puts 'c1 çağrıldı'
    end
end

class Test
    include A
    include B
    include C

    def göster
        puts 'Modüller dahil edildi'
    end
end

object=Test.new
object.göster
object.a1
object.b1
object.c1

Çıktısı :

Modüller dahil edildi
a1 çağrıldı
b1 çağrıldı
c1 çağrıldı

Modüller sınıflara birkaç değişik şekilde karıştırılabilir. Before diving into this section, it's important to understand the way that objects resolve messages into method names.


Include

düzenle
module A
    def a1
        puts "a1 modül"
    end
end

class Test
    include A

    def a1
        puts "a1 sınıf"
    end
end

test = Test.new
test.a1

Çıktısı :

a1 sınıf

include kullanınca modülden gelen metodlardan önce sınıftaki metodlar taranır. Yani önce sınıf içinde tanımlanan metod bulunur ve o çalıştırılır.


Prepend

düzenle
module A
    def a1
        puts "a1 modül"
    end
end

class Test
    prepend A

    def a1
        puts "a1 sınıf"
    end
end

test = Test.new
test.a1

Çıktısı :

a1 modül

prepend kullanılınca karıştırılan modüldeki metodlar daha önce taranır. Yani modüldeki metod daha önce bulunacak ve o çalıştırılacaktır.


Ruby Class Meta-Model

düzenle

Ruby'de her şeyin bir nesne olması prensibine bağlı kalmak amacıyla sınıfların kendileri de Class sınıfının oluşumlarıdır. Tanımlandıkları modül içinde sabitlerde saklanırlar. Bir nesne oluşumunda bir metod çağrısı nesnenin sınıfına bir referans içeren nesnenin içindeki bir değişkene devredilir. Metod uygulaması Class oluşum nesnesinin kendisinde mevcuttur. Sınıf metotları, mevcut sınıf oluşum nesnelerine bağlı olan meta sınıflarda, bu sınıf oluşumlarının onlara bağlı olduğu şekilde uygulanır. Bu meta-sınıflar birçok Ruby fonksiyonlarında gizlidir.



Önceki: Söz Dizimi/Metod Çağrıları Index Sonraki: Kaynak