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üzenleRuby'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üzenleOluş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üzenleDaha ö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üzenleSı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üzenleYukarı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üzenleSı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üzenleBir 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üzenleDefault 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üzenleBasit 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.
Public
düzenleBir 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üzenleDikkat 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üzenleBir 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üzenleModü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üzenlemodule 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üzenlemodule 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üzenleRuby'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.